Удалите дочерние данные из адаптера RecyclerView, не затрагивая видимое в данный момент представление.

Я создаю бесконечный список горизонтальной прокрутки, используя представление Recycler. 1-2 дочерних элемента могут быть видны на экране все время (поскольку их ширина больше, чем окно просмотра экрана, поэтому у нас никогда не будет видно более 2 дочерних элементов).

Исходное состояние:

начальное состояние


Пока пользователь прокручивает (он может прокручивать ОБА вперед и назад, но для встряхивания этого примера предположим, что он прокручивает НАЗАД), я извлекаю больше данных и добавляю их в начало к моим данным адаптера.

Затем я звоню notifyItemRangeInserted - пока все хорошо.

После добавления:

После добавления и уведомленияItemRangeInserted


Время отсечения:

отсечение противоположного конца данных

В то же время я добавляю все эти дочерние элементы, которые я проверяю, теперь мы получаем более MAX_DATASOURCE_CNT элементов в источнике данных.

Предположим, что MAX_DATASOURCE_CNT равно 6 для этого примера.

Если да, я удаляю лишние дочерние элементы из конца нашего источника данных, а затем вызываю notifyItemRangeRemoved.

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

Проблема:

дьявол

После того, как процесс prepend имеет место, если пользователь прокручивает слишком быстро, иногда onBindViewHolder(RecyclerView.ViewHolder holder, int position) get вызывается с position == 0 (также holder.getAdapterPosition() == 0 )

Это полностью искажает мою точку зрения, как если бы она перескочила на совершенно не относящегося к делу ребенка и показала его.

Текущий обходной путь:

Я заметил, что если я удалю весь процесс отсечения, который в основном состоит из этих строк кода:

if (itemCountAfterPrepend > MAX_DATASOURCE_CNT) {

    int extraItemCnt = itemCountAfterPrepend - MAX_DATASOURCE_CNT;

    // remove every extra item from the end of the array
    removeRange(MAX_DATASOURCE_CNT, itemCountAfterPrepend);

    // and notify our adapter about the removal
    notifyItemRangeRemoved(MAX_DATASOURCE_CNT, extraItemCnt);
}

все работает нормально. Но я действительно хочу обрезать свои данные. Что я могу сделать?

Спасибо


person SudoPlz    schedule 01.06.2018    source источник
comment
Помогает ли это увеличить значение MAX_DATASOURCE_CNT?   -  person Jantzilla    schedule 01.06.2018
comment
Не совсем. Почему это поможет?   -  person SudoPlz    schedule 01.06.2018
comment
Не является ли это основанием для определения количества отсечения?   -  person Jantzilla    schedule 01.06.2018
comment
Да, но все дело в том, что я хочу обрезать, я не хочу, чтобы он срабатывал реже. Кроме того, уменьшение частоты срабатывания не решает проблему.   -  person SudoPlz    schedule 01.06.2018
comment
Я не понимаю, почему вы загружаете больше данных, чем достаточно. Вам нужно реализовать разбиение на страницы, и вам не нужно удалять строки.   -  person just    schedule 01.06.2018
comment
Этот горизонтальный просмотр с бесконечной прокруткой представляет собой календарь, и каждый элемент представляет собой день (который содержит встречи и т. д.), и никогда не будет точки, когда у меня будет достаточно данных. Пользователь может уйти так далеко назад, как он хочет, и он также может двигаться так далеко вперед, как он хочет. Но я не могу держать в памяти все календарные дни, это не умно. Теперь это имеет больше смысла?   -  person SudoPlz    schedule 01.06.2018
comment
Я предполагаю, что я имею в виду настроить свой код так, чтобы он обрезал меньше, чтобы быстрая прокрутка не нарушала целостность данных.   -  person Jantzilla    schedule 01.06.2018
comment
Вместо того, чтобы давать весь новый извлеченный список, может быть, вам следует указать разницу между MAX_DATASOURCE_CNT и длиной элемента адаптера?   -  person Cochi    schedule 01.06.2018
comment
@Cochi Я добавляю детей в начало списка и удаляю их из конца списка. То, что вы предлагаете, сработало бы, если бы я добавлял И удалял с одной и той же стороны.   -  person SudoPlz    schedule 01.06.2018


Ответы (1)


Хорошо, я не нашел конкретного ответа, поэтому вместо этого вот что я сделал.

Поскольку проблема была notifyItemRangeRemoved запущена во время привязки представления, а представление было привязано, потому что пользователь все еще прокручивал, я удалил процесс отсечения из функций добавления/добавления.

Вместо этого я сделал процесс отсечения отдельной функцией (clipExtraDataIfNecessary).

Этот get вызывается ТОЛЬКО в onScrollStateChanged(RecyclerView recyclerView, int newState) и ТОЛЬКО когда newState == SCROLL_STATE_IDLE, т.е. пользователь остановил прокрутку:

Методы обрезки данных теперь выглядят так:

/**
 * make sure we never surpass the MAX_DATASOURCE_CNT limit
 * Clips the data ArrayList from the ending (right) side
 */
public void clipExtraEndData() {
    int itemCountAfterPrepend = this.data.size();
    // if we went above our max data source count limit
    if (itemCountAfterPrepend > MAX_DATASOURCE_CNT) {
        int extraItemCnt = itemCountAfterPrepend - MAX_DATASOURCE_CNT;

        synchronized (dataMutationLock) {
            // remove every extra item from the end of the array
            removeRange(MAX_DATASOURCE_CNT, itemCountAfterPrepend);

            // and notify our adapter about the removal
            notifyItemRangeRemoved(MAX_DATASOURCE_CNT, extraItemCnt);
        }
    }
}


/**
 * make sure we never surpass the MAX_DATASOURCE_CNT limit
 * Clips the data ArrayList from the beginning (left) side
 */
public void clipExtraStartData() {
    int itemCountAfterAppend = this.data.size();
    // if we went above our max data-source count limit
    if (itemCountAfterAppend > MAX_DATASOURCE_CNT) {
        int extraItemCnt = itemCountAfterAppend - MAX_DATASOURCE_CNT;

        synchronized (dataMutationLock) {
            // remove every extra item from the beginning of the array
            removeRange(0, extraItemCnt);

            // and notify our adapter about the removal
            notifyItemRangeRemoved(0, extraItemCnt);
        }
    }
}

/**
 * Removes all the extra data that we have accumulated by prepending/appending
 * as the user was scrolling
 *
 * CAUTION: MAKE SURE YOU RUN THIS at a time where the user is NOT scrolling,
 * otherwise we run the risk of bindViewRow being called with preLayout position
 * values (a.k.a 0)
 * @param curVisiblePosition
 */
public void clipExtraDataIfNecessary(int curVisiblePosition) {
    int initialItemCnt = size();
    if (initialItemCnt > MAX_DATASOURCE_CNT) {
        if (curVisiblePosition < initialItemCnt - curVisiblePosition) {
            // we have extra children on the end of our data
            clipExtraEndData();
        } else {
            // we have extra children on the beginning of our data
            clipExtraStartData();
        }
    }
}
person SudoPlz    schedule 01.06.2018