3

Im making an activity log and i have a problem where i can't scroll down an Inconsistency error keeps popping up. I have added the notifyDataSetChanged method as well but i still have no luck in fixing the error.

Here is the error that i am getting

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 7(offset:7).state:11 android.support.v7.widget.RecyclerView{edb17ac 

This is my Adapter code

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

private List<ListItem> listItems;
private Context context;

public MyAdapter(List<ListItem> listItems, Context context) {
    this.listItems = listItems;
    this.context = context;
}

public MyAdapter(Context applicationContext, List<ListItem> listItems) {
}

@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    View v = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.list_item, parent, false);
    return new ViewHolder(v);
}

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

    for(int i=0;i<listItems.size(); i++){
        if (listItems.get(position).getHead().equals("Time")){
            listItems.remove(listItems.get(position));
        }
    }
    ListItem listItem = listItems.get(position);
    String key = listItem.getHead();
    if (key.equals("LockTime")) {
        holder.textViewHead.setText(listItem.getTime());
        holder.textViewDesc.setText(listItem.getDesc());
        holder.layout.setBackground(ContextCompat.getDrawable(context, R.drawable.red_gradient));
        holder.image1.setImageResource(R.drawable.lockwhite);
        holder.image2.setImageResource(R.drawable.lockwhite);
    } else if (key.equals("UnlockTime")) {
        holder.textViewHead.setText(listItem.getTime());
        holder.textViewDesc.setText(listItem.getDesc());
        holder.layout.setBackground(ContextCompat.getDrawable(context, R.drawable.green_gradient));
        holder.image1.setImageResource(R.drawable.unlockwhite);
        holder.image2.setImageResource(R.drawable.unlockwhite);
    }
}

@Override
public int getItemCount() {
    return listItems.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{

    public TextView textViewHead;
    public TextView textViewDesc;
    public RelativeLayout layout;
    public ImageView image1;
    public ImageView image2;


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

        textViewHead = (TextView) itemView.findViewById(R.id.textViewHead);
        textViewDesc = (TextView) itemView.findViewById(R.id.textViewDesc);
        layout= (RelativeLayout) itemView.findViewById(R.id.relativelayout);
        image1 = itemView.findViewById(R.id.padlock1);
        image2 = itemView.findViewById(R.id.padlock2);
    }
  }



}

This is my Activity

public class ActivityLog extends AppCompatActivity {

private RecyclerView recyclerView;
private RecyclerView.Adapter adapter;

private List<ListItem> listItems;

SwipeRefreshLayout swipe;

DatabaseReference database;
LinearLayout lLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    recyclerView = (RecyclerView) findViewById(R.id.recycle);
    recyclerView.setHasFixedSize(true);
    // SharedPreferences preferences=getSharedPreferences(LOCK_PREFS,MODE_PRIVATE);
    database = FirebaseDatabase.getInstance().getReference("Activity Log/device321");
    swipe = findViewById(R.id.swiper);
    listItems = new ArrayList<>();

    database.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            for (DataSnapshot usersnapshot : dataSnapshot.getChildren()) {
                Map<String, String> map = (Map<String, String>) usersnapshot.getValue();
                for (Map.Entry<String, String> entry : map.entrySet()) {
                    listItems.add(new ListItem(entry.getKey(), entry.getValue(),usersnapshot.child("Time").getValue().toString()
                    ));
                }
            }
            recyclerView.setAdapter(new MyAdapter(getApplicationContext(),listItems));
            adapter.notifyDataSetChanged();
            adapter = new MyAdapter(listItems, ActivityLog.this);
            recyclerView.setAdapter(adapter);

        }


        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    });

    swipe.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
            database.addValueEventListener(new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {
                    for (DataSnapshot usersnapshot : dataSnapshot.getChildren()) {
                        Map<String, String> map = (Map<String, String>) usersnapshot.getValue();
                        for (Map.Entry<String, String> entry : map.entrySet()) {
                            listItems.add(new ListItem(entry.getKey(), entry.getValue(),usersnapshot.child("Time").getValue().toString()
                            ));
                        }
                    }

                    recyclerView.setAdapter(new MyAdapter(getApplicationContext(),listItems));
                    adapter = new MyAdapter(listItems, ActivityLog.this);
                    recyclerView.setAdapter(adapter);
                }


                @Override
                public void onCancelled(DatabaseError databaseError) {

                }
            });
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    swipe.setRefreshing(false);
                }
            },2000);
            listItems.clear();
        }
    });
  }
}

Any help would be greatly appreciated thank you

HawkEye
  • 81
  • 4
  • In my opinion, your adapter has no sense. `onBindViewHolder()` method does calling every time, when your recyclerview item is binded. It mean all actions, when you add/remove/change item in adapter, and when you scroll view. So, there is not recommend to call a `for()` loop every time without any conditions, when your view is binded. – grabarz121 Jun 18 '18 at 07:27
  • There are a few values that i wanted to be removed from the List, hence i put it in a for loop and removed the items which had the head called **Time** – HawkEye Jun 18 '18 at 07:32
  • But why in adapter? If you want to make it automatically, let do it in your activity, before you will pass data to adapter. – grabarz121 Jun 18 '18 at 07:34
  • A solution was added, it was said to put the for loop in the constructor instead of the onBindViewHolder. Thanks a bunch for your feedback :) – HawkEye Jun 18 '18 at 07:43

3 Answers3

0

I hope this will work for you.

You set Adapter two times. remove below lines.

recyclerView.setAdapter(new MyAdapter(getApplicationContext(),listItems));
adapter.notifyDataSetChanged();

While using swipyrefreshlayout when refreshing disable recyclerview scroll. scrolling and updating data at a time cause inconsistency error.

GParekar
  • 1,209
  • 1
  • 8
  • 15
0

You need notifyDataSetChanged() only when there have been multiple additions, deletions or rearrangments in your adapter. When you call setAdapter there is no point to call notifyDataSetChanged().

0

You shouldn't remove items from list when you are binding viewholder because you change positions and RecyclerView needs to be recreated. Remove them in your constructor or in any place than your adapter.

public MyAdapter(List<ListItem> listItems, Context context) {
    for(int i=0;i<listItems.size(); i++){
        if (listItems.get(position).getHead().equals("Time")){
            listItems.remove(listItems.get(position));
        }
     }
     this.listItems = listItems;
     this.context = context;
}
Cătălin Florescu
  • 5,012
  • 1
  • 25
  • 36
  • It worked, i now understand why it is a bad practice to iterate through the onBindViewHolder. Thanks a lot for your help! :)) – HawkEye Jun 18 '18 at 07:42