最近,我将recyclerview-v7:23升级为recyclerview-v7:24.2.0。我的应用程序有无穷无尽的滚动列表。当我将加载 View 添加到notifyItemInserted中时,错误消息指向RecyclerView行(空对象表示正在加载,id 0为空,-1为页面末尾),并且在此之前运行良好(recyclerview-v7:23),但突然我出现这样的错误,并且以某种方式我的负载显示两次,然后当它删除一个时,顶部仍然可见一个负载。

    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)

当我删除部分错误(添加加载)后,它又可以正常工作了,我不知道为什么,是新版本的recyclerview阻止太快地将数据添加到适配器中还是有回调函数在执行“度量和布局”时触发recyclerview完成,这是我的代码
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);
        }
    }

该代码是从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;
}

我的问题是:
  • 问题是否来自新版本的RecyclerView
  • 在滚动监听器中实现notifyItemInserted是错误的吗?它曾经工作过。
  • 如何解决此问题?

  • 更新了
    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)
    

    首次加载时,它们会被调用两次,第一次调用并添加加载 View 后,它们会以某种方式触发滚动并再次调用。

    最佳答案

    您也可以使用该 View 进行发布。

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

    关于android - 无法从RecyclerView.OnScrollListener调用notifyItemInserted(),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/39445330/

    10-12 06:11