Jason Jason - 5 months ago 55
Java Question

Recyclerview to busy and crashes

I often get the error "Cannot call this method while RecyclerView is computing a layout or scrolling". I think this is caused by having 5 fragments creating an recyclerview at the same time. I could fix it by setting the viewPager.setOffscreenPageLimit to 2, but that is not quitte the solution as I'm using threads and can't afford to get an tab out of focus.

I was think about adding fragments, with a slight time delay, but failed to do so. Anyone knows how to fix it?

Solution, but not optimal for me due to threads:

viewPager.setOffscreenPageLimit(2);


My onload and public class:

Public class MainActivity {
static MainActivity main;
Toolbar toolbar;
TabLayout tabLayout;
ViewPager viewPager;
ViewPagerAdapter viewPagerAdapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.toolBar);
setSupportActionBar(toolbar);
tabLayout = (TabLayout) findViewById(R.id.tabLayout);
viewPager = (ViewPager) findViewById(R.id.viewPager);
viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
viewPagerAdapter.addFragments(new Maandag_home(), "MA");
viewPagerAdapter.addFragments(new Dinsdag_home(), "DI");
viewPagerAdapter.addFragments(new Woensdag_home(), "WO");
viewPagerAdapter.addFragments(new Donderdag_home(), "DO");
viewPagerAdapter.addFragments(new Vrijdag_home(), "VR");
viewPager.setAdapter(viewPagerAdapter);
tabLayout.setupWithViewPager(viewPager);
}





Viewpager adapter code:

package com.mycompany.myapp;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;

import java.util.ArrayList;

public class ViewPagerAdapter extends FragmentPagerAdapter {

ArrayList<Fragment> fragments = new ArrayList<>();
ArrayList<String> tabTitles = new ArrayList<>();

public void addFragments(Fragment fragments, String titles)
{
this.fragments.add(fragments);
this.tabTitles.add(titles);
}

public ViewPagerAdapter (FragmentManager fm)
{
super(fm);
}
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}

@Override
public int getCount() {
return fragments.size();
}

@Override
public CharSequence getPageTitle(int position) {
return tabTitles.get(position);
}
}





Recyclerview adapter:

package com.mycompany.myapp;
import android.graphics.Color;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.ArrayList;

public class MyRecyclerViewAdapter extends RecyclerView
.Adapter<MyRecyclerViewAdapter
.DataObjectHolder> {
private static String LOG_TAG = "MyRecyclerViewAdapter";
private ArrayList<DataObject> mDataset;
private static MyClickListener myClickListener;

public static class DataObjectHolder extends RecyclerView.ViewHolder
implements View
.OnClickListener {
TextView label;
TextView dateTime;
TextView tijd;
CardView cardView;

public DataObjectHolder(View itemView) {
super(itemView);
label = (TextView) itemView.findViewById(R.id.textView);
dateTime = (TextView) itemView.findViewById(R.id.textView2);
tijd = (TextView) itemView.findViewById(R.id.textView3);
cardView = (CardView) itemView.findViewById(R.id.card_view);
Log.i(LOG_TAG, "Adding Listener");
itemView.setOnClickListener(this);
}

@Override
public void onClick(View v) {
myClickListener.onItemClick(getAdapterPosition(), v);
}
}

public void setOnItemClickListener(MyClickListener myClickListener) {
this.myClickListener = myClickListener;
}

public MyRecyclerViewAdapter(ArrayList<DataObject> myDataset) {
mDataset = myDataset;
}

@Override
public DataObjectHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.card_view_row, parent, false);

DataObjectHolder dataObjectHolder = new DataObjectHolder(view);
return dataObjectHolder;
}

@Override
public void onBindViewHolder(DataObjectHolder holder, int position) {
holder.label.setText(mDataset.get(position).getmText1());
holder.dateTime.setText(mDataset.get(position).getmText2());
holder.tijd.setText(mDataset.get(position).getmText3());
String color = mDataset.get(position).getmState();
if (color == "FFFFFF") {}
else if (color == "F44336") {
holder.cardView.setCardBackgroundColor(0xFFF44336);
holder.label.setTextColor(0xFFFFFFFF);
holder.dateTime.setTextColor(0xFFFFFFFF);
}
else if (color == "4CAF50"){
holder.cardView.setCardBackgroundColor(0xFF4CAF50);
holder.label.setTextColor(0xFFFFFFFF);
holder.dateTime.setTextColor(0xFFFFFFFF);
}
else if (color == "FFEB3B") {
holder.cardView.setCardBackgroundColor(0xFFFFEB3B);
}
else if (color == "607D8B") {
holder.cardView.setCardBackgroundColor(0xFF607D8B);
}
}

public void addItem(DataObject dataObj, int index) {
mDataset.add(index, dataObj);
notifyItemInserted(index);
}

public void deleteItem(int index) {
mDataset.remove(index);
notifyItemRemoved(index);
}

@Override
public int getItemCount() {
return mDataset.size();
}

public interface MyClickListener {
public void onItemClick(int position, View v);
}
}





How I add items (cards) to the recyclerview programatically:

DataObject obj = new DataObject("My Subject", "My Teacher", "Time", "FFFFFF");
((MyRecyclerViewAdapter) mAdapter).addItem(obj, index);





Error when running with
viewPager.setOffscreenPageLimit(4)



FATAL EXCEPTION: Thread-7997
Process: com.mycompany.myapp, PID: 21990
java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling
at android.support.v7.widget.RecyclerView.assertNotInLayoutOrScroll(RecyclerView.java:2186)
at android.support.v7.widget.RecyclerView$RecyclerViewDataObserver.onItemRangeInserted(RecyclerView.java:4281)
at android.support.v7.widget.RecyclerView$AdapterDataObservable.notifyItemRangeInserted(RecyclerView.java:9969)
at android.support.v7.widget.RecyclerView$Adapter.notifyItemInserted(RecyclerView.java:5856)
at com.mycompany.myapp.MyRecyclerViewAdapter.addItem(MyRecyclerViewAdapter.java:89)
at com.mycompany.myapp.Maandag_home$2.run(Maandag_home.java:174)


I think I've given enough information, if not please comment instead of immediately downvoting. I searched through the internet quitte a lot, even asked a Java developer, but he and the internet just didn't know.



PS: I'm wondering if the title is wrong, please correct it if it is

Answer

1/ Change this:

public MyRecyclerViewAdapter(ArrayList<DataObject> myDataset) {
    mDataset = myDataset;
}

to this:

// get Activity from the fragment with getActivity() and pass to constructor

public MyRecyclerViewAdapter(Activity activity, ArrayList<DataObject> myDataset) {
     mDataset = myDataset;
     mActivity = activity;
}

2/ Change this:

public void addItem(DataObject dataObj, int index) {
    mDataset.add(index, dataObj);
    notifyItemInserted(index);
}

to this:

public void addItem(DataObject dataObj, final int index) { 
    if (mActivity == null){ 
    return; 
} 

mActivity.runOnUiThread(new Runnable() { 
    @Override public void run() { 
        mDataset.add(index, dataObj); 
       notifyItemInserted(index); 
    } 
}); 
}

// comment: The reason of the crash is you update the data set from a background thread. You could even change to an AsyncTask to archive even a prettier implementation.