Подтвердить что ты не робот

RecyclerView notifyItemInserted IllegalStateException

Я переношу свой адаптер в RecyclerView.Adapter

, чего я хочу достичь: когда пользователь прокручивается ближе к концу, я хочу начать выборку данных, я также хочу добавить представление я ProgressBar в конце, чтобы пользователь знал, что еще больше данных.

способ, которым я реализовал это в своем BaseAdapter: на getView в запросе, запрошенном ближе всего к концу, я бы начал получать больше данных, вызываю notifyDataSetChanged (чтобы получить представление ProgressBar для отображения) и только затем вернуть представление, необходимое для getView.

что я пытался делать в RecyclerView.Adapter: Я попытался сделать то же самое в основном, на этот раз в методе onBindViewHolder,

но если я попытаюсь вызвать notifyItemInserted внутри этого метода, я получаю следующее исключение:

IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling

что я пытался: я заметил, что onBindViewHolder получает вызов из onLayoutChildren из LayoutManager, я попытался переопределить его и вызывать notifyItemInserted после его super, но я получил тот же исключение

Как я могу достичь своей цели?

4b9b3361

Ответ 1

        Handler handler = new Handler();

        final Runnable r = new Runnable() {
            public void run() {
                adapter.notifyDataSetChanged();
            }
        };

        handler.post(r);

Мой пример кода, где я вызываю adapter.notifyDataSetChanged(); от деятельности

Ответ 2

Также стоит отметить JavaDoc в методе RecyclerView.isComputingLayout(), который вызывает это исключение:

/**
 * Returns whether RecyclerView is currently computing a layout.
 * <p>
 * If this method returns true, it means that RecyclerView is in a lockdown state and any
 * attempt to update adapter contents will result in an exception because adapter contents
 * cannot be changed while RecyclerView is trying to compute the layout.
 * <p>
 * It is very unlikely that your code will be running during this state as it is
 * called by the framework when a layout traversal happens or RecyclerView starts to scroll
 * in response to system events (touch, accessibility etc).
 * <p>
 * This case may happen if you have some custom logic to change adapter contents in
 * response to a View callback (e.g. focus change callback) which might be triggered during a
 * layout calculation. In these cases, you should just postpone the change using a Handler or a
 * similar mechanism.
 *
 * @return <code>true</code> if RecyclerView is currently computing a layout, <code>false</code>
 *         otherwise
 */
public boolean isComputingLayout() {
    return mLayoutOrScrollCounter > 0;
}

Основная фраза:

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

В моем случае я модифицировал содержимое адаптера из другого потока, который (иногда) вызывал столкновение. Перенос обновления на обработчик в потоке пользовательского интерфейса естественно решает это.

Ответ 3

С помощью Handler для добавления элементов и вызова notify...() из этого Handler исправлена ​​проблема для меня.

Ответ 4

Активный и простой способ:

  recyclerView.post(new Runnable() {
                    @Override
                    public void run() {
                        adapter.notifyDataSetChanged();
                    }
                });

Объяснение: Вы используете свой экземпляр RecyclerView, а внутри метода post добавлен новый Runnable, добавленный в очередь сообщений. Runnable будет запускаться в потоке пользовательского интерфейса. Это ограничение для Android для доступа к потоку пользовательского интерфейса из фона (например, внутри метода, который будет выполняться в фоновом потоке).

Ответ 5

Попробуйте

if (!recyclerView.isComputingLayout()) {
        recyclerView.notifyDataSetChanged();
    }

Ответ 6

Я использую это:

Handler handler = new Handler();
private void notifyItemInsertedEx(final int position) {
    try {
        notifyItemInserted(position);
    } catch (Exception ex) {
        handler.post(new Runnable(){
            public void run(){
                notifyItemInserted(position);
            }
        });
    }
}