2014-10-24 5 views
30

я портирование моего адаптера в RecyclerView.AdapterRecyclerView notifyItemInserted IllegalStateException

, что я хочу добиться: , когда пользователь прокручивает вниз ближе к концу я хочу, чтобы начать получать данные, я также хочу добавить я 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, но у меня такое же исключение

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

+0

использовать LinearLayoutManager для этой работы – pskink

+0

вы могли бы написать пример ?, я не мог сделать это, не получая это исключение – user1333057

+0

переопределение onScrollStateChanged, здесь используйте findLastVisibleItemPosition, чтобы проверить, что вы внизу, и обновите свой адаптер. – pskink

ответ

9

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

+2

Правильный ответ: вы не можете изменить элемент во время он устанавливает (с вызовом onBindViewHolder). В этом случае вам нужно вызвать notifyItemInserted в конце текущего цикла, вызвав Handler.post() – pjanecze

40
 Handler handler = new Handler(); 

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

     handler.post(r); 

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

+1

Omg, который мне очень помог. Но нужен ли этот процесс большой емкости или он неэффективен? – XxGoliathusxX

+0

Я не так много знаю. –

+0

это работает, спасибо. Я не должен был обновляться до новой библиотеки поддержки. –

20

Стоит также отметить 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; 
} 

Характерная фраза существо:

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

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

6

Аккуратные и легко путь:

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

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

1

Попробуйте

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

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

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