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 } }