我有回收视图
,它可以水平滚动。每个视图都将捕捉到显示器的中心。为了实现这一点,我使用了LinearSnapHelper。这个工具工作得很好。
问题在于回收视图中的最后一项和第一项。当您滚动到开始时,这两项没有捕捉到中心
。如果您在滚动发生之前在开始时检查回收视图状态,您可以看到第一项位于屏幕中心的正确位置。我通过添加自定义的ItemOffsetDecoration实现了这一点。我根据显示和视图本身的宽度为最后和第一个视图添加了额外的填充,因此它将位于中间。
问题是,如果您滚动回收器视图并将其滚动回起点或终点,这些视图不会捕捉到中间。看起来LinearSnapHelper无法将它们检测为已捕捉。
回收站视图:
timelineSnapHelper = LinearSnapHelper()
timelineViewManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
timelineViewAdapter = TimelineListAdapter(timelineViewManager, timelineList, this)
timelineOffsetDecoration = ItemOffsetDecoration(context!!, 32, 45)
timelineRecyclerView = view.findViewById<RecyclerView>(R.id.timeline_recycler_view).apply {
layoutManager = timelineViewManager
adapter = timelineViewAdapter
addItemDecoration(timelineOffsetDecoration)
setHasFixedSize(true)
}
timelineSnapHelper.attachToRecyclerView(timelineRecyclerView)
timelineRecyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) {
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
val centerView = timelineSnapHelper.findSnapView(timelineRecyclerView.layoutManager)
val anim = AnimationUtils.loadAnimation(context, R.anim.scale_timeline_start)
centerView!!.startAnimation(anim)
anim.fillAfter = true
lastSnappedTime = centerView
} else if (lastSnappedTime != null){
lastSnappedTime = null
}
if(newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL && lastSnappedTime != null){
val anim = AnimationUtils.loadAnimation(context, R.anim.scale_timeline_end)
lastSnappedTime!!.startAnimation(anim)
anim.fillAfter = true
}
}
})
定制装饰:
class ItemOffsetDecoration(private val context: Context, private val edgePadding: Int, private var viewWidth: Int): RecyclerView.ItemDecoration() {
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State?) {
super.getItemOffsets(outRect, view, parent, state)
val itemCount = state!!.itemCount
val itemPosition = parent.getChildAdapterPosition(view)
// no position, leave it alone
if (itemPosition == RecyclerView.NO_POSITION) {
return
}
val displayMetrics = context.resources.displayMetrics
val displayWidth: Int = displayMetrics.widthPixels
val viewPixelWidth: Int = (viewWidth * (displayMetrics.densityDpi / 160f)).toInt()
val startEndPadding: Int = (displayWidth - viewPixelWidth) / 2
// first item
if (itemPosition == 0) {
outRect.set(startEndPadding, edgePadding, edgePadding, edgePadding)
}
// last item
else if (itemCount > 0 && itemPosition == itemCount - 1) {
outRect.set(edgePadding, edgePadding, startEndPadding, edgePadding)
}
// every other item
else {
outRect.set(edgePadding, edgePadding, edgePadding, edgePadding)
}
}
}
应用程序中问题的简短预览(我还必须修复一些动画错误:D
):
LinearSnapHelper
在相关视图的度量中包含了项目装饰,因此您的第一个和最后一个视图永远不会真正居中,因为度量的视图大小延伸到RecyclerView
的左侧或右侧。
你没有发布你的XML,所以你可能已经做了下面这样的事情。让开始视图和结束视图正确居中的典型方法是向回收视图
添加填充,并指定android: clipToPadd="false"
。这与从FAB等移动结束视图的技术相同。
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingStart="35dp"
android:paddingEnd="35dp"/>
在这里35dp
必须针对您的应用程序和布局进行调整,并且您不需要项目装饰-移动视图是其唯一目的。