RecyclerView со смещениями и PagerSnapHelper неправильно обрезаются - PullRequest
0 голосов
/ 26 июня 2018

У меня два 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, но все еще не может привязать первый и последний элементы.

Спасибо!

UPDATE

Сделал оба списка высотой до 500dp, но ничего не изменилось.

enter image description here

ОБНОВЛЕНИЕ 2

Добавлен пустой элемент в первую и последнюю позиции, и теперь он работает довольно хорошо. Просто нужно помнить эти предметы при расчете позиций с размерами.

enter image description here

...