我有两个listview控件,每个控件都显示列表的部分数据。它们在UI片段中上下排列。显示的数据是汽车的轮胎信息,因此顶部的列表视图显示前轮的数据,下方的列表视图显示后轮的数据(它们之间有一个汽车图像)。对列表视图进行配置,使每个视图中的数据列表反映另一个视图中的数据。因此,顶部列表的顶部行与底部列表中的底部行具有相同的数据集。当用户滚动其中一个列表时,另一个列表以镜像方式滚动。这使得用户能够滚动数据,使得他们感兴趣的数据位于汽车图像的正上方和正下方(顶部列表的底部行,底部列表的顶部行)
为此,我在一个listview上响应OnScrollListener-OnScroll事件,进行位置计算,并在另一个listview上调用listview.setSelectionFromTop( position,sety)来更新其位置。
当我第一次发布该应用程序时,这在Android 4.xx中运行良好。现在在Android7和更高版本中,似乎正在发生竞争条件,在一个列表视图上设置位置会触发另一个列表视图上的OnScroll,然后事情就会在一系列永无止境的OnScroll事件中变得糟糕。
我尝试在OnScroll函数上添加一个标志,这样当它位于来自另一个列表视图的OnScroll调用中时,它就不会触发。这不起作用,因为从调用setSelectionFromTop触发的OnScroll事件似乎是异步的,因此它们发生在标记区域之外。
我注意到的一件事是,列表视图的大小似乎很关键,它们需要完全相同的大小。在原始布局(Android4.x)中,我已经设置了它们的layoutHeight=120dp来固定它们的大小。然而,当前的安卓系统似乎并不尊重layoutHeight的值,并且结果UI将它们显示为不同的大小,这可能是解开这个难题的关键。
下面是其中一个列表视图的onScroll代码,另一个列表视图的代码是相同的,只需将lvTop替换为lvBottom
OnScrollListener lTop=new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (fInBottomScrollListener)
return;
fInTopScrollListener=true;
// Get position of this lv top
View cTop = lvTop.getChildAt(0);
View cBottom = lvBottom.getChildAt(0);
if (cTop==null || cBottom==null) return;
int topOffset = cTop.getTop();
int itemHeight = cTop.getHeight();
int firstVisPos = lvTop.getFirstVisiblePosition();
int setposition = totalItemCount - (visibleItemCount - (topOffset!=0 ? 1:0)) - firstVisPos-1;
int sety= -(itemHeight + (topOffset>0 ? 0:topOffset));
lvBottom.setSelectionFromTop(setposition, sety);
fInTopScrollListener=false;
}
};我在日志中看到一个连续的onScroll事件列表,当在Android4.x上运行时,这个列表在滚动完成后停止,而在7.x和更高版本上,它继续,滚动在两个列表视图上基本上是冻结的,直到我刷新片段
发布于 2019-10-18 22:58:37
我认为你对位置和偏移量的计算是不正确的,所以2个ListViews是因为一个小的偏移量误差而跳动。
对于相同高度的ListViews:
AbsListView.OnScrollListener lTop = new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (!fInBottomScrollListener) {
fInTopScrollListener = true;
View cTop = lvTop.getChildAt(visibleItemCount - 1);
if (cTop == null) return;
int setposition = totalItemCount - visibleItemCount - firstVisibleItem;
int sety = view.getHeight() - cTop.getBottom();
lvBottom.setSelectionFromTop(setposition, sety);
fInTopScrollListener = false;
}
}
};
AbsListView.OnScrollListener lBottom = new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (!fInTopScrollListener) {
fInBottomScrollListener = true;
View cBottom = lvBottom.getChildAt(visibleItemCount - 1);
if (cBottom == null) return;
int setposition = totalItemCount - visibleItemCount - firstVisibleItem;
int sety = view.getHeight() - cBottom.getBottom();
lvTop.setSelectionFromTop(setposition, sety);
fInBottomScrollListener = false;
}
}
};更新:
这是另一种方法,不管列表视图的高度是否相同:
final private static int SCROLL_DELAY = 100;
long timestampScroll;
AbsListView.OnScrollListener lTop = new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (!fInBottomScrollListener) {
fInTopScrollListener = true;
View cTop = lvTop.getChildAt(0);
View cBot = lvBottom.getChildAt(0);
if (cTop == null || cBot == null) return;
int lvTopMovableRange = (cTop.getHeight() + lvTop.getDividerHeight()) * totalItemCount
- lvTop.getDividerHeight() - lvTop.getHeight();
int lvBotMovableRange = (cBot.getHeight() + lvBottom.getDividerHeight()) * totalItemCount
- lvBottom.getDividerHeight() - lvBottom.getHeight();
int lvTopPointer = (cTop.getHeight() + lvTop.getDividerHeight()) * firstVisibleItem
- cTop.getTop();
int lvBotPointer = (int)((float)(lvTopMovableRange - lvTopPointer)/lvTopMovableRange*lvBotMovableRange);
int setposition = lvBotPointer / (cBot.getHeight() + lvBottom.getDividerHeight());
int sety = -lvBotPointer + setposition * (cBot.getHeight() + lvBottom.getDividerHeight());
timestampScroll = System.currentTimeMillis();
lvBottom.setSelectionFromTop(setposition, sety);
lvTop.postDelayed(new Runnable() {
@Override
public void run() {
if (System.currentTimeMillis() - timestampScroll > (SCROLL_DELAY - 10)) fInTopScrollListener = false;
}
}, SCROLL_DELAY);
}
}
};
AbsListView.OnScrollListener lBottom = new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (!fInTopScrollListener) {
fInBottomScrollListener = true;
View cTop = lvTop.getChildAt(0);
View cBot = lvBottom.getChildAt(0);
if (cTop == null || cBot == null) return;
int lvTopMovableRange = (cTop.getHeight() + lvTop.getDividerHeight()) * totalItemCount
- lvTop.getDividerHeight() - lvTop.getHeight();
int lvBotMovableRange = (cBot.getHeight() + lvBottom.getDividerHeight()) * totalItemCount
- lvBottom.getDividerHeight() - lvBottom.getHeight();
int lvBotPointer = (cBot.getHeight() + lvBottom.getDividerHeight()) * firstVisibleItem
- cBot.getTop();
int lvTopPointer = (int)((float)(lvBotMovableRange - lvBotPointer)/lvBotMovableRange*lvTopMovableRange);
int setposition = lvTopPointer / (cTop.getHeight() + lvTop.getDividerHeight());
int sety = -lvTopPointer + setposition * (cTop.getHeight() + lvTop.getDividerHeight());
timestampScroll = System.currentTimeMillis();
lvTop.setSelectionFromTop(setposition, sety);
lvBottom.postDelayed(new Runnable() {
@Override
public void run() {
if (System.currentTimeMillis() - timestampScroll > (SCROLL_DELAY - 10)) fInBottomScrollListener = false;
}
}, SCROLL_DELAY);
}
}
};希望这能有所帮助!
https://stackoverflow.com/questions/58370288
复制相似问题