使用LinearSnapHelper捕捉recyclerview中的第一项和最后一项的问题

时间:2018-07-25 08:27:22

标签: android android-recyclerview kotlin android-animation horizontal-scrolling

我有recyclerView,可以沿水平方向滚动。每个视图将捕捉到显示的中心。为了实现这一点,我使用了LinearSnapHelper。这个工具很好用。

问题出在回收站视图中的最后第一项目。滚动到开始时,这两个是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)中问题的简短预览:

enter image description here

1 个答案:

答案 0 :(得分:5)

LinearSnapHelper在相关视图的度量中包括项目修饰,因此,由于所测量的视图大小扩展到RecyclerView的左侧或右侧,因此您的第一个和最后一个视图永远不会居中。 / p>

您没有发布XML,因此您可能已经执行了以下操作。使开始和结束视图正确居中的典型方法是在RecyclerView中添加填充并指定android:clipToPadding="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必须针对您的应用和布局进行调整,并且您不需要项目装饰来改变视图是唯一的目的。