1

I have created a RecyclerView to display items and everything shows and works fine except to one scenario: when I scroll fast to the bottom of the RecyclerView.

If I do so, I get the following error:

java.lang.ArrayIndexOutOfBoundsException: length=15; index=-1

It directs me to this part of my code (specifically to checkIfBorrowed.get( getAdapterPosition() )):

public class itemtestAdapter extends RecyclerView.Adapter<itemtestAdapter.testViewHolder> {

    private List<Discoveritems> items;
    private List<Boolean> checkIftested;

    interface OnItemCheckListener {
        void onItemCheck(Discoveritems items);
        void onItemUncheck(Discoveritems items);
    }

    @NonNull
    private OnItemCheckListener onItemCheckListener;

    public itemtestAdapter(List<Discoveritems> items, List<Boolean> checkIftested, @NonNull OnItemCheckListener onItemCheckListener) {
        this.items = items;
        this.onItemCheckListener = onItemCheckListener;
        this.checkIftested = checkIftested;
    }

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

    @Override
    public void onBindViewHolder(testViewHolder holder, int position) {
        holder.bind(items.get(position));

        final Discoveritems currentItem = items.get(position);

        holder.setOnClickListener( v -> {
            holder.Cb_test.setChecked(
                    !holder.Cb_test.isChecked());
            if (holder.Cb_test.isChecked()) {
                onItemCheckListener.onItemCheck(currentItem);
            } else {
                onItemCheckListener.onItemUncheck(currentItem);
            }
        } );

    }

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

    class testViewHolder extends RecyclerView.ViewHolder {
        ImageView Iv_test,itemPic;
        TextView itemName;
        CheckBox Cb_test;
        Discoveritems items;

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

            Iv_testitemPic = itemView.findViewById(R.id.iv_itemCover);
            Tv_testitemName = itemView.findViewById( R.id.tv_testeditemName);
            Cb_test = itemView.findViewById( R.id.cb_testitem );
            Cb_test.setClickable( false );

        }

        public void setOnClickListener(View.OnClickListener onClickListener) {
            itemView.setOnClickListener(onClickListener);
        }

        public void bind(Discoveritems items) {

            this.items = items;

            String itemID = items.getitemID();
            MyitemClient client = new MyitemClient();
            client.getitems(itemID, new JsonHttpResponseHandler(){
                @Override
                public void onSuccess(int statusCode, Header[] headers, JSONObject response){
                    if(response!=null){
                        final Myitem items = Myitem.fromJson(response);

                        if (checkIftested.get( getAdapterPosition() )){
                            itemView.setEnabled(false);
                            Tv_itemName.setText("my" + items.getTitle());
                        } else {
                            Tv_itemName.setText( items.getTitle() );
                        }
                    }
                }

            });
            
        }
    }

}

Thing is that inside bind I can't figure out how to get the position of the item without using getAdapterPosition and I also cant understand why it only happens when I scroll fast.

Also it is weird that it says length = 15 while checkIfBorowed's size is 12 only. Thank you

David Kroukamp
  • 36,155
  • 13
  • 81
  • 138
Ben
  • 1,737
  • 2
  • 30
  • 61
  • 1
    I assume client.getItems is asynchronous? I think that might be causing the issue. How often is bind called? More then once right? I think you should try not do any asynchronous work inside your bind function, thats probably the quickest solution. – David Kroukamp Jul 11 '20 at 19:49
  • Yes, it is asynchronous mate – Ben Jul 11 '20 at 20:01
  • Well then I think the easiest is to ensure when binding you have all the data you need to make the view item appear correctly instead of fetching more data asynchronously inside the bind method and on success trying to get the position in the adpater. That just will never work – David Kroukamp Jul 11 '20 at 20:06
  • Like to add timer or so? or to add onFinish in the async? – Ben Jul 11 '20 at 20:10
  • maybe post your whole adapter class it will be easier that way – David Kroukamp Jul 11 '20 at 20:18
  • 1
    What happens if you change ```holder.bind(items.get(position));``` to ```holder.bind(items.get(position), position);``` and then change viewholders ```bind``` to accept the new ```position``` argument i.e. ```bind(Discoveritems items, int position)``` and use ```position``` instead of ```getAdapterPosition()```? – David Kroukamp Jul 11 '20 at 20:46
  • 1
    Seems like it works mate, Thanks a lot! – Ben Jul 11 '20 at 21:13
  • Awesome added it as an answer :) – David Kroukamp Jul 11 '20 at 21:17

1 Answers1

1

Change holder.bind(items.get(position)); to holder.bind(items.get(position), position); and then change viewholders bind method to accept the new position argument i.e. bind(Discoveritems items, int position) and use position instead of getAdapterPosition().

The issue is you are not using the position the adapter gave your viewholder which can change when views are recycled and thus they dont macth. Please see here for more https://stackoverflow.com/a/38970513/1133011

David Kroukamp
  • 36,155
  • 13
  • 81
  • 138