我需要修改与 RcyclerView 一起使用的垂直 LinearLayoutManager ,以便可以水平滚动。
当RecyclerView垂直滚动时,它会添加新项目,因为我正在扩展 LinearLayoutManager 。
我认为这样可以解决问题。
public class CustomLayoutManager extends LinearLayoutManager {
public TableLayoutManager(Context context) {
super(context);
}
@Override
public boolean canScrollHorizontally() {
return true;
}
@Override
public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
int delta = -dx;
offsetChildrenHorizontal(delta);
return -delta;
}
}
现在,当我滚动RecyclerView时,它可以正常工作,但内容可以在屏幕外滚动。
此外,当内容水平滚动然后我垂直滚动时,RecycerView将添加与左屏幕边框对齐的新视图,而不是初始视图。
演示行为的视频:
http://youtu.be/FbuZ_jph7pw
我做错了什么?
的更新
(由@yigit建议)
现在我正在使用 LinearLayoutManager 的修改版本(我不再扩展它了,因为我需要在私有方法 fill(....))。
这些是我所做的改变:
private int childrenLeftOffset = 0;
private int childrenMaxWith = 800; // I must update this value with children max width
@Override
public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
int delta = -dx;
int newOffset = childrenLeftOffset + delta;
if (newOffset < 0) {
if(newOffset < -childrenMaxWith) {
//right edge reached
offsetChildrenHorizontal(-(childrenMaxWith - Math.abs(childrenLeftOffset)));
childrenLeftOffset = -childrenMaxWith;
delta = 0; // RecyclerView will draw right edge effect.
} else {
//scroll horizontally
childrenLeftOffset = childrenLeftOffset + delta;
offsetChildrenHorizontal(delta);
}
} else {
//left edge reached
offsetChildrenHorizontal(Math.abs(childrenLeftOffset));
childrenLeftOffset = 0;
delta = 0; // RecyclerView will draw left edge effect.
}
return -delta;
}
private int fill(RecyclerView.Recycler recycler, RenderState renderState, RecyclerView.State state, boolean stopOnFocusable) {
........
........
left = left + myLeftOffset;
layoutDecorated(view, left + params.leftMargin, top + params.topMargin, right - params.rightMargin, bottom - params.bottomMargin);
.......
.......
}
滚动CustomLinearLayout(更新后的视频)http://youtu.be/DHeXkvpgruo
现在的问题是获取RecyclerView子级的最大宽度并更新childrenMaxWith。
答案 0 :(得分:2)
LinearLayoutManager确实支持在一个方向上滚动,因此当需要布局新的子项时,它只是从左侧开始。
offsetChildren
方法只会移动子项,不会保留任何信息。
最好克隆LinearLayoutManager并覆盖layoutChunk
方法。但是每次发布新的RV版本时,您都需要这样做。
另一种可能的(快速和肮脏)解决方案是:
layoutDecorated
,并通过水平滚动偏移量偏移left
和right
值,然后调用super。 这可能会奏效,但有可能打破LayoutManger未来的任何变化。虽然这种方法非常核心,但风险并不高。
答案 1 :(得分:0)
我最近在RecyclerView中显示表格数据时遇到了类似的问题。就我而言,所有被回收的视图都具有相同的宽度。我知道原来的问题不是这种情况,但是采用统一的宽度可以确保您的列都正确对齐,因此更容易阅读。它还使确定何时停止水平滚动变得更加容易。您只需使用getChildAt(0)?.measuredWidth
。此外,API可能自2015年以来在LinearLayoutManager中已更改:我发现如果您要使用yigit建议的“快速而肮脏”的解决方案,则必须覆盖layoutDecoratedWithMargins
而不是layoutDecorated
(它可能不会都无法覆盖两者)。这是我的实现方式:
class MyRowLayoutManager(context: Context) : LinearLayoutManager(context, RecyclerView.VERTICAL, false) {
private var horizontalOffset = 0
override fun canScrollHorizontally(): Boolean = true
override fun scrollHorizontallyBy(dx: Int, recycler: RecyclerView.Recycler?, state: RecyclerView.State?): Int {
if (childCount == 0) return 0
val rowWidth = getChildAt(0)?.measuredWidth ?: return 0
// if the row is narrower than the RecyclerView's width, don't scroll
if (rowWidth <= width) return 0
val remainingPxRight = rowWidth - width - horizontalOffset
// cut scrolling short if we're about to reach end of the row
val scrolled = when {
dx > remainingPxRight -> remainingPxRight
dx < -horizontalOffset -> -horizontalOffset
else -> dx
}
horizontalOffset += scrolled
offsetChildrenHorizontal(-scrolled)
return scrolled
}
override fun layoutDecorated(child: View, left: Int, top: Int, right: Int, bottom: Int) {
super.layoutDecorated(child, left - horizontalOffset, top, right - horizontalOffset, bottom)
}
override fun layoutDecoratedWithMargins(child: View, left: Int, top: Int, right: Int, bottom: Int) {
super.layoutDecoratedWithMargins(child, left - horizontalOffset, top, right - horizontalOffset, bottom)
}
}