【发布时间】:2019-01-01 23:56:31
【问题描述】:
我有recyclerView,可以水平滚动。每个视图都将捕捉到显示的中心。为了实现这一点,我使用了 LinearSnapHelper。这个工具工作得很好。
问题在于回收站视图中的 last 和 first 项目。当您滚动到开头时,这两个是not snapped to the center。如果您在滚动之前检查 recyclerview 状态,您可以看到第一个项目位于屏幕中心的正确位置。我通过添加我的自定义 ItemOffsetDecoration 来实现这一点。我已经根据显示和视图本身的宽度为最后一个和第一个视图添加了额外的填充,因此它将位于中间。
问题是,如果您滚动 recyclerview 并将其滚动回开始或结束,这些视图不会对齐到中间。看起来 LinearSnapHelper 无法将它们检测为已捕捉。
RecyclerViewInit:
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)
}
}
}
应用中问题的简短预览(I have to fix some animation bugs yet :D):
【问题讨论】:
-
当您说“LinearSnapHelper 无法检测到它们被捕捉”时,您的意思是文本没有精确地 放置在中心?如果是这样,它们一定只是偏离了几个像素,因为它们在我看来是居中的还是我误解了?我认为您不是在谈论第一个和最后一个项目缺少动画,而是实际位置。
-
正确,它们已关闭。因为第一项和最后一项在视图中添加了额外的填充。并且 SnapHelper 将整个视图计算为 padding+view_width 作为视图的宽度。但我真的不知道如何以不同的方式做到这一点。如果我更改视图填充,它将导致 SnapHelper 捕捉功能出现故障。但是每个项目都在 ItemOffsetDecoration 中设置了填充顶部/左/右/底部 32dp,其他项目工作正常。第一项的额外 padding_start 和最后一项的 padding_end 是一个问题。有时超过 200dp(以 ItemOffsetDecoration 计算)
-
我就是这么想的。请参阅我的答案以了解解决此问题的另一种方法。
标签: android android-recyclerview kotlin android-animation horizontal-scrolling