Recientemente actualizo mi recyclerview-v7:23 a recyclerview-v7:24.2.0. Mi aplicación tiene una interminable lista de desplazamiento. El mensaje de error apunta a la línea notifyItemInserted cuando agrego la carga de la vista en RecyclerView (objeto nulo significa que la carga, id 0 está vacía, -1 es el final de la página) y funciona bien antes (recyclerview-v7:23), pero de repente me dieron error como este y de alguna manera mi carga mostrar hasta dos veces, a continuación, cuando se quita uno, no es una carga que todavía 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 me quite la parte de error (agregar carga) funciona bien de nuevo, no sé por qué, es la versión más reciente de recyclerview impedir la adición de datos en el adaptador demasiado rápido o no es función de devolución de llamada que se activa cuando «medir & layout» 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);
}
}

que el 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 tema provienen de una nueva versión de RecyclerView?
  2. Es malo para implementar notifyItemInserted dentro de desplazamiento de escucha? Funcionaba antes.
  3. ¿Cómo puedo solucionar este 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 la primera carga, después de la primera llamada y agregar la carga de ver de alguna manera el desplazamiento se activa y llamar de nuevo.

  • Encontraste la solución, si es así, puedes publicar?

4 Comentarios

  1. 98

    También puede publicar la utilización de la vista.

       recyclerView.post(new Runnable() {
    public void run() {
    articleAdapter.notifyItemInserted(allArticles.size() - 1);
    }
    });
    • Cuidado de la modificación de una recyclerView adaptador fuera del subproceso de interfaz de usuario… Que podrían conducir a un molesto java.lang.IndexOutOfBoundsException: la Inconsistencia detectada. Elemento no válido posición» si en alguna otra parte de que el adaptador está modificado.
    • Sólo un rápido mensaje a +1 @FlorianT advertencia, esta excepción se hapen.
  2. 34
    1. El problema no es con la nueva versión de Recyclerview.

    2 & 3. Usted no puede cambiar la pieza durante la configuración (con llamadas onBindViewHolder). En ese caso, usted tiene que llamar a notifyItemInserted al final del bucle actual llamando al Controlador.post()

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

    Espero, se va a resolver su problema.

    • recyclerView.addOnScrollListener(new EndlessRecyclerViewScrollListener(linearLayoutManager) { @Override public void onLoadMore(final int page, int totalItemsCount) { if (!isFirstCall) { loadArticles(page); } } }); pero es llamado dos veces cuando la actividad se ven, no es una manera de evitar que por mover isFirstCall = false; después de la primera solicitud terminado (éxito/error) entonces creo que el más reciente RecyclerView tiene diferentes onScroll comportamiento
    • Usted debe llamar a loadArticles(página) método cuando hay 4 elementos de la izquierda debajo de la pantalla. Seguir este artículo guides.codepath.com/android/…
    • sí, sigo el tutorial y funciona bien antes, Pero ahora el problema ha sido resuelto..
    • Ok eso es genial!!
    • ¿Cuál fue el problema exacto?
    • Como he dicho antes, la Recyclerview activa evento onScroll cuando el adaptador se ha establecido, por lo que hacen 2-3 solicitar a la red a la vez, y añadir 2-3 cargar la vista en lista así
    • Permítanos seguir esta discusión en el chat.
    • A quien pueda interesar, en algunos casos estaba «al azar» obtención de un IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder de la RecyclerView al parecer porque estaba extracción de datos (en mi caso) fuera del controlador. He tenido que mover esa llamada en el controlador también con el fin de dejar de obtener de ella.

  3. 3

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

    Tasks.call(new Callable<Void>() {
    @Override
    public Void call() throws Exception {
    allArticles.add(null);
    articleAdapter.notifyItemInserted(allArticles.size() - 1);
    return null;
    }
    });
    • Por qué usted necesita para utilizar Google Tareas de la API, si SDK de Android y java proporcionarle las herramientas necesarias, que parece más natural.
  4. 0

    En algunos casos, onScrollStateChanged puede ser suficiente

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

Dejar respuesta

Please enter your comment!
Please enter your name here