XML
文件
ItemView
的XML
文件R.layout.shape_item_view
<?xml version="1.0" encoding="utf-8"?>
< FrameLayout xmlns: android= " http://schemas.android.com/apk/res/android"
android: layout_width= " 100dp"
android: layout_height= " 100dp"
android: background= " @drawable/shape_item_view" >
< TextView
android: id= " @+id/textView"
android: layout_width= " wrap_content"
android: layout_height= " wrap_content"
android: layout_gravity= " center"
android: textSize= " 16sp" />
</ FrameLayout>
滑动到对齐ItemView
的XML
文件 R.drawable.shape_item_view_selected
<?xml version="1.0" encoding="utf-8"?>
< shape xmlns: android= " http://schemas.android.com/apk/res/android" >
< solid android: color= " #FFFFFF" />
< corners android: radius= " 8dp" />
< stroke android: color= " #FFFF00" android: width= " 5dp" />
</ shape>
未滑动到对齐ItemView
的XML
文件 R.drawable.shape_item_view
<?xml version="1.0" encoding="utf-8"?>
< shape xmlns: android= " http://schemas.android.com/apk/res/android" >
< solid android: color= " #FFFFFF" />
< corners android: radius= " 8dp" />
< stroke android: color= " #000000" android: width= " 5dp" />
</ shape>
Activity
的XML文件R.layout.activity_main
<?xml version="1.0" encoding="utf-8"?>
< LinearLayout xmlns: android= " http://schemas.android.com/apk/res/android"
android: layout_width= " match_parent"
android: layout_height= " match_parent"
android: gravity= " center" >
< androidx.recyclerview.widget.RecyclerView
android: id= " @+id/recyclerView"
android: layout_width= " match_parent"
android: layout_height= " 100dp"
android: layout_marginLeft= " @dimen/edit_crop_frame_padding"
android: layout_marginRight= " @dimen/edit_crop_frame_padding"
android: orientation= " horizontal" />
</ LinearLayout>
RecyclerView
代码
class MyAdapter ( private val numbers: List< Int> ) : RecyclerView. Adapter< MyAdapter. MyViewHolder> ( ) {
private var selectedPosition = RecyclerView. NO_POSITION
override fun onCreateViewHolder ( parent: ViewGroup, viewType: Int) : MyViewHolder {
val view = LayoutInflater. from ( parent. context) . inflate ( R. layout. item_view, parent, false )
return MyViewHolder ( view)
}
override fun onBindViewHolder ( holder: MyViewHolder, position: Int) {
holder. textView. text = numbers[ position] . toString ( )
if ( selectedPosition == position) {
holder. itemView. setBackgroundResource ( R. drawable. shape_item_view_selected)
} else {
holder. itemView. setBackgroundResource ( R. drawable. shape_item_view)
}
}
override fun getItemCount ( ) = numbers. size
fun setSelectedPosition ( position: Int) {
val oldPosition = selectedPosition
selectedPosition = position
notifyItemChanged ( oldPosition)
notifyItemChanged ( selectedPosition)
}
class MyViewHolder ( itemView: View) : RecyclerView. ViewHolder ( itemView) {
val textView: TextView = itemView. findViewById ( R. id. textView)
}
}
class SpaceItemDecoration ( private val spaceSize: Int, private val itemSize : Int) : RecyclerView. ItemDecoration ( ) {
private val paint = Paint ( ) . apply {
color = Color. RED
style = Paint. Style. FILL
}
var spacePadding = 0
override fun getItemOffsets ( outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView. State) {
super . getItemOffsets ( outRect, view, parent, state)
outRect. left = spaceSize
outRect. right = spaceSize
spacePadding = ( parent. measuredWidth / 2 - itemSize / 2 )
val size = parent. adapter? . itemCount ?: 0
val position = parent. getChildAdapterPosition ( view)
if ( position == 0 ) {
outRect. left = spacePadding
} else if ( position == size - 1 ) {
outRect. right = spacePadding
}
}
override fun onDraw ( c: Canvas, parent: RecyclerView, state: RecyclerView. State) {
super . onDraw ( c, parent, state)
val childCount = parent. childCount
for ( i in 0 until childCount) {
val child = parent. getChildAt ( i)
val position = parent. getChildAdapterPosition ( child)
val params = child. layoutParams as RecyclerView. LayoutParams
var left : Int
var right : Int
var top : Int
var bottom : Int
if ( position == 0 ) {
left = child. left - params. leftMargin - spacePadding
right = child. right + params. rightMargin + spaceSize
top = child. top - params. topMargin
bottom = child. bottom + params. bottomMargin
} else if ( position == parent. adapter? . itemCount!! - 1 ) {
left = child. left - params. leftMargin - spaceSize
right = child. right + params. rightMargin + spacePadding
top = child. top - params. topMargin
bottom = child. bottom + params. bottomMargin
} else {
left = child. left - params. leftMargin - spaceSize
right = child. right + params. rightMargin + spaceSize
top = child. top - params. topMargin
bottom = child. bottom + params. bottomMargin
}
c. drawRect ( left. toFloat ( ) , top. toFloat ( ) , right. toFloat ( ) , bottom. toFloat ( ) , paint)
}
}
}
RecyclerView
对齐工具类SnapHelper
实现类代码
① attachToRecyclerView()
方法:将RecyclerView
对齐操作交给SnapHelper
实现类
override fun attachToRecyclerView ( recyclerView: RecyclerView? ) {
Log. i ( TAG, "attachToRecyclerView" )
mRecyclerView = recyclerView
super . attachToRecyclerView ( recyclerView)
}
② createScroller()
方法:创建惯性滑动的Scroller
onTargetFound()
回调方法:找到对齐位置之后回调,计算目标位置到对齐位置需要滚动的距离和时间
calculateSpeedPerPixel()
回调方法:滚动一英寸所需时间除以屏幕密度,得到滚动一像素所需的时间
override fun createScroller ( layoutManager: RecyclerView. LayoutManager? ) : RecyclerView. SmoothScroller? {
if ( layoutManager ! is RecyclerView. SmoothScroller. ScrollVectorProvider) {
return null
}
Log. i ( TAG, "createScroller" )
return object : LinearSmoothScroller ( mRecyclerView? . context) {
override fun onTargetFound ( targetView: View, state: RecyclerView. State, action: Action) {
if ( mRecyclerView == null ) return
Log. i ( TAG, "onTargetFound" )
val snapDistances : IntArray? = calculateDistanceToFinalSnap ( mRecyclerView? . layoutManager!! , targetView)
val dx = snapDistances? . get ( 0 ) ?: 0
val dy = snapDistances? . get ( 1 ) ?: 0
val time = calculateTimeForDeceleration ( Math. max ( Math. abs ( dx) , Math. abs ( dy) ) ) * 10
if ( time > 0 ) {
action. update ( dx, dy, time, mInterpolator)
}
}
override fun calculateSpeedPerPixel ( displayMetrics: DisplayMetrics? ) : Float {
Log. i ( TAG, "calculateSpeedPerPixel" )
return MILLISECONDS_PER_INCH / displayMetrics? . densityDpi!!
}
}
}
③ findTargetSnapPosition()
方法:找到需要对齐的ItemView
的位置
RecyclerView.SmoothScroller.ScrollVectorProvider.computeScrollVectorForPosition()
:计算从0
的位置滚动到ItemCount-1
的位置需要滚动的方向,vectorForEnd.x
表示水平方向(>0
向右,<0
向左),vectorForEnd.y
表示竖直方向(>0
向下,<0
向上),正负值由结束位置和开始位置的差值得出
override fun findTargetSnapPosition ( layoutManager: RecyclerView. LayoutManager? , velocityX: Int, velocityY: Int) : Int {
if ( layoutManager ! is RecyclerView. SmoothScroller. ScrollVectorProvider) return RecyclerView. NO_POSITION
val itemCount = layoutManager. itemCount
if ( itemCount == 0 ) return RecyclerView. NO_POSITION
val currentView = findSnapView ( layoutManager) ?: return RecyclerView. NO_POSITION
val currentPosition = layoutManager. getPosition ( currentView)
if ( currentPosition == RecyclerView. NO_POSITION) return RecyclerView. NO_POSITION
val vectorProvider = layoutManager as RecyclerView. SmoothScroller. ScrollVectorProvider
val vectorForEnd = vectorProvider. computeScrollVectorForPosition ( itemCount - 1 ) ?: return RecyclerView. NO_POSITION
Log. i ( TAG, "findTargetSnapPosition" )
var maxHorizontalItemViewCount: Int
var maxVerticalItemViewCount: Int
if ( layoutManager. canScrollHorizontally ( ) ) {
maxHorizontalItemViewCount = estimateNextPositionDiffForFling ( layoutManager, getHorizontalHelper ( layoutManager) , velocityX, 0 )
var sign = Math. signum ( velocityX. toFloat ( ) )
if ( sign == 0f ) sign = 1f
maxHorizontalItemViewCount = ( sign * Math. min ( Math. max ( Math. abs ( maxHorizontalItemViewCount) , 0 ) , 2 ) ) . toInt ( )
if ( vectorForEnd. x < 0 ) {
maxHorizontalItemViewCount = - maxHorizontalItemViewCount
}
} else {
maxHorizontalItemViewCount = 0
}
if ( layoutManager. canScrollVertically ( ) ) {
maxVerticalItemViewCount = estimateNextPositionDiffForFling ( layoutManager, getVerticalHelper ( layoutManager) , 0 , velocityY)
var sign = Math. signum ( velocityY. toFloat ( ) )
if ( sign == 0f ) sign = 1f
maxVerticalItemViewCount = ( sign * Math. min ( Math. max ( Math. abs ( maxVerticalItemViewCount) , 0 ) , 2 ) ) . toInt ( )
if ( vectorForEnd. y < 0 ) {
maxVerticalItemViewCount = - maxVerticalItemViewCount
}
} else {
maxVerticalItemViewCount = 0
}
val finalItemCount = if ( layoutManager. canScrollHorizontally ( ) ) {
maxHorizontalItemViewCount
} else {
maxVerticalItemViewCount
}
if ( finalItemCount == 0 ) return RecyclerView. NO_POSITION
var targetPosition = currentPosition + finalItemCount
if ( targetPosition < 0 ) targetPosition = 0
if ( targetPosition >= layoutManager. itemCount) targetPosition = layoutManager. itemCount - 1
return targetPosition
}
④ findSnapView()
方法:调用findCenterView()
找到最接近中心点的ItemView
findCenterView()
方法:拿到每个ItemView
的left
加上自身宽度的一半和RecyclerView
的中心点进行比较,找到最接近中心点的ItemView
override fun findSnapView ( layoutManager: RecyclerView. LayoutManager? ) : View? {
Log. i ( TAG, "findSnapView" )
if ( layoutManager!! . canScrollVertically ( ) ) {
return findCenterView ( layoutManager, getVerticalHelper ( layoutManager) )
} else if ( layoutManager. canScrollHorizontally ( ) ) {
return findCenterView ( layoutManager, getHorizontalHelper ( layoutManager) )
}
return null
}
private fun findCenterView ( layoutManager: RecyclerView. LayoutManager, helper: OrientationHelper) : View? {
Log. i ( TAG, "findCenterView" )
val childCount = layoutManager. childCount
if ( childCount == 0 ) return null
var closestItemView: View? = null
val center = helper. startAfterPadding + helper. totalSpace / 2
var absClosest = Int. MAX_VALUE
for ( i in 0 until childCount) {
val child = layoutManager. getChildAt ( i)
val childCenter = child? . left!! + helper. getDecoratedMeasurement ( child) / 2
val childDistance = Math. abs ( childCenter - center)
if ( childDistance < absClosest) {
absClosest = childDistance
closestItemView = child
}
}
return closestItemView
}
⑤ estimateNextPositionDiffForFling()
方法:计算当前位置到目标对齐位置还差了几个ItemView
的个数
calculateScrollDistance()
:计算RecyclerView
的滚动距离
computeDistancePerChild()
:计算每个ItemView
的滚动距离
private fun estimateNextPositionDiffForFling ( layoutManager: RecyclerView. LayoutManager, helper: OrientationHelper, velocityX: Int, velocityY: Int) : Int {
Log. i ( TAG, "estimateNextPositionDiffForFling" )
val distances = calculateScrollDistance ( velocityX, velocityY)
val distancePerChild = computeDistancePerChild ( layoutManager, helper)
if ( distancePerChild <= 0 ) return 0
val distance = if ( Math. abs ( distances[ 0 ] ) > Math. abs ( distances[ 1 ] ) ) distances[ 0 ] else distances[ 1 ]
return Math. round ( distance / distancePerChild)
}
override fun calculateScrollDistance ( velocityX: Int, velocityY: Int) : IntArray {
Log. i ( TAG, "calculateScrollDistance" )
return super . calculateScrollDistance ( velocityX, velocityY)
}
private fun computeDistancePerChild ( layoutManager: RecyclerView. LayoutManager, helper: OrientationHelper) : Float {
Log. i ( TAG, "computeDistancePerChild" )
var minPositionView : View ? = null
var maxPositionView : View ? = null
var minPosition = Integer. MAX_VALUE
var maxPosition = Integer. MIN_VALUE
val itemViewCount = layoutManager. childCount
if ( itemViewCount == 0 ) return INVALID_DISTANCE
for ( i in 0 until itemViewCount) {
val child = layoutManager. getChildAt ( i) ?: continue
val position = layoutManager. getPosition ( child)
if ( position == RecyclerView. NO_POSITION) continue
if ( position < minPosition) {
minPosition = position
minPositionView = child
}
if ( position > maxPosition) {
maxPosition = position
maxPositionView = child
}
}
if ( minPositionView == null || maxPositionView == null ) return INVALID_DISTANCE
val start = Math. min ( helper. getDecoratedStart ( minPositionView) , helper. getDecoratedStart ( maxPositionView) )
val end = Math. max ( helper. getDecoratedEnd ( minPositionView) , helper. getDecoratedEnd ( maxPositionView) )
val distance = end - start
if ( distance <= 0 ) return INVALID_DISTANCE
return 1f * distance / ( maxPosition - minPosition + 1 )
}
⑥ onTargetFound()
方法:找对对齐ItemView
位置后回调
calculateDistanceToFinalSnap()
:计算最终需要滚动到对齐ItemView
位置的距离
calculateTimeForDeceleration()
:计算最终需要滚动到对齐ItemView
位置所花时间
override fun onTargetFound ( targetView: View, state: RecyclerView. State, action: Action) {
if ( mRecyclerView == null ) return
Log. i ( TAG, "onTargetFound" )
val snapDistances : IntArray? = calculateDistanceToFinalSnap ( mRecyclerView? . layoutManager!! , targetView)
val dx = snapDistances? . get ( 0 ) ?: 0
val dy = snapDistances? . get ( 1 ) ?: 0
val time = calculateTimeForDeceleration ( Math. max ( Math. abs ( dx) , Math. abs ( dy) ) ) * 10
if ( time > 0 ) {
action. update ( dx, dy, time, mInterpolator)
}
}
override fun calculateDistanceToFinalSnap ( layoutManager: RecyclerView. LayoutManager, targetView: View) : IntArray? {
Log. i ( TAG, "calculateDistanceToFinalSnap" )
val out = IntArray ( 2 )
if ( layoutManager. canScrollHorizontally ( ) ) {
out [ 0 ] = distanceToCenter ( targetView, getHorizontalHelper ( layoutManager) !! )
} else {
out [ 0 ] = 0
}
if ( layoutManager. canScrollVertically ( ) ) {
out [ 1 ] = distanceToCenter ( targetView, getVerticalHelper ( layoutManager) !! )
} else {
out [ 1 ] = 0
}
return out
}
⑦ distanceToCenter()
方法:计算目标对齐ItemView
距离RecyclerView
中心点的距离
private fun distanceToCenter ( targetView: View, helper: OrientationHelper) : Int {
Log. i ( TAG, "distanceToCenter" )
val childCenter = helper. getDecoratedStart ( targetView) + helper. getDecoratedMeasurement ( targetView) / 2
val containerCenter = helper. startAfterPadding + helper. totalSpace / 2
return childCenter - containerCenter
}
open class MySmoothSnapHelper : SnapHelper ( ) {
private val INVALID_DISTANCE = 1f
private val MILLISECONDS_PER_INCH = 25f
private var mVerticalHelper : OrientationHelper ? = null
private var mHorizontalHelper : OrientationHelper ? = null
private var mRecyclerView : RecyclerView ? = null
private val mInterpolator = LinearOutSlowInInterpolator ( )
override fun attachToRecyclerView ( recyclerView: RecyclerView? ) {
Log. i ( TAG, "attachToRecyclerView" )
mRecyclerView = recyclerView
super . attachToRecyclerView ( recyclerView)
}
override fun createScroller ( layoutManager: RecyclerView. LayoutManager? ) : RecyclerView. SmoothScroller? {
Log. i ( TAG, "createScroller" )
if ( layoutManager ! is RecyclerView. SmoothScroller. ScrollVectorProvider) {
return null
}
return object : LinearSmoothScroller ( mRecyclerView? . context) {
override fun onTargetFound ( targetView: View, state: RecyclerView. State, action: Action) {
if ( mRecyclerView == null ) return
Log. i ( TAG, "onTargetFound" )
val snapDistances : IntArray? = calculateDistanceToFinalSnap ( mRecyclerView? . layoutManager!! , targetView)
val dx = snapDistances? . get ( 0 ) ?: 0
val dy = snapDistances? . get ( 1 ) ?: 0
val time = calculateTimeForDeceleration ( Math. max ( Math. abs ( dx) , Math. abs ( dy) ) ) * 10
if ( time > 0 ) {
action. update ( dx, dy, time, mInterpolator)
}
}
override fun calculateSpeedPerPixel ( displayMetrics: DisplayMetrics? ) : Float {
Log. i ( TAG, "calculateSpeedPerPixel" )
return MILLISECONDS_PER_INCH / displayMetrics? . densityDpi!!
}
}
}
override fun calculateDistanceToFinalSnap ( layoutManager: RecyclerView. LayoutManager, targetView: View) : IntArray? {
Log. i ( TAG, "calculateDistanceToFinalSnap" )
val out = IntArray ( 2 )
if ( layoutManager. canScrollHorizontally ( ) ) {
out [ 0 ] = distanceToCenter ( targetView, getHorizontalHelper ( layoutManager) !! )
} else {
out [ 0 ] = 0
}
if ( layoutManager. canScrollVertically ( ) ) {
out [ 1 ] = distanceToCenter ( targetView, getVerticalHelper ( layoutManager) !! )
} else {
out [ 1 ] = 0
}
return out
}
private fun distanceToCenter ( targetView: View, helper: OrientationHelper) : Int {
Log. i ( TAG, "distanceToCenter" )
val childCenter = helper. getDecoratedStart ( targetView) + helper. getDecoratedMeasurement ( targetView) / 2
val containerCenter = helper. startAfterPadding + helper. totalSpace / 2
return childCenter - containerCenter
}
override fun findSnapView ( layoutManager: RecyclerView. LayoutManager? ) : View? {
Log. i ( TAG, "findSnapView" )
if ( layoutManager!! . canScrollVertically ( ) ) {
return findCenterView ( layoutManager, getVerticalHelper ( layoutManager) )
} else if ( layoutManager. canScrollHorizontally ( ) ) {
return findCenterView ( layoutManager, getHorizontalHelper ( layoutManager) )
}
return null
}
private fun findCenterView ( layoutManager: RecyclerView. LayoutManager, helper: OrientationHelper) : View? {
Log. i ( TAG, "findCenterView" )
val childCount = layoutManager. childCount
if ( childCount == 0 ) return null
var closestItemView: View? = null
val center = helper. startAfterPadding + helper. totalSpace / 2
var absClosest = Int. MAX_VALUE
for ( i in 0 until childCount) {
val child = layoutManager. getChildAt ( i)
val childCenter = child? . left!! + helper. getDecoratedMeasurement ( child) / 2
val childDistance = Math. abs ( childCenter - center)
if ( childDistance < absClosest) {
absClosest = childDistance
closestItemView = child
}
}
return closestItemView
}
override fun findTargetSnapPosition ( layoutManager: RecyclerView. LayoutManager? , velocityX: Int, velocityY: Int) : Int {
Log. i ( TAG, "findTargetSnapPosition" )
if ( layoutManager ! is RecyclerView. SmoothScroller. ScrollVectorProvider) return RecyclerView. NO_POSITION
val itemCount = layoutManager. itemCount
if ( itemCount == 0 ) return RecyclerView. NO_POSITION
val currentView = findSnapView ( layoutManager) ?: return RecyclerView. NO_POSITION
val currentPosition = layoutManager. getPosition ( currentView)
if ( currentPosition == RecyclerView. NO_POSITION) return RecyclerView. NO_POSITION
val vectorProvider = layoutManager as RecyclerView. SmoothScroller. ScrollVectorProvider
val vectorForEnd = vectorProvider. computeScrollVectorForPosition ( itemCount - 1 ) ?: return RecyclerView. NO_POSITION
var maxHorizontalItemViewCount: Int
var maxVerticalItemViewCount: Int
if ( layoutManager. canScrollHorizontally ( ) ) {
maxHorizontalItemViewCount = estimateNextPositionDiffForFling ( layoutManager, getHorizontalHelper ( layoutManager) , velocityX, 0 )
var sign = Math. signum ( velocityX. toFloat ( ) )
if ( sign == 0f ) sign = 1f
maxHorizontalItemViewCount = ( sign * Math. min ( Math. max ( Math. abs ( maxHorizontalItemViewCount) , 0 ) , 2 ) ) . toInt ( )
if ( vectorForEnd. x < 0 ) {
maxHorizontalItemViewCount = - maxHorizontalItemViewCount
}
} else {
maxHorizontalItemViewCount = 0
}
if ( layoutManager. canScrollVertically ( ) ) {
maxVerticalItemViewCount = estimateNextPositionDiffForFling ( layoutManager, getVerticalHelper ( layoutManager) , 0 , velocityY)
var sign = Math. signum ( velocityY. toFloat ( ) )
if ( sign == 0f ) sign = 1f
maxVerticalItemViewCount = ( sign * Math. min ( Math. max ( Math. abs ( maxVerticalItemViewCount) , 0 ) , 2 ) ) . toInt ( )
if ( vectorForEnd. y < 0 ) {
maxVerticalItemViewCount = - maxVerticalItemViewCount
}
} else {
maxVerticalItemViewCount = 0
}
val finalItemCount = if ( layoutManager. canScrollHorizontally ( ) ) {
maxHorizontalItemViewCount
} else {
maxVerticalItemViewCount
}
if ( finalItemCount == 0 ) return RecyclerView. NO_POSITION
var targetPosition = currentPosition + finalItemCount
if ( targetPosition < 0 ) targetPosition = 0
if ( targetPosition >= layoutManager. itemCount) targetPosition = layoutManager. itemCount - 1
return targetPosition
}
override fun calculateScrollDistance ( velocityX: Int, velocityY: Int) : IntArray {
Log. i ( TAG, "calculateScrollDistance" )
return super . calculateScrollDistance ( velocityX, velocityY)
}
private fun estimateNextPositionDiffForFling ( layoutManager: RecyclerView. LayoutManager, helper: OrientationHelper, velocityX: Int, velocityY: Int) : Int {
Log. i ( TAG, "estimateNextPositionDiffForFling" )
val distances = calculateScrollDistance ( velocityX, velocityY)
val distancePerChild = computeDistancePerChild ( layoutManager, helper)
if ( distancePerChild <= 0 ) return 0
val distance = if ( Math. abs ( distances[ 0 ] ) > Math. abs ( distances[ 1 ] ) ) distances[ 0 ] else distances[ 1 ]
return Math. round ( distance / distancePerChild)
}
private fun computeDistancePerChild ( layoutManager: RecyclerView. LayoutManager, helper: OrientationHelper) : Float {
Log. i ( TAG, "computeDistancePerChild" )
var minPositionView : View ? = null
var maxPositionView : View ? = null
var minPosition = Integer. MAX_VALUE
var maxPosition = Integer. MIN_VALUE
val itemViewCount = layoutManager. childCount
if ( itemViewCount == 0 ) return INVALID_DISTANCE
for ( i in 0 until itemViewCount) {
val child = layoutManager. getChildAt ( i) ?: continue
val position = layoutManager. getPosition ( child)
if ( position == RecyclerView. NO_POSITION) continue
if ( position < minPosition) {
minPosition = position
minPositionView = child
}
if ( position > maxPosition) {
maxPosition = position
maxPositionView = child
}
}
if ( minPositionView == null || maxPositionView == null ) return INVALID_DISTANCE
val start = Math. min ( helper. getDecoratedStart ( minPositionView) , helper. getDecoratedStart ( maxPositionView) )
val end = Math. max ( helper. getDecoratedEnd ( minPositionView) , helper. getDecoratedEnd ( maxPositionView) )
val distance = end - start
if ( distance <= 0 ) return INVALID_DISTANCE
return 1f * distance / ( maxPosition - minPosition + 1 )
}
private fun getVerticalHelper ( layoutManager: RecyclerView. LayoutManager) : OrientationHelper {
Log. i ( TAG, "getVerticalHelper" )
if ( mVerticalHelper == null || mVerticalHelper? . layoutManager != layoutManager) {
mVerticalHelper = OrientationHelper. createVerticalHelper ( layoutManager)
}
return mVerticalHelper!!
}
private fun getHorizontalHelper ( layoutManager: RecyclerView. LayoutManager) : OrientationHelper {
Log. i ( TAG, "getHorizontalHelper" )
if ( mHorizontalHelper == null || mHorizontalHelper!! . layoutManager != layoutManager) {
mHorizontalHelper = OrientationHelper. createHorizontalHelper ( layoutManager)
}
return mHorizontalHelper!!
}
}
Activity
代码
第一次findSnapView
:正常滑动停止后触发,需要找到对齐的View
第二次findSnapView
:惯性滑动停止后触发,需要找到对齐的View
const val TAG = "Yang"
class MainActivity : AppCompatActivity ( ) {
override fun onCreate ( savedInstanceState: Bundle? ) {
super . onCreate ( savedInstanceState)
setContentView ( R. layout. activity_main)
val numberList = List ( 10 ) { it}
val layoutManager = LinearLayoutManager ( this , LinearLayoutManager. HORIZONTAL, false )
val mRv = findViewById< RecyclerView> ( R. id. recyclerView)
val mAdapter = MyAdapter ( numberList)
mRv. addItemDecoration ( SpaceItemDecoration ( dpToPx ( this , 25f ) , dpToPx ( this , 100f ) ) )
val linearSnapHelper = object : MySmoothSnapHelper ( ) {
override fun findSnapView ( layoutManager: RecyclerView. LayoutManager? ) : View? {
val snapView = super . findSnapView ( layoutManager)
val snapPosition = snapView? . let { mRv. getChildAdapterPosition ( it) }
snapPosition? . let {
if ( snapPosition != RecyclerView. NO_POSITION) {
mAdapter. setSelectedPosition ( snapPosition)
}
}
return snapView
}
}
linearSnapHelper. attachToRecyclerView ( mRv)
mRv? . layoutManager = layoutManager
mRv? . adapter = mAdapter
}
fun dpToPx ( context: Context, dp: Float) : Int {
val metrics = context. resources. displayMetrics
return TypedValue. applyDimension ( TypedValue. COMPLEX_UNIT_DIP, dp, metrics) . toInt ( )
}
}
2024 - 06 - 21 01 : 18 : 42.794 17860 - 17860 Yang I attachToRecyclerView
2024 - 06 - 21 01 : 18 : 45.412 17860 - 17860 Yang I createScroller
2024 - 06 - 21 01 : 18 : 45.413 17860 - 17860 Yang I findTargetSnapPosition
2024 - 06 - 21 01 : 18 : 45.413 17860 - 17860 Yang I findSnapView
2024 - 06 - 21 01 : 18 : 45.413 17860 - 17860 Yang I getHorizontalHelper
2024 - 06 - 21 01 : 18 : 45.413 17860 - 17860 Yang I findCenterView
2024 - 06 - 21 01 : 18 : 45.413 17860 - 17860 Yang I getHorizontalHelper
2024 - 06 - 21 01 : 18 : 45.413 17860 - 17860 Yang I estimateNextPositionDiffForFling
2024 - 06 - 21 01 : 18 : 45.413 17860 - 17860 Yang I calculateScrollDistance
2024 - 06 - 21 01 : 18 : 45.413 17860 - 17860 Yang I computeDistancePerChild
2024 - 06 - 21 01 : 18 : 45.430 17860 - 17860 Yang I onTargetFound
2024 - 06 - 21 01 : 18 : 45.430 17860 - 17860 Yang I calculateDistanceToFinalSnap
2024 - 06 - 21 01 : 18 : 45.430 17860 - 17860 Yang I getHorizontalHelper
2024 - 06 - 21 01 : 18 : 45.430 17860 - 17860 Yang I distanceToCenter
2024 - 06 - 21 01 : 18 : 45.430 17860 - 17860 Yang I calculateSpeedPerPixel
2024 - 06 - 21 01 : 18 : 46.400 17860 - 17860 Yang I findSnapView
2024 - 06 - 21 01 : 18 : 46.400 17860 - 17860 Yang I getHorizontalHelper
2024 - 06 - 21 01 : 18 : 46.400 17860 - 17860 Yang I findCenterView
2024 - 06 - 21 01 : 18 : 46.400 17860 - 17860 Yang I calculateDistanceToFinalSnap
2024 - 06 - 21 01 : 18 : 46.400 17860 - 17860 Yang I getHorizontalHelper
2024 - 06 - 21 01 : 18 : 46.400 17860 - 17860 Yang I distanceToCenter
效果图