简体   繁体   中英

Why am I getting an IndexOutOfBoundsException error when calling an activity method from my dialog?

I have a Recycler View full of items, when you long press on a row, a remove buttons appears to allow the user to delete the row - I managed to get that working correctly but I then wanted to add a bit of safety to it by adding a dialog which asks the user whether or not they want to delete the row. However when I call the method in my dialog I get an IndexOutOfBoundsException as below:

2021-10-19 15:52:06.774 16238-16238/com.example.it_stock E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.it_stock, PID: 16238
    java.lang.IndexOutOfBoundsException: Index: 6, Size: 0
        at java.util.ArrayList.get(ArrayList.java:411)
        at com.example.it_stock.MainActivity.deleteItemRow(MainActivity.java:93)
        at com.example.it_stock.ConfirmDeleteDialog.lambda$yes$1$ConfirmDeleteDialog(ConfirmDeleteDialog.java:48)
        at com.example.it_stock.-$$Lambda$ConfirmDeleteDialog$VgpDlH3zTD1jVLwQl8Gp5RaCjYw.onClick(lambda)
        at android.view.View.performClick(View.java:5637)
        at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1119)
        at android.view.View$PerformClick.run(View.java:22433)
        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:6121)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)

I am using an interface to long click on the recycler view rows:

package com.example.it_stock;

public interface StockViewInterface {
    void onItemClick(int position);
    void onLongItemClick(int position);
}

This is in my Adapter:

// Uses the StockViewInterface to implement click listeners. These can then be used in Main Activity.
            stockView.setOnClickListener(v -> {
                stockViewInterface.onItemClick(getAdapterPosition());
                notifyItemChanged(selectedPosition);
                selectedPosition = RecyclerView.NO_POSITION;
                notifyItemChanged(selectedPosition);
            });

            stockView.setOnLongClickListener(v -> {
                stockViewInterface.onLongItemClick(getAdapterPosition());
                // Highlight row
                notifyItemChanged(selectedPosition);
                selectedPosition = getAdapterPosition();
                notifyItemChanged(selectedPosition);
                return true; // doesn't allow multiple rows to be selected. False would highlight multiple rows but the remove method only allows one deletion at a time for now.
            });

These are the long click and delete row methods in my Main Activity:

    // Long tap on a row in the Recycler View. Remove button becomes visible and once clicked opens the confirmation dialog.
    @Override
    public void onLongItemClick(int position) {
        btnRemoveItem.setVisibility(View.VISIBLE);
        btnRemoveItem.setOnClickListener(v -> {
            ConfirmDeleteDialog confirmDeleteDialog = new ConfirmDeleteDialog();
            Bundle bundle = new Bundle();
            bundle.putInt("position", position);
            confirmDeleteDialog.setArguments(bundle);
            confirmDeleteDialog.show((MainActivity.this).getSupportFragmentManager(),"confirm");
        });
        System.out.println(position);
    }

    // Deletes row.
    public void deleteItemRow(int position) {
        String stock = allStock.get(position).getItem();
        db.deleteStockItem(db.getStockItem(allStock.get(position).getID()));
        allStock.remove(position);
        stockAdapter.notifyItemRemoved(position);
        stockAdapter.notifyDataSetChanged();
        btnRemoveItem.setVisibility(View.INVISIBLE);
        stockAdapter.selectedPosition = RecyclerView.NO_POSITION;
        Toast.makeText(this, stock + " successfully deleted!", Toast.LENGTH_SHORT).show();
    }

The issue occurs in that deleteItemRow() method when I call it from my Dialog in the yes() method:

public class ConfirmDeleteDialog extends DialogFragment {
    private MainActivity mainActivity;
    public static final String TAG = "confirm";
    int position;
    Button no, yes;

    @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        LayoutInflater inflater = getActivity().getLayoutInflater();
        View v = inflater.inflate(R.layout.dialog_confirm_delete, null);
        Bundle bundle = getArguments();
        position = bundle.getInt("position", position);
        no = v.findViewById(R.id.btnNo);
        yes = v.findViewById(R.id.btnYes);
        mainActivity = new MainActivity();
        System.out.println(position);
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setView(v);
        no();
        yes();
        return builder.create();
    }

    private void no() {
        no.setOnClickListener(v -> {
            dismiss();
        });
    }

    private void yes() {
        yes.setOnClickListener(v -> {
            mainActivity.deleteItemRow(position);
            System.out.println(position);
            dismiss();
        });
    }
}

As mentioned at the start, the deleteItemRow() works correctly if I call it on the remove item button press if I do this:

@Override
    public void onLongItemClick(int position) {
        btnRemoveItem.setVisibility(View.VISIBLE);
        btnRemoveItem.setOnClickListener(v -> {
            deleteItemRow(position);
            
        });
        System.out.println(position);
    }

I have used the System.out lines to makes sure position is correct.

Can anyone help me understand why the list is empty when calling the method from my dialog but isn't empty if I call it in my main activity. And is there a way to fix this? Thanks in advance.

EDIT: My allStock ArrayList is empty when the method is called from the dialog. This is how I get the ArrayList:

private DBHandlerStock db;
private ArrayList<Stock> allStock = new ArrayList<>();

db = new DBHandlerStock(this);
allStock = db.getAllStock();

I managed to 'fix' this by making my DBHandler, ArrayList, Adapter and Button static. However this creates memory leaks so I'm going to find another way round it. Thanks for the replies though.

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