OnScrollListener反复调用

时间:2019-10-14 04:54:51

标签: android listview onscrolllistener

我有2个listview控件,每个控件都显示列表数据的一部分。它们在UI片段中彼此上下定位。显示的数据是汽车的轮胎信息,因此上列表视图显示前轮的数据,下列表视图显示后轮的数据(中间有汽车图像)。配置列表视图,以便每个视图中的数据列表镜像另一个视图中的数据。因此,顶部列表的顶行与底部列表的底行具有相同的数据集。当用户滚动其中一个列表时,另一个列表将以镜像方式滚动。这使用户可以滚动数据,使他们感兴趣的数据位于汽车图像的正上方和正下方(顶部列表的底部行,底部列表的顶部行)

要完成此操作,我要响应一个列表视图上的OnScrollListener-OnScroll事件,进行位置计算并在另一个列表视图上调用listview.setSelectionFromTop(position,sety)以更新其位置。

当我首次发布该应用程序时,这在Android 4.xx中工作正常。现在,在Android 7及更高版本中,似乎出现了一种竞争情况,即在一个列表视图上设置位置会触发另一个列表视图上的OnScroll,并且在此过程中,永无止境的一系列OnScroll事件使事情变坏了。

我尝试过在OnScroll函数上添加一个标志,这样如果它在另一个列表视图的OnScroll调用中不会触发。这没有用,因为似乎通过调用setSelectionFromTop触发的OnScroll事件是异步的,因此它们发生在标记区域之外。

我注意到的一件事是,列表视图的大小似乎很关键,并且两者都必须完全相同。在原始布局(Android 4.x)中,我已设置其layoutHeight = 120dp来固定其大小。但是,当前的android似乎不尊重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事件的连续列表,当在Android 4.X上运行时,此列表在滚动完成后停止,而在7.x及更高版本上,它将继续并且基本上在两个列表视图上都冻结了滚动,直到我刷新片段

1 个答案:

答案 0 :(得分:0)

我认为您对位置和偏移量的计算不正确,因此2个ListView由于一个小的偏移量误差而反弹。

对于具有相同高度的ListView:

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);
        }
    }
};

希望有帮助!