1

In my app I am showing a list of items in a recyclerview. I have create two funtionality edit and delete for each of the item in recyclerview. With this app user can create a list of task , can edit the item, also delete the item from the recyclerview. Now if I click edit, it is working perfectly. But if I click delete option the app crashes with the message array index out of rance exception in the adaper class.

Here is my code for adapter. I have also mentioned the line in code , where I am getting app crashes.

Edited Adapter Class

public class TodoAdapter extends RealmRecyclerViewAdapter<TodoModel, TodoAdapter.TaskHolder> {
    public final static String INTENT_KEY_ID = "taskId";
    public final static String INTENT_KEY_POSITION = "position";
    public final static String DATE_FORMAT = "dd/MMM/yy";


    private Realm realm;

    public interface TaskListener {

    }

    private final TaskListener taskListener;
    private final Context context;

    public TodoAdapter(TaskListener taskListener, RealmResults<TodoModel> realmResults, Context context, Realm realm) {
        super(realmResults, true);
        this.taskListener = taskListener;
        this.context = context;
        this.realm = realm;
    }

    @Override
    public TaskHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new TaskHolder( LayoutInflater.from(parent.getContext()).inflate(R.layout.todo_row, parent, false));
    }

    @Override
    public void onBindViewHolder(TaskHolder holder, final int position) {

        final TodoModel task = getData().get(position);
        holder.taskTextView.setText(task.getName());
        holder.doneCheckBox.setChecked(task.isDone());
        holder.timeTextView.setText( task.getTime() );

        final SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);

        final Date date = (task.getDate());
        if (sdf.format(date).equals(sdf.format(getDate(0))))
            holder.dateTextView.setText("Today");
        else if (sdf.format(date).equals(sdf.format(getDate(-1))))
            holder.dateTextView.setText("Yesterday");
        else if (sdf.format(date).equals(sdf.format(getDate(1))))
            holder.dateTextView.setText("Tomorrow");
        else if (date.getTime() < getDate(6).getTime()) {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(date);
            holder.dateTextView.setText( DateFormat.format("EEEE", calendar.getTime()).toString());
        } else
            holder.dateTextView.setText(sdf.format(task.getDate()));

        holder.cardView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(context, TodoAddActivity.class);
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                intent.putExtra(INTENT_KEY_ID, getTask(position).getId());
                context.startActivity(intent);
            }
        });

        holder.doneCheckBox.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                TodoModel task = new TodoModel();
                task.setId(getTask(position).getId());
                task.setDate(getTask(position).getDate());
                task.setName(getTask(position).getName());
                task.setDone(((CheckBox) v).isChecked());
                updateTask(position, task);
            }
        });

        holder.deleteImageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (getTask(position).isDone()) {
                    removeTask(position);
                    notifyDataSetChanged();
                    return;
                }
                AlertDialog.Builder alertDialog = new AlertDialog.Builder(context);
                alertDialog.setTitle("Confirm Delete?");
                alertDialog.setMessage("Do you want to delete the task you created?");
                alertDialog.setPositiveButton("YES", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                        removeTask(position);
                    }
                });
                alertDialog.setNegativeButton("NO", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                });
                alertDialog.show();
            }
        });
    }

    public class TaskHolder extends RecyclerView.ViewHolder {
        public CardView cardView;
        public TextView taskTextView;
        public TextView dateTextView;
        public TextView timeTextView;
        public ImageView deleteImageView;
        public CheckBox doneCheckBox;


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

            taskTextView = (TextView) itemView.findViewById( R.id.row_task_list_tv_name);
            dateTextView = (TextView) itemView.findViewById(R.id.row_task_list_tv_date);
            timeTextView=(TextView)itemView.findViewById( R.id.row_task_list_tv_time );
            deleteImageView = (ImageView) itemView.findViewById(R.id.row_task_list_iv_delete);
            doneCheckBox = (CheckBox) itemView.findViewById(R.id.row_task_list_cb_done);
            cardView = (CardView) itemView.findViewById(R.id.cardView);
        }
    }

    private Date getDate(int day) {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE, day);
        return calendar.getTime();
    }

    protected TodoModel getTask(int position) {
        return getData().get(position);
    }

    protected void updateTask(int position, TodoModel task) {
        realm.beginTransaction();
        TodoModel newTask = realm.where(TodoModel.class).equalTo("id", task.getId()).findFirst();
        newTask.setDate(task.getDate());
        newTask.setDone(task.isDone());
        newTask.setName(task.getName());
        realm.commitTransaction();
    }

    protected void removeTask(int position) {
        TodoModel newTask = realm.where(TodoModel.class).equalTo("id", getTask(position).getId()).findFirst();
        realm.beginTransaction();
        newTask.deleteFromRealm();
        realm.commitTransaction();
        notifyItemRemoved(position);
    }
   }

The Crash report

09-10 12:25:57.876 7332-7332/realmtest.com.to_do_list_test E/AndroidRuntime: FATAL EXCEPTION: main
                                                                         Process: realmtest.com.to_do_list_test, PID: 7332
                                                                         java.lang.ArrayIndexOutOfBoundsException: Out of range  in /Users/cm/Realm/realm-java/realm/realm-library/src/main/cpp/io_realm_internal_Collection.cpp line 133(requested: 1 valid: 1)
                                                                             at io.realm.internal.Collection.nativeGetRow(Native Method)
                                                                             at io.realm.internal.Collection.getUncheckedRow(Collection.java:386)
                                                                             at io.realm.OrderedRealmCollectionImpl.get(OrderedRealmCollectionImpl.java:106)
                                                                             at io.realm.RealmResults.get(RealmResults.java:53)
                                                                             at io.realm.OrderedRealmCollectionImpl.get(OrderedRealmCollectionImpl.java:19)
                                                                             at realmtest.com.to_do_list_test.activity.TodoAdapter.getTask(TodoAdapter.java:157)
                                                                             at realmtest.com.to_do_list_test.activity.TodoAdapter$3.onClick(TodoAdapter.java:106)
                                                                             at android.view.View.performClick(View.java:5610)
                                                                             at android.view.View$PerformClick.run(View.java:22265)
                                                                             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:6077)
                                                                             at java.lang.reflect.Method.invoke(Native Method)
                                                                             at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
                                                                             at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)

implementation of getData()

@Nullable
public OrderedRealmCollection<T> getData() {
    return adapterData;
}

TaskActivity Class

    public class TaskActivity extends AppCompatActivity implements TodoAdapter.TaskListener{

    private static final int ADD_TASK_REQUEST_CODE = 1000;
    private static final int EDIT_TASK_REQUEST_CODE = 1001;

    private Realm realm;

    private RecyclerView recyclerView;
    private TodoAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.fragment_todo_layout);

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        toolbar.setTitle("ToDo List");
        setSupportActionBar(toolbar);

        realm = Realm.getDefaultInstance();
        recyclerView =(RecyclerView)findViewById(R.id.activity_tasks_ll_task);

        setUpRecycler();

        // Variables

        // Views
        FloatingActionButton addFloatingActionButton = (FloatingActionButton) findViewById(R.id.activity_tasks_fab_add);
        //Listeners
        addFloatingActionButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivityForResult(new Intent(getApplicationContext(), TodoAddActivity.class), ADD_TASK_REQUEST_CODE);
            }
        });
    }

    private void setAdapter() {
        adapter = new TodoAdapter(this, realm.where(TodoModel.class).findAll(),this,realm);
        recyclerView.setAdapter(adapter);
        adapter.notifyDataSetChanged();

    }

    private void setUpRecycler() {
        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        recyclerView.setHasFixedSize(true);
        // use a linear layout manager since the cards are vertically scrollable
        final LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(layoutManager);
        setAdapter();
    }
}

image1

image2

tamrezh21
  • 125
  • 4
  • 14
  • move all `onClickListener` in view holder class and use `getAdapterPosition()` for index. – Murli Prajapati Sep 10 '17 at 12:47
  • check size of `adapterData` variable. it looks like you have only one element in database. – Murli Prajapati Sep 10 '17 at 13:17
  • @MurliPrajapati, The problem is this exception does not occur all the time. only after deleting some items thsi occur. I am nor getting actually what would be the reason for it. yes I ahve only 1 element. but how can modify my code, to catch this exeption, beacuse sometimes I may have only 1 item – tamrezh21 Sep 10 '17 at 13:26
  • use realmResult variable for populating,updating, deleting recycler view items. do not use getData() method for fetching data from database. – Murli Prajapati Sep 10 '17 at 13:46
  • @MurliPrajapati how can I edit my code here, to implement Realm reasult – tamrezh21 Sep 10 '17 at 13:53
  • replace `getData().get(position)` with `realmResult.get(position)` . change accordingly in your code – Murli Prajapati Sep 10 '17 at 14:01
  • what is realm Result here? – tamrezh21 Sep 10 '17 at 14:03
  • look at your constructor of adapter – Murli Prajapati Sep 10 '17 at 14:06
  • @tamrezh21 what is the `position` value and what is the `getData()` returning when you click the delete button? – joao86 Sep 10 '17 at 14:07
  • @joao86 I have faced the problem again, The problem I identified, but could not gett the solution of it. First I create list of 4 items, task1, task2, task3, task4. then I delete task 2, after deleting task 2, task 3 and task 4 , both are updating to task 4, and if I want to delete any one of those item the app is crashes. but if I delete task 1, it was ok. is there any problem in updating the list in recyclerview? please help me to solve this problem. I have given the image in editing question – tamrezh21 Sep 10 '17 at 15:00

3 Answers3

0

You are changing the list of the adapter after removing the entry but you are not notifying the adapter that you have changed the size of the list. So, you have to tell the adapter that the size of the list has been changed.

holder.deleteImageView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (getTask(position).isDone()) { //Error for this line 
            removeTask(position);
            notifyDataSetChanged(); <-------------------Add this line to your code.
            return;
        }
        AlertDialog.Builder alertDialog = new AlertDialog.Builder(context);
        alertDialog.setTitle("Confirm Delete?");
        alertDialog.setMessage("Do you want to delete the task you created?");
        alertDialog.setPositiveButton("YES", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                dialog.cancel();
                removeTask(position);
            }
        });
        alertDialog.setNegativeButton("NO", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                dialog.cancel();
            }
        });
        alertDialog.show();
    }
});

Whenever you change the size of the list of the adapter, you have to tell the adapter explicitly to the adapter about the change by calling the function notifyDataSetChanged().

  • I still got the error for this line return getData().get(position); – tamrezh21 Sep 10 '17 at 13:03
  • I have faced the problem again, The problem I identified, but could not gett the solution of it. First I create list of 4 items, task1, task2, task3, task4. then I delete task 2, after deleting task 2, task 3 and task 4 , both are updating to task 4, and if I want to delete any one of those item the app is crashes. but if I delete task 1, it was ok. is there any problem in updating the list in recyclerview? please help me to solve this problem. I have given the image in editing question – tamrezh21 Sep 10 '17 at 15:04
  • Try  notifyItemRangeChanged(position, getItemCount()); after **notifyItemRemoved(position);** And use only one, either notifyDataSetChanged() or notifyItemRemoved() Check out this thread - https://stackoverflow.com/questions/28189371/using-notifyitemremoved-or-notifydatasetchanged-with-recyclerview-in-android – Chhatrasal Singh Bundela Sep 10 '17 at 15:25
0

Remove the return statement from if (getTask(position).isDone()) { and put the AlertDialog in the else-clause of that if, as seen below.

holder.deleteImageView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (getTask(position).isDone()) {
                removeTask(position);
                notifyDataSetChanged();

            }else {
                AlertDialog.Builder alertDialog = new AlertDialog.Builder(context);
                alertDialog.setTitle("Confirm Delete?");
                alertDialog.setMessage("Do you want to delete the task you created?");
                alertDialog.setPositiveButton("YES", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                       dialog.cancel();
                       removeTask(position);
                    }
                });
                alertDialog.setNegativeButton("NO", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                       dialog.cancel();
                    }
                });
                alertDialog.show();
            }
        }
});
joao86
  • 2,056
  • 1
  • 21
  • 23
0

notifyDataSetChanged() can be used

But if you want to show animation as well as update Item Postions in list

Use this code in sequest

notifyItemRemoved(position)

MH: for remove item and show animation

notifyItemRangeChanged(position, ListSize);
aMJay
  • 2,215
  • 6
  • 22
  • 34
Muhammad Hassaan
  • 874
  • 6
  • 18