OnScrollListener вызывается повторно - PullRequest
0 голосов
/ 14 октября 2019

У меня есть 2 элемента управления списком, каждый из которых отображает часть данных списка. Они ориентированы выше и ниже друг друга в фрагменте пользовательского интерфейса. Отображаемые данные являются информацией о шинах для автомобиля, поэтому в верхнем представлении списка отображаются данные для передних колес, а в нижнем представлении списка отображаются данные для задних колес (с изображением автомобиля между ними). Представления списка настроены так, что список данных в каждом представлении отражает данные в другом. Таким образом, верхняя строка верхнего списка имеет тот же набор данных, что и нижняя строка в нижнем списке. Когда пользователь прокручивает любой список, другой список прокручивается зеркально. Это позволяет пользователю прокручивать данные таким образом, чтобы данные, которые им интересны, располагались непосредственно над и под изображением автомобиля (нижний ряд верхнего списка, верхний ряд нижнего списка)

Для этого яя отвечаю на событие OnScrollListener-OnScroll в одном просмотре списка, производя вычисления позиции и вызывая listview.setSelectionFromTop (position, sety) в другом просмотре списка, чтобы обновить свою позицию.

Это нормально работает в Android 4.xx, когда я впервые выпустил приложение. Теперь в Android 7 и более поздних версиях, похоже, возникает состояние гонки, когда установка позиции в одном списке просмотра вызывает OnScroll для другого, и от этого дела идут бесконечно в бесконечной серии событий OnScroll.

Я попытался добавить флаг в функцию OnScroll, чтобы он не срабатывал, если он находится внутри вызова OnScroll из другого списка. Это не сработало, так как кажется, что события OnScroll, вызванные вызовом setSelectionFromTop, являются асинхронными, поэтому они происходят за пределами отмеченной области.

Одна вещь, которую я заметил, заключается в том, что кажется, что размеры представлений списка являются критическимии что они оба должны быть одинакового размера. В исходном макете (Android 4.x) я установил их layoutHeight = 120dp, чтобы зафиксировать их размер. Тем не менее, текущий Android, похоже, не учитывает значения layoutHeight, и в результате пользовательский интерфейс показывает их в разных размерах, это может быть ключом к загадке.

Вот код 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 голосов
/ 18 октября 2019

Я думаю, что ваши расчеты для положения и смещения неверны, поэтому 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);
        }
    }
};

Надеюсь, это поможет!

...