Размер представления становится разным между LayoutManager и ViewHolder - PullRequest
0 голосов
/ 21 апреля 2020

Я делаю собственный LayoutManager. Эффект, как показано на рисунке. What I want is this Это то, что я хочу.

Wrong effect demo picture Это неправильный эффект после прокрутки.

Большой палец элемента № 9 и № 11 не заполнил полностью, это патентное представление. Голубой - это цвет фона изображения большого пальца, но картинка не в полном размере. Я печатаю размер в методе LayoutManager onLayoutChildren и scrollHorizontallyBy, размер представления правильный. Но в методе Adapter onBindViewHolder размер представления неправильный, поэтому glide сделал неправильный размер большого пальца. Вопрос в том, почему размер отличается между LayoutManager и Adapter.

Мой пользовательский код LayoutManager

class GalleryLayoutManager(private val gap: Int) : RecyclerView.LayoutManager() {

    companion object {
        private val TAG = GalleryLayoutManager::class.java.simpleName
    }

    private val sizes = arrayOfNulls<Size>(4)
    private var groupWidth: Int = 0
    private var maxScrollX = 0

    private var groupSizeWithWidth = 0

    private val orientationHelper = OrientationHelper.createHorizontalHelper(this)

    init {
        /*if (orientation != RecyclerView.HORIZONTAL && orientation != RecyclerView.VERTICAL) {
            throw IllegalArgumentException("Illegal orientation($orientation), " +
                "must be RecyclerView.HORIZONTAL or RecyclerView.VERTICAL")
        }*/
    }

    override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams {
        return RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.MATCH_PARENT)
    }

    override fun onLayoutChildren(recycler: RecyclerView.Recycler, state: RecyclerView.State) {
        if (itemCount == 0) {
            detachAndScrapAttachedViews(recycler)
            return
        }
        if (childCount == 0 && state.isPreLayout) {
            return
        }

        if (sizes.isNulls()) {
            val h0 = height- 2 * gap
            sizes[0] = Size(h0, h0)

            val h1 = (height - 3 * gap) / 2
            sizes[1] = Size(h0, h1)

            val h2 = (h0 - gap) / 2
            sizes[2] = Size(h2, h2)
            sizes[3] = Size(h2, h2)

            groupWidth = sizes[0]!!.width + sizes[1]!!.width + gap * 2
            groupSizeWithWidth = getItemCountWithinWidth()
        }

        val groupCountTotally = itemCount / 4
        val remainCount = itemCount % 4

        val distance = groupCountTotally * groupWidth +
            when (remainCount) {
                0 -> {
                    gap
                }
                1 -> {
                    sizes[0]!!.width + gap * 2
                }
                else -> {
                    groupWidth + gap
                }
            }

        maxScrollX = min(0, width - distance)

        detachAndScrapAttachedViews(recycler)

        for (p in 0 until itemCount) {
            val m = p % sizes.size
            val offsetX = getAbsoluteOffsetXForPosition(p) + scrollX
            val offsetY = when(m) {
                0,1 -> gap
                2,3 -> gap * 2 + sizes[1]!!.height
                else -> 0
            }
            val s = sizes[m]!!
            val l = offsetX
            val r = l + s.width
            val t = offsetY
            val b = t + s.height
            if (r < 0) {
                continue
            }
            if (l > width) {
                break
            }
            val child = recycler.getViewForPosition(p)
            child.layoutParams.apply {
                width = s.width
                height = s.height
            }
            measureChildWithMargins(child, s.width, s.height)
            child.requestLayout()
            addView(child)

            layoutDecoratedWithMargins(child, l, t, r, b)
        }

    }

    override fun canScrollHorizontally(): Boolean {

        return true
    }

    private var scrollX = 0
    override fun scrollHorizontallyBy(
        dx: Int,
        recycler: RecyclerView.Recycler,
        state: RecyclerView.State
    ): Int {
        // <- positive
        // -> negative

        var predictSX = scrollX - dx
        if (dx > 0 && predictSX < maxScrollX && scrollX >= maxScrollX) {
            predictSX = maxScrollX
        } else if (dx < 0 && predictSX > 0 && scrollX <= 0) {
            predictSX = 0
        }
        val newDX = scrollX - predictSX

        scrollX = predictSX

        recycleViews(newDX, recycler)
        fill(dx, recycler)
        offsetChildrenHorizontal(-newDX)

        return newDX
    }


    private fun fill(dx: Int, recycler: RecyclerView.Recycler) {
        if (dx > 0) {
            val lastVisibleView = getChildAt(childCount - 1) ?: return
            val position = getPosition(lastVisibleView)
            if (position >= itemCount - 1) {
                return
            }
            val nextPosition = position + 1
            val m = nextPosition % sizes.size
            val s = sizes[m]!!
            val nextView = recycler.getViewForPosition(nextPosition)
            nextView.layoutParams.apply {
                width = s.width
                height = s.height
            }
            addView(nextView)
            if (position == 9) {
                Log.v(TAG, "dx=$dx fill addViewFor nextPosition=$nextPosition")
            }
            measureChild(nextView, s.width, s.height)
            nextView.requestLayout()

            val viewWidth = getDecoratedMeasuredWidth(nextView)
            val viewHeight = getDecoratedMeasuredHeight(nextView)

            val offsetX = when(m) {
                0, 1, 3 -> lastVisibleView.right + gap
                else -> lastVisibleView.left
            }
            val offsetY = when(m) {
                0, 1 -> gap
                else -> sizes[1]!!.height + 2 * gap
            }
            layoutDecorated(nextView, offsetX, offsetY, offsetX + viewWidth, offsetY + viewHeight)
        } else if (dx < 0) {
            val firstVisibleView = getChildAt(0) ?: return
            val position = getPosition(firstVisibleView)
            if (position <= 0) {
                return
            }
            val prePosition = position - 1
            val m = prePosition % sizes.size
            val s = sizes[m]!!
            val preView = recycler.getViewForPosition(prePosition)
            preView.layoutParams.apply {
                width = s.width
                height = s.height
            }
            addView(preView)
            measureChild(preView, s.width, s.height)
            preView.requestLayout()

            val viewWidth = getDecoratedMeasuredWidth(preView)
            val viewHeight = getDecoratedMeasuredHeight(preView)

            val offsetX = when(m) {
                0 -> firstVisibleView.left - gap - s.width
                1 -> firstVisibleView.left
                2 -> firstVisibleView.left - gap - s.width
                else -> firstVisibleView.left - gap - s.width
            }
            val offsetY = when(m) {
                0, 1 -> gap
                else -> sizes[1]!!.height + 2 * gap
            }
            layoutDecorated(preView, offsetX, offsetY, offsetX + viewWidth, offsetY + viewHeight)
        }
    }

    private fun recycleViews(dx: Int, recycler: RecyclerView.Recycler) {
        for (i in 0 until itemCount) {
            val childView = getChildAt(i) ?: return
            val position = getPosition(childView)
            //左滑
            if (dx > 0) {
                //移除并回收 原点 左侧的子View
                if (childView.right - dx < 0) {
                    removeAndRecycleViewAt(i, recycler)
                }
            } else { //右滑
                //移除并回收 右侧即RecyclerView宽度之以外的子View
                if (childView.left - dx > width) {
                    removeAndRecycleViewAt(i, recycler)
                }
            }
        }
    }



    private fun getItemCountWithinWidth(): Int {
        val groupCount = ceil(width * 1.0 / groupWidth).toInt()
        return groupCount * sizes.size
    }

    private fun getAbsoluteOffsetXForPosition(position: Int): Int {
        val g = position / sizes.size
        val m = position % sizes.size
        return groupWidth * g + when(m) {
            0 -> gap
            1 -> gap * 2 + sizes[0]!!.width
            2 -> gap * 2 + sizes[0]!!.width
            3 -> gap * 3 + sizes[0]!!.width + sizes[2]!!.width
            else -> 0
        }
    }

}```
...