Kevin Murvie
Kevin Murvie

Reputation: 2652

Android - getAdapterPosition() returns -1 after deleting an item

The title says it all, I have a RecyclerView which is used to store values dynamically, but only one item at a time. The item contains 3 TextViews

When I add all the items normally, it works, but when I add x item, then remove the last one, and then click on "submit", the app crashes.

I do removeAt(getAdapterPosition() in the viewholder where `removeAt(int) is :

private void removeAt(int removePosition){
    grosirList.product_grosir_list.remove(removePosition);
    notifyItemRemoved(removePosition);
}

And submit button :

promoConfirmBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Gson gson = new Gson();
                if (postProductPromoAdapter.getGrosirList() == null){

                } else {
                    String grosirAddJson = gson.toJson(postProductPromoAdapter.getGrosirList());

                    Intent intent = new Intent();
                    intent.putExtra("grosirPromoPrice", grosirAddJson);
                    intent.putExtra("promoPrice", promoPriceET.getText().toString());
                    setResult(RESULT_OK, intent);
                    finish();
                }
            }
        });

getGrosirList() is

    public GrosirAddList getGrosirList(){
        if (mAwesomeValidation.validate()){
            return this.grosirList;
        } else {
            return null;
        }
    }

Basically the submit button validates the TextViews and if it's validated, returns true.

This is where the error happens :

    minRangeET.addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {

                }

                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                }

                @Override
                public void afterTextChanged(Editable s) {
                    if(!s.toString().equals("")){
                        grosirList.product_grosir_list.get(getAdapterPosition()).grosir_min = String.valueOf(s);
                    } else {
                        grosirList.product_grosir_list.get(getAdapterPosition()).grosir_min = null; // Error happens here
                    }
                }
            });

I save user's input to an object inside the adapter to be retrieved later through TextWatcher

This is the error log :

java.lang.ArrayIndexOutOfBoundsException: length=12; index=-1
at java.util.ArrayList.get(ArrayList.java:310)
at xx.PostProductPromoAdapter$FiturPromoHolder$1.afterTextChanged(PostProductPromoAdapter.java:236)
at android.widget.TextView.sendAfterTextChanged(TextView.java:7563)
at android.widget.TextView.setText(TextView.java:3920)
at android.widget.TextView.setText(TextView.java:3769)
at android.widget.EditText.setText(EditText.java:84)
at android.widget.TextView.setText(TextView.java:3744)
at com.basgeekball.awesomevalidation.helper.SpanHelper.setColor(SpanHelper.java:22)
at com.basgeekball.awesomevalidation.validators.ColorationValidator$1.execute(ColorationValidator.java:34)
at com.basgeekball.awesomevalidation.validators.Validator.checkFields(Validator.java:76)
at com.basgeekball.awesomevalidation.validators.ColorationValidator.trigger(ColorationValidator.java:25)
at com.basgeekball.awesomevalidation.AwesomeValidation.validate(AwesomeValidation.java:81)
at xx.getGrosirList(PostProductPromoAdapter.java:76)
at xx$3.onClick(PostProductPromoActivityRecycler.java:164)

Update :

I use this to check whether the items are valid :

public boolean filledCheck(int itemNumber){
    return this.grosirList.product_grosir_list.size() > 0
            && this.grosirList.product_grosir_list.get(itemNumber - 1).grosir_price != null
            && this.grosirList.product_grosir_list.get(itemNumber - 1).grosir_max != null
            && this.grosirList.product_grosir_list.get(itemNumber - 1).grosir_min != null;
}

And this to add the values to the EditText. Values is taken from user input using TextWatcher

    if(grosirList.product_grosir_list.get(position).grosir_min == null) {
        ((FiturPromoHolder)holder).minRangeET.setText("");
    } else {
        ((FiturPromoHolder)holder).minRangeET.setText(grosirList.product_grosir_list.get(position).grosir_min);
    }

    if(grosirList.product_grosir_list.get(position).grosir_max == null) {
        ((FiturPromoHolder)holder).maxRangeET.setText("");
    } else {
        ((FiturPromoHolder)holder).maxRangeET.setText(grosirList.product_grosir_list.get(position).grosir_max);
    }

    if(grosirList.product_grosir_list.get(position).grosir_price == null) {
        ((FiturPromoHolder)holder).grossPriceET.setText("");
    } else {
        ((FiturPromoHolder)holder).grossPriceET.setText(grosirList.product_grosir_list.get(position).grosir_price);
    }

Update, here's the whole Adapter code :

public boolean uploadProductLoading = true;

    private Context context;
    private AwesomeValidation mAwesomeValidation;

    public GrosirAddList grosirList = new GrosirAddList();

    private SharedPreferencesList sharedPreferencesList;
    private SharedPreferenceUtilities sharedPreferenceUtilities;
    private Utilities utilities;
    private UtilityUriHelper utilityUriHelper;

    // Allows to remember the last item shown on screen
    private int lastPosition = -1;


    // User information
    private String userIdString;
    private String userAliasString;
    private String userEmailString;
    private String loginSharedPrefsString;

    private String userId;
    private String userAlias;

    public PostProductPromoAdapter(Context context) {

        this.sharedPreferencesList = new SharedPreferencesList();
        this.sharedPreferenceUtilities = new SharedPreferenceUtilities();
        this.context = context;
        this.userIdString = sharedPreferencesList.userIDString;
        this.userAliasString = sharedPreferencesList.userAliasString;
        this.loginSharedPrefsString = sharedPreferencesList.loginSharedPreference;
        this.utilities = new Utilities();
        this.utilityUriHelper = new UtilityUriHelper();

        this.userId = sharedPreferenceUtilities.getValue(context, loginSharedPrefsString, userIdString);
        this.userAlias = sharedPreferenceUtilities.getValue(context, loginSharedPrefsString, userAliasString);
        this.mAwesomeValidation = new AwesomeValidation(ValidationStyle.COLORATION);
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View productView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.activity_marketplace_upload_produk_fitur_promo_card, parent, false);
        return new FiturPromoHolder(productView);
    }

    @Override
    public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {

        // Animation, disabled for now
//        setAnimation(holder.itemView, position);

        /*((FiturPromoHolder)holder).grossPriceDeleteBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//                notifyItemRangeChanged(position, grosirList.product_grosir_list.size());
//                notifyDataSetChanged();
//                notifyItemRemoved(position);
//                notifyItemRangeChanged(position, grosirList.product_grosir_list.size());
            }
        });*/



        /*((FiturPromoHolder)holder).minRangeET.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
            }

            @Override
            public void afterTextChanged(Editable s) {
                if(!s.toString().equals("")){
                    if (grosirList.product_grosir_list.size() != 0){
                        grosirList.product_grosir_list.get(position).grosir_min = String.valueOf(s);
                    }
                } else {
                    if (grosirList.product_grosir_list.size() != 0){
                        grosirList.product_grosir_list.get(position).grosir_min = null;
                    }
                }
            }
        });

        ((FiturPromoHolder)holder).maxRangeET.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
            }

            @Override
            public void afterTextChanged(Editable s) {
                if (!s.toString().equals("")) {
                    if (grosirList.product_grosir_list.size() != 0){
                        grosirList.product_grosir_list.get(position).grosir_max = String.valueOf(s);
                    }
                } else {
                    if (grosirList.product_grosir_list.size() != 0){
                        grosirList.product_grosir_list.get(position).grosir_max = null;
                    }
                }
            }
        });

        ((FiturPromoHolder)holder).grossPriceET.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
            }

            @Override
            public void afterTextChanged(Editable s) {
                if (!s.toString().equals("")) {
                    if (grosirList.product_grosir_list.size() != 0){
                        grosirList.product_grosir_list.get(position).grosir_price = String.valueOf(s);
                    }
                } else {
                    if (grosirList.product_grosir_list.size() != 0){
                        grosirList.product_grosir_list.get(position).grosir_price = null;
                    }
                }
            }
        });*/

        int x = holder.getLayoutPosition();

        if(grosirList.product_grosir_list.get(x).grosir_min != null) {
            ((FiturPromoHolder)holder).minRangeET.setText(grosirList.product_grosir_list.get(x).grosir_min);
        } else {
            ((FiturPromoHolder)holder).minRangeET.setText(null);
        }

        if(grosirList.product_grosir_list.get(x).grosir_max != null) {
            ((FiturPromoHolder)holder).maxRangeET.setText(grosirList.product_grosir_list.get(x).grosir_max);
        } else {
            ((FiturPromoHolder)holder).maxRangeET.setText(null);
        }

        if(grosirList.product_grosir_list.get(x).grosir_price != null) {
            ((FiturPromoHolder)holder).grossPriceET.setText(grosirList.product_grosir_list.get(x).grosir_price);
        } else {
            ((FiturPromoHolder)holder).grossPriceET.setText(null);
        }
    }

    /**
     * Here is the key method to apply the animation
     */
    private void setAnimation(View viewToAnimate, int position)
    {
        // If the bound view wasn't previously displayed on screen, it's animated
        if (position > lastPosition)
        {
            viewToAnimate.clearAnimation();
//            Animation animation = AnimationUtils.loadAnimation(context, android.R.anim.slide_in_left);
            Animation animation = AnimationUtils.loadAnimation(context, R.anim.fade_in);
            viewToAnimate.startAnimation(animation);
            lastPosition = position;
        }
    }

    public boolean filledCheck(int itemNumber){
        return this.grosirList.product_grosir_list.size() > 0
                && this.grosirList.product_grosir_list.get(itemNumber - 1).grosir_price.trim().length() > 0
                && this.grosirList.product_grosir_list.get(itemNumber - 1).grosir_max.trim().length() > 0
                && this.grosirList.product_grosir_list.get(itemNumber - 1).grosir_min.trim().length() > 0;
    }

    private void removeAt(int removePosition){
        grosirList.product_grosir_list.remove(removePosition);
//        notifyItemRemoved(removePosition);
//        notifyItemRangeChanged(removePosition, grosirList.product_grosir_list.size());
//        notifyDataSetChanged();
        notifyItemRemoved(removePosition);
        notifyDataSetChanged();
//        notifyItemRangeRemoved(removePosition, grosirList.product_grosir_list.size());

//        notifyItemRangeChanged(removePosition, grosirList.product_grosir_list.size());
    }

    private void removeRange(int removePosition){
        int tempSize = grosirList.product_grosir_list.size();
        for (int i = removePosition; i < tempSize; i++){
            grosirList.product_grosir_list.remove(removePosition);
//            notifyItemRemoved(i);
        }
        notifyDataSetChanged();
//        notifyItemRangeRemoved(removePosition, grosirList.product_grosir_list.size());
//        notifyItemRangeChanged(0, this.grosirList.product_grosir_list.size());
    }

    private void clearAnimation(View viewToAnimate, int position)
    {
        // If the bound view wasn't previously displayed on screen, it's animated
        viewToAnimate.clearAnimation();
    }

    public void addGrosirList(GrosirAddList dataset){
        this.grosirList.product_grosir_list.addAll(dataset.product_grosir_list);
        notifyDataSetChanged();
//        notifyItemInserted(grosirList.product_grosir_list.size() - 1);
//        this.grosirList.add(grosirList.size(), dataset.product_grosir_list.get());
//        notifyItemInserted(grosirList.size() - 1);
    }

    public GrosirAddList getGrosirList(){
        if (mAwesomeValidation.validate()){
            return this.grosirList;
        } else {
            return null;
        }
//        return this.grosirList;
    }

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

    public class FiturPromoHolder extends RecyclerView.ViewHolder implements View.OnClickListener{

        Handler handler;
        RelativeLayout minRangeWrapper;
        OpenSansEditText minRangeET;
        RelativeLayout maxRangeWrapper;
        OpenSansEditText maxRangeET;
        RelativeLayout grossPriceWrapper;
        OpenSansEditText grossPriceET;
        OpenSansButton grossPriceDeleteBtn;

        public FiturPromoHolder(View promoView) {
            super(promoView);
            minRangeWrapper = (RelativeLayout)promoView.findViewById(R.id.fragment_marketplace_upload_produk_fitur_promo_card_minRangeWrapper);
            minRangeWrapper.setOnClickListener(this);
            minRangeET = (OpenSansEditText)promoView.findViewById(R.id.fragment_marketplace_upload_produk_fitur_promo_card_minRangeET);
            maxRangeWrapper = (RelativeLayout)promoView.findViewById(R.id.fragment_marketplace_upload_produk_fitur_promo_card_maxRangeWrapper);
            maxRangeWrapper.setOnClickListener(this);
            maxRangeET = (OpenSansEditText)promoView.findViewById(R.id.fragment_marketplace_upload_produk_fitur_promo_card_maxRangeET);
            grossPriceWrapper = (RelativeLayout)promoView.findViewById(R.id.fragment_marketplace_upload_produk_fitur_promo_card_grossPriceWrapper);
            grossPriceWrapper.setOnClickListener(this);
            grossPriceET = (OpenSansEditText)promoView.findViewById(R.id.fragment_marketplace_upload_produk_fitur_promo_card_grossPriceET);
            grossPriceDeleteBtn = (OpenSansButton)promoView.findViewById(R.id.fragment_marketplace_upload_produk_fitur_promo_card_grossPriceDeleteBtn);
            grossPriceDeleteBtn.setOnClickListener(this);
            this.setIsRecyclable(false);
            handler = new Handler();
            minRangeET.addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {

                }

                @Override
                public void onTextChanged(final CharSequence s, int start, int before, int count) {
                    handler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            grosirList.product_grosir_list.get(getLayoutPosition()).grosir_min = String.valueOf(s);
                        }
                    }, 200);
                }

                @Override
                public void afterTextChanged(Editable s) {
                }
            });

            maxRangeET.addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {

                }

                @Override
                public void onTextChanged(final CharSequence s, int start, int before, int count) {
                    handler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            grosirList.product_grosir_list.get(getLayoutPosition()).grosir_max = String.valueOf(s);
                        }
                    }, 200);
                }

                @Override
                public void afterTextChanged(Editable s) {
                }
            });

            grossPriceET.addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {

                }

                @Override
                public void onTextChanged(final CharSequence s, int start, int before, int count) {
                    handler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            grosirList.product_grosir_list.get(getLayoutPosition()).grosir_price = String.valueOf(s);
                        }
                    }, 200);
                }

                @Override
                public void afterTextChanged(Editable s) {
                }
            });

            grossPriceET.addTextChangedListener(new NumberTextWatcher(grossPriceET));

            mAwesomeValidation.addValidation(minRangeET, RegexTemplate.NOT_EMPTY, "Jumlah minimum tidak boleh kosong");
            mAwesomeValidation.addValidation(maxRangeET, RegexTemplate.NOT_EMPTY, "Jumlah maximum tidak boleh kosong");
            mAwesomeValidation.addValidation(grossPriceET, RegexTemplate.NOT_EMPTY, "Harga tidak boleh kosong");
        }

        @Override
        public void onClick(View v) {
            if (v.equals(grossPriceDeleteBtn)) {
                int removePosition = getAdapterPosition();
                try {
                    grosirList.product_grosir_list.remove(removePosition);
                    notifyItemRemoved(removePosition);
                    notifyDataSetChanged();
                    minRangeET.setError(null);
                    maxRangeET.setError(null);
                    grossPriceET.setError(null);
//
                    mAwesomeValidation.addValidation(minRangeET, RegexTemplate.NOT_EMPTY, "Jumlah minimum tidak boleh kosong");
                    mAwesomeValidation.addValidation(maxRangeET, RegexTemplate.NOT_EMPTY, "Jumlah maximum tidak boleh kosong");
                    mAwesomeValidation.addValidation(grossPriceET, RegexTemplate.NOT_EMPTY, "Harga tidak boleh kosong");
                }catch (ArrayIndexOutOfBoundsException e){e.printStackTrace();}
            } else if(v.equals(minRangeWrapper)) {
                minRangeET.requestFocusFromTouch();
            } else if(v.equals(maxRangeWrapper)) {
                maxRangeET.requestFocusFromTouch();
            } else if(v.equals(grossPriceWrapper)) {
                grossPriceET.requestFocusFromTouch();
            }
        }
    }

Hopefully the mistake isn't so stupid.....

Upvotes: 2

Views: 2587

Answers (2)

Meet Vora
Meet Vora

Reputation: 2818

May be it's due to the animation while we use notifyItemRemoved(). So during that animation delay, if we instantly call getAdapterPosition() method, then it'll return -1. I had the same issue while I repeatedly remove more than one items instantly, I got this exception.

Possible solution: You can just first check position first, like below code:

private void removeAt(int removePosition){
    if(removePosition == -1)    
        return;
    grosirList.product_grosir_list.remove(removePosition);
    notifyItemRemoved(removePosition);
}

This is a late answer, but Hope that helps! :)

Upvotes: 2

user4156995
user4156995

Reputation:

There might be several reasons for this. First of all, check your getItemCount() in your Adapter, make sure it returns the size of your stack. Second, instead of notifyItemRemoved() notify your adapter that the stack has changed with notifyDataSetChanged()

If above those not solve your issue please post your Adapter code, and every code that you work around with your stack. Problems should be rising from one of those points.

Best, Renç.

Upvotes: 0

Related Questions