简体   繁体   中英

Getting ArrayIndexOutOfBoundsException in Adapter

I am displaying data in a recyclerview. I am using Firebase Firestore. Here is how I am setting up my Adapter in MainActivity:

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

    setUpRecyclerView();
}

private void setUpRecyclerView() {
    FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder().setPersistenceEnabled(true).build();
    db.setFirestoreSettings(settings);

    Query query = db.collection("users").document(firebase_user_uid).collection("notes");


    FirestoreRecyclerOptions<Note> response = new FirestoreRecyclerOptions.Builder<Note>().setQuery(query, Note.class).build();
    adapter = new NoteListAdapter(response, MainActivity.this);

    recyclerViewLayoutManager = new LinearLayoutManager(MainActivity.this, LinearLayoutManager.VERTICAL, false);
    recyclerView.setLayoutManager(recyclerViewLayoutManager);
    recyclerView.setItemAnimator(new DefaultItemAnimator());
    recyclerView.addItemDecoration(new CustomRecyclerViewDivider(this, LinearLayoutManager.VERTICAL, 16));

    recyclerView.setAdapter(adapter);

    ItemTouchHelper.SimpleCallback itemTouchHelperCallback = new RecyclerItemTouchHelper(0, ItemTouchHelper.LEFT, MainActivity.this);
    new ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(recyclerView);
}

And here is my Adapter:

public class NoteListAdapter extends FirestoreRecyclerAdapter<Note, NoteListAdapter.NoteViewHolder>{

private Context context;

public NoteListAdapter(@NonNull FirestoreRecyclerOptions<Note> options, @NonNull Context context) {
    super(options);
    this.context = context;
}

@Override
protected void onBindViewHolder(@NonNull NoteViewHolder holder, int position, @NonNull Note note) {
    holder.title.setText(note.getTitle());
    holder.content.setText(note.getContent());
}

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

public void removeItem(int position) {
    getSnapshots().getSnapshot(position).getReference().delete();
    notifyItemRemoved(position);
}

public class NoteViewHolder extends RecyclerView.ViewHolder {

    public TextView title, content;
    public ImageView bg_note_image;
    public RelativeLayout viewBackground, viewForeground;

    public NoteViewHolder(View view) {
        super(view);
        title = view.findViewById(R.id.tvTitle);
        content = view.findViewById(R.id.tvContent);
        bg_note_image = view.findViewById(R.id.note_image_view);
        viewBackground = view.findViewById(R.id.view_background);
        viewForeground = view.findViewById(R.id.view_foreground);

        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                DocumentSnapshot snapshot = getSnapshots().getSnapshot(getAdapterPosition());
                Note note = snapshot.toObject(Note.class);
                String id = getSnapshots().getSnapshot(getAdapterPosition()).getId();
                MainActivity.updateNote(id, note);
                return;
            }
        });
    }
}}  

So my App crashes and thats caused by this line:

DocumentSnapshot snapshot = getSnapshots().getSnapshot(getAdapterPosition());

Here's the Error:

09-06 21:09:11.976 19959-19959/com.test.test.test E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.test.test.test, PID: 19959
java.lang.ArrayIndexOutOfBoundsException: length=10; index=-1
    at java.util.ArrayList.get(ArrayList.java:413)
    at com.firebase.ui.common.BaseObservableSnapshotArray.getSnapshot(BaseObservableSnapshotArray.java:70)
    at com.raycroud.notes.notes.adapter.NoteListAdapter$NoteViewHolder$1.onClick(NoteListAdapter.java:97)
    at android.view.View.performClick(View.java:5685)
    at android.view.View$PerformClick.run(View.java:22481)
    at android.os.Handler.handleCallback(Handler.java:751)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:241)
    at android.app.ActivityThread.main(ActivityThread.java:6274)
    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)

In the documentation under getAdapterPosition() there is a Note:

Note that if you've called notifyDataSetChanged(), until the next layout pass, the return value of this method will be NO_POSITION.  

So now when I don't want to crash my App I must update my OnClick in ViewHolder like this:

@Override
            public void onClick(View view) {
                if (getAdapterPosition() == RecyclerView.NO_POSITION) {
                    return;
                }
                DocumentSnapshot snapshot = getSnapshots().getSnapshot(getAdapterPosition());
                Note note = snapshot.toObject(Note.class);
                String id = getSnapshots().getSnapshot(getAdapterPosition()).getId();
                MainActivity.updateNote(id, note);
                return;
            }  

This code works and the App doesnt crash anymore. But when the error normally apears I can't click the Items anymore. I think that's logical right when you look at the code, because than the dapter position returns -1 or NO_POSITION . So now is my Queston how to fix the whole Error? What I shuld do that the app won't crash and that I can click the Items?

I'm not sure why the adapter position is being invalidated (and that should be investigated later), but you can "fix" this problem by improving your code:

  1. Move NoteViewHolder into a separate class
  2. Add a bind(Note note, String id) method to NoteViewHolder and store those values as mutable fields
  3. Change the anonymous inner class to setOnClickListener(this) and implement the click listener in NoteViewHolder
  4. The click listener is shown below
  5. In onBindViewHolder , call bind with the note and id

NoteViewHolder :

public final class NoteViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    // ...
    private Note note;
    private String id;

    public NoteViewHolder(View itemView) {
        super(itemView);
        // ...

        itemView.setOnClickListener(this);
    }

    public void bind(Note note, String id) {
        this.note = note;
        this.id = id;

        title.setText(note.getTitle());
        content.setText(note.getContent());
    }

    @Override
    public void onClick(View v) {
        MainActivity activity = (MainActivity) itemView.getContext(); // You really shouldn't do this, but I don't know what you're building
        activity.updateNote(id, note);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM