Recyclerview - cannot call this method on scroll callback

I've seen this question many times on StackOverflow, but I couldn't seem to fix the problem. So get help. Please note, I've already checked other questions on SO, and while I could get rid of the error, there are other issues as well.

Here is my existing code:

My adapter class:

  public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

        Context context;

        private final int VIEW_TYPE_ITEM = 0;
        private final int VIEW_TYPE_LOADING = 1;
        private IOnLoadMoreListener mIOnLoadMoreListener;

        private int visibleThreshold = 3;
        private int lastVisibleItem, totalItemCount;
        private int[] lastVisibleItems = new int[mStaggeredLayoutManager.getSpanCount()];

        public RecyclerViewAdapter(Context context) {
            this.context = context;

            final StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) recyclerView.getLayoutManager();

            recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                    super.onScrolled(recyclerView, dx, dy);

                    totalItemCount = staggeredGridLayoutManager.getItemCount();
                    lastVisibleItems = staggeredGridLayoutManager.findLastVisibleItemPositions(new int[mStaggeredLayoutManager.getSpanCount()]);

                    if (staggeredGridLayoutManager.getSpanCount() == 1) {
                        lastVisibleItem = lastVisibleItems[0];
                    } else if (staggeredGridLayoutManager.getSpanCount() == 2) {
                        lastVisibleItem = Math.max(lastVisibleItems[0], lastVisibleItems[1]);
                    } else if (staggeredGridLayoutManager.getSpanCount() == 3) {
                        lastVisibleItem = Math.max(Math.max(lastVisibleItems[0], lastVisibleItems[1]), lastVisibleItems[2]);
                    }

                    if (!isRefreshing && totalItemCount <= lastVisibleItem + visibleThreshold) {

                        if (mIOnLoadMoreListener != null) {
                            mIOnLoadMoreListener.onLoadMore();
                        }

                        isRefreshing = true;
                    }
                }
            });

        }

        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

            if (viewType == VIEW_TYPE_ITEM) {

                View v = LayoutInflater.from(parent.getContext())
                        .inflate(R.layout.item_recyclerview_shop_fragment, parent, false);
                return new CustomViewHolder(v);
            } else if (viewType == VIEW_TYPE_LOADING) {

                View v = LayoutInflater.from(parent.getContext())
                        .inflate(R.layout.item_recyclerview_loading_shop_fragment, parent, false);
                return new LoadingViewHolder(v);
            }

            return null;
        }

        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

            Product item = listItems.get(position);

            if (holder.getItemViewType() == VIEW_TYPE_ITEM) {

                CustomViewHolder customViewHolder = (CustomViewHolder) holder;
                Glide.with(context).load(item.getFeaturedSrc()).into(customViewHolder.imageView);

            } else if (holder.getItemViewType() == VIEW_TYPE_LOADING) {

                if (holder instanceof LoadingViewHolder) {
                    LoadingViewHolder loadingViewHolder = (LoadingViewHolder) holder;
                    loadingViewHolder.progressBar.setIndeterminate(true);
                }
            }


        }

        @Override
        public int getItemCount() {
            return listItems == null ? 0 : listItems.size();
        }

        @Override
        public int getItemViewType(int position) {

            if (listItems.get(position) == null) {
                return VIEW_TYPE_LOADING;
            } else {
                return VIEW_TYPE_ITEM;
            }
        }

        public void setOnLoadMoreListener(IOnLoadMoreListener mIOnLoadMoreListener) {
            this.mIOnLoadMoreListener = mIOnLoadMoreListener;
        }


        public class CustomViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

            ImageView imageView;
            TextView textView;

            String id;

            public CustomViewHolder(View itemView) {
                super(itemView);

                itemView.setOnClickListener(this);
                imageView = (ImageView) itemView.findViewById(R.id.imageView_thumbnail);

            }

            @Override
            public void onClick(View v) {

                int position = getAdapterPosition();

            }
        }

        public class LoadingViewHolder extends RecyclerView.ViewHolder {
            public ProgressBar progressBar;

            public LoadingViewHolder(View progressView) {
                super(progressView);
                progressBar = (ProgressBar) progressView.findViewById(R.id.progressBar);
            }
        }

    }

      

My previous code:

   mRecyclerViewAdapter.setOnLoadMoreListener(new IOnLoadMoreListener() {
        @Override
        public void onLoadMore() {

 listItems.add(null);

                    mRecyclerViewAdapter.notifyItemChanged(listItems.size() - 1);
                    mRecyclerViewAdapter.notifyDataSetChanged();

                    isProgressVisible = true;

                    getDataFromServer(false);

        }
    });

      

With my old code, I am getting the following IllegalStateException:

W/RecyclerView: Cannot call this method in a scroll callback. Scroll callbacks mightbe run during a measure & layout pass where you cannot change theRecyclerView data. Any method call that might change the structureof the RecyclerView or the adapter contents should be postponed tothe next frame.
                                                                   java.lang.IllegalStateException: 
                                                                       at android.support.v7.widget.RecyclerView.assertNotInLayoutOrScroll(RecyclerView.java:2581)
                                                                       at android.support.v7.widget.RecyclerView$RecyclerViewDataObserver.onItemRangeChanged(RecyclerView.java:4943)
                                                                       at android.support.v7.widget.RecyclerView$AdapterDataObservable.notifyItemRangeChanged(RecyclerView.java:11373)
                                                                       at android.support.v7.widget.RecyclerView$AdapterDataObservable.notifyItemRangeChanged(RecyclerView.java:11364)
                                                                       at android.support.v7.widget.RecyclerView$Adapter.notifyItemChanged(RecyclerView.java:6652)
                                                                       at codsiga.com.xx.ShopFragment$1.onLoadMore(ShopFragment.java:148)
                                                                       at codsiga.com.xx.ShopFragment$RecyclerViewAdapter$1.onScrolled(ShopFragment.java:229)
                                                                       at android.support.v7.widget.RecyclerView.dispatchOnScrolled(RecyclerView.java:4618)
                                                                       at android.support.v7.widget.RecyclerView.dispatchLayoutStep3(RecyclerView.java:3679)
                                                                       at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:3323)
                                                                       at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:3844)
                                                                       at android.view.View.layout(View.java:17637)
                                                                       at android.view.ViewGroup.layout(ViewGroup.java:5575)
                                                                       at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1741)
                                                                       at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1585)
                                                                       at android.widget.LinearLayout.onLayout(LinearLayout.java:1494)
                                                                       at android.view.View.layout(View.java:17637)
                                                                       at android.view.ViewGroup.layout(ViewGroup.java:5575)
                                                                       at android.support.v4.widget.SwipeRefreshLayout.onLayout(SwipeRefreshLayout.java:636)
                                                                       at android.view.View.layout(View.java:17637)
                                                                       at android.view.ViewGroup.layout(ViewGroup.java:5575)
                                                                       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
                                                                       at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
                                                                       at android.view.View.layout(View.java:17637)
                                                                       at android.view.ViewGroup.layout(ViewGroup.java:5575)
                                                                       at android.support.v4.view.ViewPager.onLayout(ViewPager.java:1795)
                                                                       at android.view.View.layout(View.java:17637)
                                                                       at android.view.ViewGroup.layout(ViewGroup.java:5575)
                                                                       at android.support.design.widget.HeaderScrollingViewBehavior.layoutChild(HeaderScrollingViewBehavior.java:131)
                                                                       at android.support.design.widget.ViewOffsetBehavior.onLayoutChild(ViewOffsetBehavior.java:42)
                                                                       at android.support.design.widget.AppBarLayout$ScrollingViewBehavior.onLayoutChild(AppBarLayout.java:1391)
                                                                       at android.support.design.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:870)
                                                                       at android.view.View.layout(View.java:17637)
                                                                       at android.view.ViewGroup.layout(ViewGroup.java:5575)
                                                                       at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1079)
                                                                       at android.view.View.layout(View.java:17637)
                                                                       at android.view.ViewGroup.layout(ViewGroup.java:5575)
                                                                       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
                                                                       at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
                                                                       at android.view.View.layout(View.java:17637)
                                                                       at android.view.ViewGroup.layout(ViewGroup.java:5575)
                                                                       at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1741)
                                                                       at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1585)
                                                                       at android.widget.LinearLayout.onLayout(LinearLayout.java:1494)
                                                                       at android.view.View.layout(View.java:17637)
                                                                       at android.view.ViewGroup.layout(ViewGroup.java:5575)
                                                                       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
                                                                       at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
                                                                       at android.view.View.layout(View.java:17637)
                                                                       at android.view.ViewGroup.layout(ViewGroup.java:5575)
                                                                       at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1741)
                                                                       at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1585)
                                                                       at android.widget.LinearLayout.onLayout(LinearLayout.java:1494)
03-22 11:36:32.083 3189-3189/codsiga.com.xx W/RecyclerView:     at android.view.View.layout(View.java:17637)
                                                                       at android.view.ViewGroup.layout(ViewGroup.java:5575)
                                                                       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
                                                                       at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
                                                                       at com.android.internal.policy.DecorView.onLayout(DecorView.java:726)
                                                                       at android.view.View.layout(View.java:17637)
                                                                       at android.view.ViewGroup.layout(ViewGroup.java:5575)
                                                                       at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2346)
                                                                       at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2068)
                                                                       at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1254)
                                                                       at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6337)
                                                                       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:874)
                                                                       at android.view.Choreographer.doCallbacks(Choreographer.java:686)
                                                                       at android.view.Choreographer.doFrame(Choreographer.java:621)
                                                                       at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:860)
                                                                       at android.os.Handler.handleCallback(Handler.java:751)
                                                                       at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                       at android.os.Looper.loop(Looper.java:154)
                                                                       at android.app.ActivityThread.main(ActivityThread.java:6119)
                                                                       at java.lang.reflect.Method.invoke(Native Method)
                                                                       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
                                                                       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)

      

So, as suggested in various StackOverflow answers, I modified it as shown below:

modified code looking at different answers:

mRecyclerViewAdapter.setOnLoadMoreListener(new IOnLoadMoreListener() {
            @Override
            public void onLoadMore() {

                recyclerView.post(new Runnable() {
                    @Override
                    public void run() {

                        listItems.add(null);

                        mRecyclerViewAdapter.notifyItemChanged(listItems.size() - 1);
                        mRecyclerViewAdapter.notifyDataSetChanged();

                        isProgressVisible = true;

                        getDataFromServer(false);


                    }
                });
            }
        });

      

Now I don't get the error, but the onLoadMore () is called twice, every time, and the views in the recyclerview are constantly updating. What am I doing wrong? See logcat output:

D/xx: Response => {…}
D/xx: Length = 9
D/xx: Query 'SELECT * FROM products' returned 9 rows

D/xx: Response => {…}
D/xx: Length = 9
D/xx: Query 'SELECT * FROM products' returned 18 rows

      

+10


source to share


1 answer


There is no way to call this method in a scroll callback. Scroll callbacks can be executed during the dimension and layout phase when you cannot change the RecyclerView data. Any method call that might change the structure of the RecyclerView or the contents of the adapter should be wrapped to the next frame.

The warning itself is an explanation. In other words, you are updating the view while the viewer calculates the dimensions of the layout .

Something went wrong? The implementation is below -



mRecyclerViewAdapter.notifyItemChanged(listItems.size() - 1);
mRecyclerViewAdapter.notifyDataSetChanged();

      

What you can do is publish this UI activity in the next UI frame. You can do this by calling View.post()

.

recyclerView.post(new Runnable() {
        public void run() {
            // There is no need to use notifyDataSetChanged()
            mRecyclerViewAdapter.notifyItemInserted(allArticles.size() - 1);
        }
    });

      

+21


source







All Articles