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
, но я получил тот же исключение
Как я могу достичь своей цели?
Ответы
Ответ 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);
}
});
}
}