SubwayTooter-Android-App/app/src/main/java/jp/juggler/subwaytooter/view/GravitySnapHelper.kt

206 lines
6.3 KiB
Kotlin

package jp.juggler.subwaytooter.view
import android.annotation.SuppressLint
import android.support.v4.view.ViewCompat
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.LinearSnapHelper
import android.support.v7.widget.OrientationHelper
import android.support.v7.widget.RecyclerView
import android.view.Gravity
import android.view.View
import android.view.animation.DecelerateInterpolator
import android.widget.Scroller
import jp.juggler.util.LogCategory
class GravitySnapHelper @SuppressLint("RtlHardcoded")
constructor(gravity : Int) : LinearSnapHelper() {
companion object {
internal val log = LogCategory("GravitySnapHelper")
}
private var verticalHelper : OrientationHelper? = null
private var horizontalHelper : OrientationHelper? = null
private var gravity : Int = 0
private var isRTL : Boolean = false
private var mRecyclerView : RecyclerView? = null
private var mGravityScroller : Scroller? = null
init {
this.gravity = gravity
if(this.gravity == Gravity.LEFT) {
this.gravity = Gravity.START
} else if(this.gravity == Gravity.RIGHT) {
this.gravity = Gravity.END
}
}
@Throws(IllegalStateException::class)
override fun attachToRecyclerView(recyclerView : RecyclerView?) {
mRecyclerView = recyclerView
if(recyclerView != null) {
isRTL = ViewCompat.getLayoutDirection(recyclerView) == ViewCompat.LAYOUT_DIRECTION_RTL
mGravityScroller = Scroller(recyclerView.context, DecelerateInterpolator())
}
super.attachToRecyclerView(recyclerView)
}
override fun calculateDistanceToFinalSnap(
layoutManager : RecyclerView.LayoutManager, targetView : View
) : IntArray? {
val out = IntArray(2)
if(! layoutManager.canScrollHorizontally()) {
out[0] = 0
} else if(gravity == Gravity.START) {
out[0] = distanceToStart(targetView, getHorizontalHelper(layoutManager))
} else {
out[0] = distanceToEnd(targetView, getHorizontalHelper(layoutManager))
}
if(! layoutManager.canScrollVertically()) {
out[1] = 0
} else if(gravity == Gravity.TOP) {
out[1] = distanceToStart(targetView, getVerticalHelper(layoutManager))
} else {
out[1] = distanceToEnd(targetView, getVerticalHelper(layoutManager))
}
return out
}
override fun findSnapView(layoutManager : RecyclerView.LayoutManager) : View? {
if(layoutManager is LinearLayoutManager) {
when(gravity) {
Gravity.START -> return findStartView(layoutManager, getHorizontalHelper(layoutManager))
Gravity.TOP -> return findStartView(layoutManager, getVerticalHelper(layoutManager))
Gravity.END -> return findEndView(layoutManager, getHorizontalHelper(layoutManager))
Gravity.BOTTOM -> return findEndView(layoutManager, getVerticalHelper(layoutManager))
}
}
return super.findSnapView(layoutManager)
}
private fun distanceToStart(targetView : View, helper : OrientationHelper) : Int {
return if(isRTL) {
helper.getDecoratedEnd(targetView) - helper.endAfterPadding
} else {
helper.getDecoratedStart(targetView) - helper.startAfterPadding
}
}
private fun distanceToEnd(targetView : View, helper : OrientationHelper) : Int {
return if(isRTL) {
helper.getDecoratedStart(targetView) - helper.startAfterPadding
} else {
helper.getDecoratedEnd(targetView) - helper.endAfterPadding
}
}
private fun findStartView(
layoutManager : RecyclerView.LayoutManager, helper : OrientationHelper
) : View? {
if(layoutManager is LinearLayoutManager) {
val firstChild = layoutManager.findFirstVisibleItemPosition()
if(firstChild == RecyclerView.NO_POSITION) {
return null
}
val child = layoutManager.findViewByPosition(firstChild)
@Suppress("CascadeIf")
return if(helper.getDecoratedEnd(child) >= helper.getDecoratedMeasurement(child) / 2 && helper.getDecoratedEnd(child) > 0) {
child
} else if(layoutManager.findLastCompletelyVisibleItemPosition() == layoutManager.getItemCount() - 1) {
null
} else {
layoutManager.findViewByPosition(firstChild + 1)
}
}
return super.findSnapView(layoutManager)
}
private fun findEndView(
layoutManager : RecyclerView.LayoutManager, helper : OrientationHelper
) : View? {
if(layoutManager is LinearLayoutManager) {
val lastChild = layoutManager.findLastVisibleItemPosition()
if(lastChild == RecyclerView.NO_POSITION) {
return null
}
val child = layoutManager.findViewByPosition(lastChild)
@Suppress("CascadeIf")
return if(helper.getDecoratedStart(child) + helper.getDecoratedMeasurement(child) / 2 <= helper.totalSpace) {
child
} else if(layoutManager.findFirstCompletelyVisibleItemPosition() == 0) {
null
} else {
layoutManager.findViewByPosition(lastChild - 1)
}
}
return super.findSnapView(layoutManager)
}
private fun getVerticalHelper(layoutManager : RecyclerView.LayoutManager) : OrientationHelper {
var verticalHelper = this.verticalHelper
if(verticalHelper == null) {
verticalHelper = OrientationHelper.createVerticalHelper(layoutManager) as OrientationHelper
this.verticalHelper = verticalHelper
}
return verticalHelper
}
private fun getHorizontalHelper(layoutManager : RecyclerView.LayoutManager) : OrientationHelper {
var horizontalHelper = this.horizontalHelper
if(horizontalHelper == null) {
horizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager) as OrientationHelper
this.horizontalHelper = horizontalHelper
}
return horizontalHelper
}
// var columnWidth : Int = 0
override fun findTargetSnapPosition(
layoutManager : RecyclerView.LayoutManager, velocityX : Int, velocityY : Int
) : Int {
var targetPos = super.findTargetSnapPosition(layoutManager, velocityX, velocityY)
if(targetPos != RecyclerView.NO_POSITION) {
val currentView = findSnapView(layoutManager )
if(currentView != null) {
val currentPosition = layoutManager.getPosition(currentView)
val clip = 1
if(targetPos - currentPosition > clip) {
targetPos = currentPosition + clip
} else if(targetPos - currentPosition < - clip) {
targetPos = currentPosition - clip
}
}
}
return targetPos
}
}