Я делаю собственный LayoutManager. Эффект, как показано на рисунке. Это то, что я хочу.
Это неправильный эффект после прокрутки.
Большой палец элемента № 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
}
}
}```