带有偏移量的RecyclerView和PagerSnapHelper无法正确裁剪

时间:2018-06-26 14:59:31

标签: android android-recyclerview kotlin pagersnaphelper

我有两个RecyclerViews和两个ItemDecoration,它们分别为第一项和最后一项添加了固定的偏移量。

StartOffsetItemDecoration.kt

class StartOffsetItemDecoration : RecyclerView.ItemDecoration {

    private var mOffsetPx: Int = 0
    private var mOffsetDrawable: Drawable? = null
    private var mOrientation: Int = 0

    /**
     * Constructor that takes in the size of the offset to be added to the
     * start of the RecyclerView.
     *
     * @param offsetPx The size of the offset to be added to the start of the
     * RecyclerView in pixels
     */
    constructor(offsetPx: Int) {
        mOffsetPx = offsetPx
    }

    /**
     * Constructor that takes in a {@link Drawable} to be drawn at the start of
     * the RecyclerView.
     *
     * @param offsetDrawable The {@code Drawable} to be added to the start of
     *                       the RecyclerView
     */
    constructor(offsetDrawable: Drawable) {
        mOffsetDrawable = offsetDrawable
    }

    /**
     * Determines the size and location of the offset to be added to the start
     * of the RecyclerView.
     *
     * @param outRect The [Rect] of offsets to be added around the child view
     * @param view The child view to be decorated with an offset
     * @param parent The RecyclerView onto which dividers are being added
     * @param state The current RecyclerView.State of the RecyclerView
     */
    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
        super.getItemOffsets(outRect, view, parent, state)
        if (parent.getChildAdapterPosition(view) > 0) {
            return
        }

        mOrientation = (parent.layoutManager as LinearLayoutManager).orientation
        if (mOrientation == LinearLayoutManager.HORIZONTAL) {
            if (mOffsetPx > 0) {
                outRect.left = mOffsetPx
            } else if (mOffsetDrawable != null) {
                outRect.left = mOffsetDrawable!!.intrinsicWidth
            }
        } else if (mOrientation == LinearLayoutManager.VERTICAL) {
            if (mOffsetPx > 0) {
                outRect.top = mOffsetPx
            } else if (mOffsetDrawable != null) {
                outRect.top = mOffsetDrawable!!.intrinsicHeight
            }
        }
    }

    /**
     * Draws horizontal or vertical offset onto the start of the parent
     * RecyclerView.
     *
     * @param c The [Canvas] onto which an offset will be drawn
     * @param parent The RecyclerView onto which an offset is being added
     * @param state The current RecyclerView.State of the RecyclerView
     */
    override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        super.onDraw(c, parent, state)
        if (mOffsetDrawable == null) {
            return
        }

        if (mOrientation == LinearLayoutManager.HORIZONTAL) {
            drawOffsetHorizontal(c, parent)
        } else if (mOrientation == LinearLayoutManager.VERTICAL) {
            drawOffsetVertical(c, parent)
        }
    }


    private fun drawOffsetHorizontal(canvas: Canvas, parent: RecyclerView) {
        val parentTop = parent.paddingTop
        val parentBottom = parent.height - parent.paddingBottom
        val parentLeft = parent.paddingLeft
        val offsetDrawableRight = parentLeft + mOffsetDrawable!!.intrinsicWidth

        mOffsetDrawable?.setBounds(parentLeft, parentTop, offsetDrawableRight, parentBottom)
        mOffsetDrawable?.draw(canvas)
    }

    private fun drawOffsetVertical(canvas: Canvas, parent: RecyclerView) {
        val parentLeft = parent.paddingLeft
        val parentRight = parent.width - parent.paddingRight
        val parentTop = parent.paddingTop
        val offsetDrawableBottom = parentTop + mOffsetDrawable!!.intrinsicHeight

        mOffsetDrawable?.setBounds(parentLeft, parentTop, parentRight, offsetDrawableBottom)
        mOffsetDrawable?.draw(canvas)
    }

}

EndOffsetItemDecoration.kt

class EndOffsetItemDecoration: RecyclerView.ItemDecoration {

    private var mOffsetPx: Int = 0
    private var mOffsetDrawable: Drawable? = null
    private var mOrientation: Int = 0

    /**
     * Constructor that takes in the size of the offset to be added to the
     * start of the RecyclerView.
     *
     * @param offsetPx The size of the offset to be added to the start of the
     * RecyclerView in pixels
     */
    constructor(offsetPx: Int) {
        mOffsetPx = offsetPx
    }

    /**
     * Constructor that takes in a {@link Drawable} to be drawn at the start of
     * the RecyclerView.
     *
     * @param offsetDrawable The {@code Drawable} to be added to the start of
     *                       the RecyclerView
     */
    constructor(offsetDrawable: Drawable) {
        mOffsetDrawable = offsetDrawable
    }

    /**
     * Determines the size and location of the offset to be added to the end
     * of the RecyclerView.
     *
     * @param outRect The [Rect] of offsets to be added around the child view
     * @param view The child view to be decorated with an offset
     * @param parent The RecyclerView onto which dividers are being added
     * @param state The current RecyclerView.State of the RecyclerView
     */
    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
        super.getItemOffsets(outRect, view, parent, state)

        val itemCount = state.itemCount
        if (parent.getChildAdapterPosition(view) != itemCount - 1) {
            return
        }

        mOrientation = (parent.layoutManager as LinearLayoutManager).orientation
        if (mOrientation == LinearLayoutManager.HORIZONTAL) {
            if (mOffsetPx > 0) {
                outRect.right = mOffsetPx
            } else if (mOffsetDrawable != null) {
                outRect.right = mOffsetDrawable!!.intrinsicWidth
            }
        } else if (mOrientation == LinearLayoutManager.VERTICAL) {
            if (mOffsetPx > 0) {
                outRect.bottom = mOffsetPx
            } else if (mOffsetDrawable != null) {
                outRect.bottom = mOffsetDrawable!!.intrinsicHeight
            }
        }
    }

    /**
     * Draws horizontal or vertical offset onto the end of the parent
     * RecyclerView.
     *
     * @param c The [Canvas] onto which an offset will be drawn
     * @param parent The RecyclerView onto which an offset is being added
     * @param state The current RecyclerView.State of the RecyclerView
     */
    override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        super.onDraw(c, parent, state)
        if (mOffsetDrawable == null) {
            return
        }

        if (mOrientation == LinearLayoutManager.HORIZONTAL) {
            drawOffsetHorizontal(c, parent)
        } else if (mOrientation == LinearLayoutManager.VERTICAL) {
            drawOffsetVertical(c, parent)
        }
    }

    private fun drawOffsetHorizontal(canvas: Canvas, parent: RecyclerView) {
        val parentTop = parent.paddingTop
        val parentBottom = parent.height - parent.paddingBottom

        val lastChild = parent.getChildAt(parent.childCount - 1)
        val lastChildLayoutParams = lastChild.layoutParams as RecyclerView.LayoutParams
        val offsetDrawableLeft = lastChild.right + lastChildLayoutParams.rightMargin
        val offsetDrawableRight = offsetDrawableLeft + mOffsetDrawable!!.intrinsicWidth

        mOffsetDrawable?.setBounds(offsetDrawableLeft, parentTop, offsetDrawableRight, parentBottom)
        mOffsetDrawable?.draw(canvas)
    }

    private fun drawOffsetVertical(canvas: Canvas, parent: RecyclerView) {
        val parentLeft = parent.paddingLeft
        val parentRight = parent.width - parent.paddingRight

        val lastChild = parent.getChildAt(parent.childCount - 1)
        val lastChildLayoutParams = lastChild.layoutParams as RecyclerView.LayoutParams
        val offsetDrawableTop = lastChild.bottom + lastChildLayoutParams.bottomMargin
        val offsetDrawableBottom = offsetDrawableTop + mOffsetDrawable!!.intrinsicHeight

        mOffsetDrawable?.setBounds(parentLeft, offsetDrawableTop, parentRight, offsetDrawableBottom)
        mOffsetDrawable?.draw(canvas)
    }
}

我还在两个PagerSnapHelper上都添加了一个RecyclerViews,但在给定的偏移量下它们似乎效果不佳。我认为我可能缺少某些配置,或者使PageSnapHelper意识到了偏移量,以便它可以为第一个和最后一个元素计算正确的对齐方式。

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_anomaly_solution_hours)

        hours.layoutManager = LinearLayoutManager(
                this@AnomalySolutionHoursActivity, LinearLayoutManager.VERTICAL, false
        )
        minutes.layoutManager = LinearLayoutManager(
                this@AnomalySolutionHoursActivity, LinearLayoutManager.VERTICAL, false
        )

        hours.setHasFixedSize(true)
        minutes.setHasFixedSize(true)

        hours.addItemDecoration(StartOffsetItemDecoration(calculateOffset().toInt()))
        hours.addItemDecoration(EndOffsetItemDecoration(calculateOffset().toInt()))

        minutes.addItemDecoration(StartOffsetItemDecoration(calculateOffset().toInt()))
        minutes.addItemDecoration(EndOffsetItemDecoration(calculateOffset().toInt()))

        val hoursSnapHelper = PagerSnapHelper()
        val minutesSnapHelper = PagerSnapHelper()

        hours.adapter = TimeAdapter((0..10).toList().toTypedArray(), this@AnomalySolutionHoursActivity,
                object: TimeAdapter.OnItemClickListener {
                    override fun onItemClick(unit: Int) {

                    }
                })
        hoursSnapHelper.attachToRecyclerView(hours)


        minutes.adapter = TimeAdapter((0..60).toList().toTypedArray(), this@AnomalySolutionHoursActivity,
                object: TimeAdapter.OnItemClickListener {
                    override fun onItemClick(unit: Int) {

                    }
                })
        minutesSnapHelper.attachToRecyclerView(minutes)

    }

    private fun calculateOffset(): Float {
        val listHalfHeight = (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 250f, resources.displayMetrics)) / 2
        val listItemHalfHeight = (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48f, resources.displayMetrics)) / 2
        return listHalfHeight - listItemHalfHeight
    }
}

有人能够使PagerSnapHelper与包含偏移量的RecyclerView一起使用吗? LinearSnapHelper似乎比PagerSnapHelper更好,但是仍然无法捕捉到第一项和最后一项。

谢谢!

更新

使两个列表的高度都达到500dp,但没有任何变化。

enter image description here

更新2

在第一个和最后一个位置添加了一个空项目,现在效果很好。在计算尺寸位置时,只需记住这些项目即可。

enter image description here

0 个答案:

没有答案