vlayout
vlayout copied to clipboard
关于瀑布流的一点小建议,希望作者看到
经过一段时间的使用,发现瀑布流中有部分自己刷新的view,position为1的View比position为0的View要高的时候,会造成右侧自动上移一段距离。
经过查看代码,发现是每次刷新,都会调用onLayoutChildren,造成helper中layoutviews被调用。
但是由于helper.checkAnchorInfo()中,判断是否第一行用的是: boolean isStartLine = anchorPos == range.getLower(); 这个判断是判断是否是第一个元素,当第一个元素展示出来时不添加padding,当不是时添加padding。这样就造成了当第一个元素不展示了,第二个元素(另外列的第一个元素)展示时,刷新列表造成位置错乱。
修改方案: 在checkAnchorInfo()方法中添加: boolean isSpanStartLine = anchorPos - range.getLower() < mNumLanes; for (Span span : mSpans) { if (!span.mViews.isEmpty()) { //如果不是从尾部开始的 if (anchorInfo.layoutFromEnd) { span.cacheReferenceLineAndClear(helper.getReverseLayout() ^ anchorInfo.layoutFromEnd, isSpanStartLine? 0:offset, orientationHelper); }else { View view = span.mViews.get(0); if (isSpanStartLine && anchorPos == helper.getPosition(view)){//如果当前是Span的第一个 span.cacheReferenceLineAndClear(helper.getReverseLayout() ^ anchorInfo.layoutFromEnd, 0, orientationHelper); } else { span.cacheReferenceLineAndClear(helper.getReverseLayout() ^ anchorInfo.layoutFromEnd, offset, orientationHelper); } } } }
此写法感觉不太优雅,但能解决问题。
还请作者指正,感谢
"position为1的View比position为1的View要高的时候" 这句话是否有误? 可以以pr的形式改好发过来看看,整体看一下修复的方式。
@longerian 确实打错字了应该是: position为1的View比position为0的View要高的时候 以下是我修改的方式,有部分注释:
public void checkAnchorInfo(RecyclerView.State state, VirtualLayoutManager.AnchorInfoWrapper anchorInfo, LayoutManagerHelper helper) {
super.checkAnchorInfo(state, anchorInfo, helper);
ensureLanes();
final Range<Integer> range = getRange();
if (anchorInfo.layoutFromEnd) {
if (anchorInfo.position < range.getLower() + mNumLanes - 1) {
anchorInfo.position = Math.min(range.getLower() + mNumLanes - 1, range.getUpper());
}
} else {
if (anchorInfo.position > range.getUpper() - (mNumLanes - 1)) {
anchorInfo.position = Math.max(range.getLower(), range.getUpper() - (mNumLanes - 1));
}
}
View reference = helper.findViewByPosition(anchorInfo.position);
final boolean layoutInVertical = helper.getOrientation() == VERTICAL;
int mainGap = layoutInVertical ? mVGap : mHGap;
final OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();
if (reference == null) {
if (BuildConfig.DEBUG) {
Log.d(TAG, "checkAnchorInfo span.clear()");
}
for (Span span : mSpans) {
span.clear();
span.setLine(anchorInfo.coordinate);
}
} else {
int anchorPos = anchorInfo.layoutFromEnd ? Integer.MIN_VALUE : Integer.MAX_VALUE;
for (Span span : mSpans) {
if (!span.mViews.isEmpty()) {
if (anchorInfo.layoutFromEnd) {
View view = span.mViews.get(span.mViews.size() - 1);
anchorPos = Math.max(anchorPos, helper.getPosition(view));
} else {
View view = span.mViews.get(0);
anchorPos = Math.min(anchorPos, helper.getPosition(view));
}
}
}
int offset = INVALID_OFFSET;
if (!isOutOfRange(anchorPos)) {
//判断anchorPos是否是第一个元素
boolean isStartLine = anchorPos == range.getLower();
View view = helper.findViewByPosition(anchorPos);
if (view != null) {
if (anchorInfo.layoutFromEnd) {
anchorInfo.position = anchorPos;
final int endRef = orientationHelper.getDecoratedEnd(reference);
if (endRef < anchorInfo.coordinate) {
offset = anchorInfo.coordinate - endRef;
offset += (isStartLine ? 0 : mainGap);
anchorInfo.coordinate = orientationHelper.getDecoratedEnd(view) + offset;
} else {
offset = (isStartLine ? 0 : mainGap);
anchorInfo.coordinate = orientationHelper.getDecoratedEnd(view) + offset;
}
} else {
anchorInfo.position = anchorPos;
final int startRef = orientationHelper.getDecoratedStart(reference);
if (startRef > anchorInfo.coordinate) {
// move align up
offset = anchorInfo.coordinate - startRef;
offset -= (isStartLine ? 0 : mainGap);
anchorInfo.coordinate = orientationHelper.getDecoratedStart(view) + offset;
} else {
// 此时如果anchorPos的元素是第一行(startLine)中的元素,
// 第一个元素已经滑动出屏幕,
// 则offset = -mainGap;
// 之后执行span.cacheReferenceLineAndClear(helper.getReverseLayout() ^ anchorInfo.layoutFromEnd,
// offset, orientationHelper);
// 会造成anchorPos的元素一直往上移动
offset = -(isStartLine ? 0 : mainGap);
anchorInfo.coordinate = orientationHelper.getDecoratedStart(view) + offset;
}
}
}
}
if (BuildConfig.DEBUG) {
Log.d(TAG, "checkAnchorInfo span.cacheReferenceLineAndClear()");
}
boolean isSpanStartLine = anchorPos - range.getLower() < mNumLanes;
for (Span span : mSpans) {
if (!span.mViews.isEmpty()) {
//如果是从尾部开始的
if (anchorInfo.layoutFromEnd) {
span.cacheReferenceLineAndClear(helper.getReverseLayout() ^ anchorInfo.layoutFromEnd, isSpanStartLine? 0:offset, orientationHelper);
}else {
View view = span.mViews.get(0);
//判断是否需要添加offset
if (isSpanStartLine && anchorPos == helper.getPosition(view)){
span.cacheReferenceLineAndClear(helper.getReverseLayout() ^ anchorInfo.layoutFromEnd, 0, orientationHelper);
} else {
span.cacheReferenceLineAndClear(helper.getReverseLayout() ^ anchorInfo.layoutFromEnd, offset, orientationHelper);
}
}
}
}
}
}
修改后就正常了
如果这个padding是staggeredLayoutHelper的属性的话,vlayout的意图还是希望第一列和第二列的第一个元素的上边距都有一个padding空白,看你截图的效果,好像第一列没有padding的空白,但第二列的有,这个是符合你的效果? 所以还需要看一下你的staggeredLayoutHelper设置的属性、以及position = 0 和 1 的两个view的属性,按照预期,他们的top本应该对齐,所以不该发生那样的问题。
我的ViewHolder是同一个,只是有高有低而已,这是我瀑布流helper的设置方法
mStaggeredGridLayoutHelper.setMargin(ScreenUtils.Dp2Px(getContext(), 10), 0, ScreenUtils.Dp2Px(getContext(), 10), 0); mStaggeredGridLayoutHelper.setHGap(ScreenUtils.Dp2Px(getContext(), 5)); mStaggeredGridLayoutHelper.setVGap(ScreenUtils.Dp2Px(getContext(), 5));
如果我的VGap设置为0的话也不会出现这个问题。
根据checkAnchorInfo()方法里的判断
final boolean layoutInVertical = helper.getOrientation() == VERTICAL;
当我把第一列的第一个元素滑到屏幕外,但第二列第一个元素还在屏幕中时,这个判断就是false的,此时按照原来代码,
offset = -(isStartLine ? 0 : mainGap);
span.cacheReferenceLineAndClear(helper.getReverseLayout() ^ anchorInfo.layoutFromEnd, offset, orientationHelper);
第二列会自动向上移动一个VGap的距离。
什么情况下会出现『当我把第一列的第一个元素滑到屏幕外,但第二列第一个元素还在屏幕中时』?讲道理一开始他们的顶部是对齐的,应该是一起移动到屏幕外
@longerian 之前我发的那个GIF图就是这样的情况啊,左边的第一个元素已经滑上去了,右边的第一个元素那不是还在外面嘛...这时候刷新就会有问题
嗯 我先研究一下纯瀑布流时的表现。按理不应该出现左边的滑上去,右边的没有滑动。
是这样的,一开始是正常一起滑动的,但是当左边第一个View滑动出去后,此时停止手动滑动,然后现在子View中又有刷新布局的操作,例如gif图里面的动画或者是视频播放等,就会造成右边列自动往上移动一段距离。
主要原因就是布局的时候拿的mCachedStart不正确。
我再次测了一下,确实也有这个问题,但这样改还是有问题,我在demo里跑布局彻底乱掉了,所以我再想想更好的办法。
OK,由于上拉加载也经常会错位,我查了一下是因为缓存在上拉加载后被清除了,我把清楚缓存的操作放到了外面,只有在重新布局的时候才清除缓存。
@Override public void onItemsChanged(LayoutManagerHelper helper) { // mLazySpanLookup.clear(); }
没想到过了快两年才来回复,我正在测试改动这里,按道理不应该简单粗暴的clear掉,这会导致append数据之后因为这里clear了布局开始各种问题