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" > < TextViewandroid: 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.RecyclerViewandroid: 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_POSITIONoverride 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. sizefun setSelectedPosition ( position: Int) { val oldPosition = selectedPositionselectedPosition = positionnotifyItemChanged ( 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. REDstyle = 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 = spaceSizeoutRect. right = spaceSizespacePadding = ( 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. childCountfor ( i in 0 until childCount) { val child = parent. getChildAt ( i) val position = parent. getChildAdapterPosition ( child) val params = child. layoutParams as RecyclerView. LayoutParamsvar left : Intvar right : Intvar top : Intvar bottom : Intif ( position == 0 ) { left = child. left - params. leftMargin - spacePaddingright = child. right + params. rightMargin + spaceSizetop = child. top - params. topMarginbottom = child. bottom + params. bottomMargin} else if ( position == parent. adapter? . itemCount!! - 1 ) { left = child. left - params. leftMargin - spaceSizeright = child. right + params. rightMargin + spacePaddingtop = child. top - params. topMarginbottom = child. bottom + params. bottomMargin} else { left = child. left - params. leftMargin - spaceSizeright = child. right + params. rightMargin + spaceSizetop = child. top - params. topMarginbottom = 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 = recyclerViewsuper . 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_POSITIONval itemCount = layoutManager. itemCountif ( itemCount == 0 ) return RecyclerView. NO_POSITIONval currentView = findSnapView ( layoutManager) ?: return RecyclerView. NO_POSITIONval currentPosition = layoutManager. getPosition ( currentView) if ( currentPosition == RecyclerView. NO_POSITION) return RecyclerView. NO_POSITIONval vectorProvider = layoutManager as RecyclerView. SmoothScroller. ScrollVectorProviderval vectorForEnd = vectorProvider. computeScrollVectorForPosition ( itemCount - 1 ) ?: return RecyclerView. NO_POSITIONLog. i ( TAG, "findTargetSnapPosition" ) var maxHorizontalItemViewCount: Intvar maxVerticalItemViewCount: Intif ( 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_POSITIONvar targetPosition = currentPosition + finalItemCountif ( 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. childCountif ( childCount == 0 ) return null var closestItemView: View? = null val center = helper. startAfterPadding + helper. totalSpace / 2 var absClosest = Int. MAX_VALUEfor ( 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 = childDistanceclosestItemView = 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_VALUEvar maxPosition = Integer. MIN_VALUEval itemViewCount = layoutManager. childCountif ( itemViewCount == 0 ) return INVALID_DISTANCEfor ( 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 = positionminPositionView = child} if ( position > maxPosition) { maxPosition = positionmaxPositionView = child} } if ( minPositionView == null || maxPositionView == null ) return INVALID_DISTANCEval start = Math. min ( helper. getDecoratedStart ( minPositionView) , helper. getDecoratedStart ( maxPositionView) ) val end = Math. max ( helper. getDecoratedEnd ( minPositionView) , helper. getDecoratedEnd ( maxPositionView) ) val distance = end - startif ( distance <= 0 ) return INVALID_DISTANCEreturn 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 = recyclerViewsuper . 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. childCountif ( childCount == 0 ) return null var closestItemView: View? = null val center = helper. startAfterPadding + helper. totalSpace / 2 var absClosest = Int. MAX_VALUEfor ( 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 = childDistanceclosestItemView = 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_POSITIONval itemCount = layoutManager. itemCountif ( itemCount == 0 ) return RecyclerView. NO_POSITIONval currentView = findSnapView ( layoutManager) ?: return RecyclerView. NO_POSITIONval currentPosition = layoutManager. getPosition ( currentView) if ( currentPosition == RecyclerView. NO_POSITION) return RecyclerView. NO_POSITIONval vectorProvider = layoutManager as RecyclerView. SmoothScroller. ScrollVectorProviderval vectorForEnd = vectorProvider. computeScrollVectorForPosition ( itemCount - 1 ) ?: return RecyclerView. NO_POSITIONvar maxHorizontalItemViewCount: Intvar maxVerticalItemViewCount: Intif ( 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_POSITIONvar targetPosition = currentPosition + finalItemCountif ( 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_VALUEvar maxPosition = Integer. MIN_VALUEval itemViewCount = layoutManager. childCountif ( itemViewCount == 0 ) return INVALID_DISTANCEfor ( 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 = positionminPositionView = child} if ( position > maxPosition) { maxPosition = positionmaxPositionView = child} } if ( minPositionView == null || maxPositionView == null ) return INVALID_DISTANCEval start = Math. min ( helper. getDecoratedStart ( minPositionView) , helper. getDecoratedStart ( maxPositionView) ) val end = Math. max ( helper. getDecoratedEnd ( minPositionView) , helper. getDecoratedEnd ( maxPositionView) ) val distance = end - startif ( distance <= 0 ) return INVALID_DISTANCEreturn 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 = layoutManagermRv? . adapter = mAdapter} fun dpToPx ( context: Context, dp: Float) : Int { val metrics = context. resources. displayMetricsreturn 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
效果图