I have seen this question many times in StackOverflow, but I couldn't get the issue fixed. So reaching out for help.
Please note: I have already checked other questions on SO and though I could get rid of the error, there are other issues with it.
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 get 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 on various StackOverflow answers, I modified it like below:
modified code looking at various 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, everytime and the views in recyclerview keep refreshing everytime. What is it I am doing wrong? Please see the 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
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.
The warning itself is an explaination. In other words, you are updating a view while recyclerview is calculating layout measurements.
What went wrong? The implementation below -
mRecyclerViewAdapter.notifyItemChanged(listItems.size() - 1);
mRecyclerViewAdapter.notifyDataSetChanged();
What you can do is post this UI operation to the next UI frame. You can do this by calling View.post()
method.
recyclerView.post(new Runnable() {
public void run() {
// There is no need to use notifyDataSetChanged()
mRecyclerViewAdapter.notifyItemInserted(allArticles.size() - 1);
}
});
I use the following variant to update recyclerViewAdapter
:
java:
recyclerView.post(new Runnable() {
public void run() {
mRecyclerViewAdapter.notifyDataSetChanged();
}
});
kotlin:
recyclerView.post {
mRecyclerViewAdapter.notifyDataSetChanged()
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With