studio recyclerview recycler ejemplo dependency card and android android-recyclerview recycler-adapter

android - ejemplo - recyclerview in fragment



No se puede invocar notifyItemInserted() desde RecyclerView.OnScrollListener (4)

Recientemente actualicé mi recyclerview-v7:23 a recyclerview-v7:24.2.0 . Mi aplicación tiene una lista de desplazamiento infinita. El mensaje de error apunta a la línea notifyItemInserted cuando agrego la vista de carga en RecyclerView (objeto nulo significa cargar, id 0 está vacío, -1 es el final de la página) y funciona bien antes (recyclerview-v7: 23) pero de repente Obtuve un error como este y, de alguna manera, mi carga aparece dos veces y luego, cuando quita una, hay una carga que todavía está visible en la parte superior.

W/RecyclerView: Cannot call this method in a scroll callback. Scroll callbacks might be run during a measure & layout pass where you cannot change the RecyclerView data. Any method call that might change the structure of the RecyclerView or the adapter contents should be postponed to the next frame.java.lang.IllegalStateException: at android.support.v7.widget.RecyclerView.assertNotInLayoutOrScroll(RecyclerView.java:2403) at android.support.v7.widget.RecyclerView$RecyclerViewDataObserver.onItemRangeInserted(RecyclerView.java:4631) at android.support.v7.widget.RecyclerView$AdapterDataObservable.notifyItemRangeInserted(RecyclerView.java:10469) at android.support.v7.widget.RecyclerView$Adapter.notifyItemInserted(RecyclerView.java:6211) at com.sketchproject.infogue.fragments.MessageFragment.loadMessages(MessageFragment.java:109) at com.sketchproject.infogue.fragments.MessageFragment.access$100(MessageFragment.java:42) at com.sketchproject.infogue.fragments.MessageFragment$1.onLoadMore(MessageFragment.java:87) at com.sketchproject.infogue.modules.EndlessRecyclerViewScrollListener.onScrolled(EndlessRecyclerViewScrollListener.java:74)

Cuando elimino la parte de error (agregar carga) funciona bien de nuevo, no sé por qué, la versión más reciente de recyclerview impide agregar datos en el adaptador demasiado rápido o hay una función de devolución de llamada que se activa cuando "medida y diseño" de recyclerview terminado, este es mi código

private void loadArticles(final int page) { if (!isEndOfPage && apiArticleUrl != null) { if (swipeRefreshLayout == null || !swipeRefreshLayout.isRefreshing()) { allArticles.add(null); articleAdapter.notifyItemInserted(allArticles.size() - 1); // error here } JsonObjectRequest articleRequest = new JsonObjectRequest(Request.Method.GET, apiArticleUrl, null, new Response.Listener<JSONObject>() { @Override public void onResponse(JSONObject response) { try { String status = response.getString("status"); JSONObject articles = response.getJSONObject("articles"); String nextUrl = articles.getString("next_page_url"); int currentPage = articles.getInt("current_page"); int lastPage = articles.getInt("last_page"); JSONArray data = articles.optJSONArray("data"); apiArticleUrl = nextUrl; if (status.equals(APIBuilder.REQUEST_SUCCESS)) { if (swipeRefreshLayout == null || !swipeRefreshLayout.isRefreshing()) { allArticles.remove(allArticles.size() - 1); articleAdapter.notifyItemRemoved(allArticles.size()); } else { swipeRefreshLayout.setRefreshing(false); int total = allArticles.size(); for (int i = 0; i < total; i++) { allArticles.remove(0); } articleAdapter.notifyItemRangeRemoved(0, total); } List<Article> moreArticles = new ArrayList<>(); if (data != null) { for (int i = 0; i < data.length(); i++) { JSONObject articleData = data.getJSONObject(i); Article article = new Article(); article.setId(articleData.getInt(Article.ID)); article.setSlug(articleData.getString(Article.SLUG)); article.setTitle(articleData.getString(Article.TITLE)); article.setFeatured(articleData.getString(Article.FEATURED_REF)); article.setCategoryId(articleData.getInt(Article.CATEGORY_ID)); article.setCategory(articleData.getString(Article.CATEGORY)); article.setSubcategoryId(articleData.getInt(Article.SUBCATEGORY_ID)); article.setSubcategory(articleData.getString(Article.SUBCATEGORY)); article.setContent(articleData.getString(Article.CONTENT)); article.setContentUpdate(articleData.getString(Article.CONTENT_UPDATE)); article.setPublishedAt(articleData.getString(Article.PUBLISHED_AT)); article.setView(articleData.getInt(Article.VIEW)); article.setRating(articleData.getInt(Article.RATING_TOTAL)); article.setStatus(articleData.getString(Article.STATUS)); moreArticles.add(article); } } int curSize = articleAdapter.getItemCount(); allArticles.addAll(moreArticles); if (allArticles.size() <= 0) { Log.i("INFOGUE/Article", "Empty on page " + page); isEndOfPage = true; Article emptyArticle = new Article(0, null, "Empty page"); allArticles.add(emptyArticle); } else if (currentPage >= lastPage) { Log.i("INFOGUE/Article", "End on page " + page); isEndOfPage = true; Article endArticle = new Article(-1, null, "End of page"); allArticles.add(endArticle); } articleAdapter.notifyItemRangeInserted(curSize, allArticles.size() - 1); } else { Log.i("INFOGUE/Article", "Error on page " + page); Helper.toastColor(getContext(), R.string.error_unknown, R.color.color_warning_transparent); isEndOfPage = true; Article failureArticle = new Article(); failureArticle.setId(-2); failureArticle.setTitle(getString(R.string.error_unknown)); allArticles.add(failureArticle); } } catch (JSONException e) { e.printStackTrace(); } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { error.printStackTrace(); if (swipeRefreshLayout != null && swipeRefreshLayout.isRefreshing()) { swipeRefreshLayout.setRefreshing(false); } // remove last loading allArticles.remove(allArticles.size() - 1); articleAdapter.notifyItemRemoved(allArticles.size()); String errorMessage = getString(R.string.error_unknown); NetworkResponse networkResponse = error.networkResponse; if (networkResponse == null) { if (error.getClass().equals(TimeoutError.class)) { errorMessage = getString(R.string.error_timeout); } else if (error.getClass().equals(NoConnectionError.class)) { errorMessage = getString(R.string.error_no_connection); } } else { if (networkResponse.statusCode == 404) { errorMessage = getString(R.string.error_not_found); } else if (networkResponse.statusCode == 500) { errorMessage = getString(R.string.error_server); } else if (networkResponse.statusCode == 503) { errorMessage = getString(R.string.error_maintenance); } } Helper.toastColor(getContext(), errorMessage, R.color.color_danger_transparent); // add error view holder isEndOfPage = true; Article errorArticle = new Article(); errorArticle.setId(-2); errorArticle.setTitle(errorMessage); allArticles.add(errorArticle); } } ); articleRequest.setTag("articles"); articleRequest.setRetryPolicy(new DefaultRetryPolicy( APIBuilder.TIMEOUT_SHORT, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT)); VolleySingleton.getInstance(getContext()).addToRequestQueue(articleRequest); } }

ese código fue llamado desde el método onCreate

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_article_list, container, false); // Set the adapter if (view instanceof RecyclerView) { Context context = view.getContext(); recyclerView = (RecyclerView) view; // determine column of list LinearLayoutManager linearLayoutManager; if (mColumnCount <= 1) { linearLayoutManager = new LinearLayoutManager(context); } else { linearLayoutManager = new GridLayoutManager(context, mColumnCount); } // if article list authored by logged user then prefer editable view holder if (mMyArticle) { articleAdapter = new ArticleRecyclerViewAdapter(allArticles, mArticleListListener, mArticleEditableListener); } else { articleAdapter = new ArticleRecyclerViewAdapter(allArticles, mArticleListListener, hasHeader); } // set the adapter and attach custom scroll listener that triggered onLoadMore() and onReachTop() recyclerView.setAdapter(articleAdapter); recyclerView.setLayoutManager(linearLayoutManager); recyclerView.addOnScrollListener(new EndlessRecyclerViewScrollListener(linearLayoutManager) { @Override public void onLoadMore(final int page, int totalItemsCount) { if (!isFirstCall) { loadArticles(page); } } @Override public void onReachTop(boolean isFirst) { // activate swipe function when list reach top only, find out where do fragment attached if (getActivity() instanceof ArticleActivity) { ((ArticleActivity) getActivity()).setSwipeEnable(isFirst); } else if (getActivity() instanceof ApplicationActivity) { ((ApplicationActivity) getActivity()).setSwipeEnable(isFirst); } } }); if (isFirstCall) { isFirstCall = false; loadArticles(0); } } return view; }

Mis preguntas son:

  1. ¿El problema proviene de la nueva versión de RecyclerView ?
  2. ¿Es incorrecto implementar notifyItemInserted dentro de la escucha de desplazamiento? Funcionó antes.
  3. ¿Como puedó resolver esté problema?

Actualizado

when I logged the code inside first call and scroll, 09-12 03:49:10.078 7046-7046/com.sketchproject.infogue I/Infogue/Contributor: Followers URL http://192.168.43.141:8000/api/contributor/support/followers?contributor_id=1 09-12 03:49:26.421 7046-7046/com.sketchproject.infogue I/Infogue/Contributor: Followers first call 09-12 03:49:26.421 7046-7046/com.sketchproject.infogue I/Infogue/Contributor: Followers URL http://192.168.43.141:8000/api/contributor/support/followers?contributor_id=1 09-12 03:49:26.617 7046-7046/com.sketchproject.infogue I/Infogue/Contributor: Followers second call (scroll) 09-12 03:49:26.618 7046-7046/com.sketchproject.infogue I/Infogue/Contributor: Followers URL http://192.168.43.141:8000/api/contributor/support/followers?contributor_id=1 09-12 03:49:27.365 7046-7046/com.sketchproject.infogue I/Infogue/Contributor: Followers second call (scroll)

Se llaman dos veces cuando se carga por primera vez, después de la primera llamada y se agrega la vista de carga. De alguna manera, el desplazamiento se desencadena y se vuelve a llamar.


  1. El problema no es con la nueva versión de Recyclerview.

2 y 3. No puede cambiar el elemento mientras está configurando (con la llamada aBindViewHolder). En ese caso, debe llamar a notifyItemInserted al final del ciclo actual llamando a Handler.post ()

Handler handler = new Handler(); final Runnable r = new Runnable() { public void run() { articleAdapter.notifyItemInserted(allArticles.size() - 1); } }; handler.post(r);

Espero, eso resolverá tu problema.


En algunos casos, onScrollStateChanged puede ser suficiente

override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) { super.onScrollStateChanged(recyclerView, newState) adapter.notifyDataSetChanged() }


También puede publicar usando la vista.

recyclerView.post(new Runnable() { public void run() { articleAdapter.notifyItemInserted(allArticles.size() - 1); } });


También puede usar la API de tareas del servicio de Google Play

Tasks.call(new Callable<Void>() { @Override public Void call() throws Exception { allArticles.add(null); articleAdapter.notifyItemInserted(allArticles.size() - 1); return null; } });