UnsupportedOperationException:单击肯定按钮时警报对话框崩溃

UnsupportedOperationException: Alert dialogcrashing when positive button clicked

所以我有这个回收站视图,其中最后一个项目应该在单击时显示一个对话框。该对话框还包含 2 个按钮,positive 和 negetive(默认按钮)。
在按下 Positive 按钮时,对话框应该触发回调,这将在回收器视图中添加一个新条目。在按下否定按钮时,对话框就会消失。

一切正常,直到用户单击肯定按钮。该应用程序崩溃并显示以下日志:


2019-08-13 18:27:35.668 29482-29482/z.y.x E/AndroidRuntime: FATAL EXCEPTION: main
    Process: z.y.x, PID: 29482
    java.lang.UnsupportedOperationException
        at java.util.AbstractList.add(AbstractList.java:148)
        at java.util.AbstractList.add(AbstractList.java:108)
        at z.y.x.dashboard_files.dashboard_fragment.QuantityButtonsAdapter.addItemInCentre(QuantityButtonsAdapter.java:85)
        at z.y.x.dashboard_files.dashboard_fragment.DashboardFragment.onPositiveButtonClick(DashboardFragment.java:144)
        at z.y.x.dashboard_files.dashboard_fragment.DashboardFragment$QuantityDialog.onClick(DashboardFragment.java:253)
        at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:172)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6692)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

这是自定义对话框 class:


    public static class QuantityDialog {
        @Nullable
        private QuantityButtonModel currentData;
        private AlertDialog.Builder builder;

        public interface OnPositiveClickListener {
            void onPositiveButtonClick(QuantityButtonModel data);
        }

        public QuantityDialog(Context ctx) {
            currentData = QUANTITY_GLASS;
            View dialogView = createView(ctx);
            builder = new AlertDialog.Builder(ctx)
                    .setView(dialogView)
                    .setCancelable(false)
                    .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            dialogInterface.dismiss();
                        }
                    });
        }
//        public void show() {
//            show(null);
//        }

        @SuppressLint("InflateParams")
        private View createView(Context ctx) {
            View v = LayoutInflater.from(ctx)
                    .inflate(R.layout.dialog_new_quantitiy_btn, null);

            //init ui
            SeekBar seekQty = v.findViewById(R.id.seekbar_qty);
            final TextView tvQty = v.findViewById(R.id.tv_qty_text_dialog);
            final ImageView ivQty = v.findViewById(R.id.iv_qty_icon_dialog);

            //init data and defaults
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                seekQty.setMin(0);
            }
            seekQty.setMax(QUANTITY_MAX);
            if (currentData != null) {
                seekQty.setProgress(currentData.getQty());
                tvQty.setText(String.format(Locale.ROOT, "%d ml", currentData.getQty()));
                ivQty.setImageResource(currentData.getQtyImage());
            }
            //init listener
            seekQty.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                @Override
                public void onProgressChanged(SeekBar seekBar, int progress, boolean b) {
                    tvQty.setText(String.format(Locale.ROOT, "%d ml", progress));
                    int resID = getResForQty(progress);
                    ivQty.setImageResource(resID);
                    currentData = new QuantityButtonModel(resID, progress);
                }

                @Override
                public void onStartTrackingTouch(SeekBar seekBar) {
                }

                @Override
                public void onStopTrackingTouch(SeekBar seekBar) {
                }
            });

            return v;
        }

        public void setOnPositiveClickListener(@Nullable final OnPositiveClickListener listener) {
            builder.setPositiveButton("Done", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                    if (listener != null) {
                        listener.onPositiveButtonClick(currentData);
                    }
                    dialogInterface.dismiss();
                }
            });

        }

        public void show() {
            builder.show();
        }

    }

在这里,我为我的回收站视图的适配器设置了一个侦听器。侦听器的制作方式是,如果项目位于最后一个位置以外的任何位置,它将显示敬酒,否则将显示对话框。该对话框也没有被重用(命名错误,我后来更改了它),而是在每次点击时生成并在同一点附加点击监听器,即在 rv 项目的点击上。

        adpButtons.setClickListener(new QuantityButtonClickListener() {
            @Override
            public void onItemClick(int qty) {
                Toast.makeText(
                        fragView.getContext(),
                        "add:" + qty + " to shared preferences",
                        Toast.LENGTH_SHORT)
                        .show();
            }

            @Override
            public void onAddNewItemClick() {
                QuantityDialog reusableQuantityDialog;
                reusableQuantityDialog = new QuantityDialog(fragView.getContext());
                reusableQuantityDialog.setOnPositiveClickListener(
                        new OnPositiveClickListener() {
                            @Override
                            public void onPositiveButtonClick(QuantityButtonModel data) {
                                adpButtons.addItemInCentre(data);
                            }
                        });
                reusableQuantityDialog.show();
            }
        });

希望我能解释清楚。我猜是某种回调地狱导致了这个问题。但我什至尝试重新使用仍然导致相同错误的对话框。
请帮忙。

编辑:这是适配器代码:


public class QuantityButtonsAdapter extends RecyclerView.Adapter<QuantityButtonsAdapter.RvHolder> {


    @NonNull
    private List<QuantityButtonModel> buttonModelList;
    private QuantityButtonClickListener clickListener;

    QuantityButtonsAdapter() {
        this(new ArrayList<QuantityButtonModel>(), null);
    }

    private QuantityButtonsAdapter(@NonNull List<QuantityButtonModel> buttonModelList,
                                   QuantityButtonClickListener listener) {
        this.buttonModelList = buttonModelList;
        this.clickListener = listener;
    }

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

    @Override
    public void onBindViewHolder(@NonNull RvHolder holder, int pos) {
        QuantityButtonModel data = buttonModelList.get(pos);

        //is last is a check based on which our code to add new data will get triggerred
        boolean isLast = (pos==(buttonModelList.size()-1));
        holder.bind(data.getQtyImage(), data.getQty(), clickListener, isLast);
    }

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

    @NonNull
    public List<QuantityButtonModel> getButtonModelList() {
        return buttonModelList;
    }

    public void setButtonModelList(@NonNull List<QuantityButtonModel> buttonModelList) {
        this.buttonModelList = buttonModelList;
        notifyDataSetChanged();
    }

    public QuantityButtonClickListener getClickListener() {
        return clickListener;
    }

    public void setClickListener(QuantityButtonClickListener clickListener) {
        this.clickListener = clickListener;
        notifyDataSetChanged();
    }


    public void addItemInCentre(QuantityButtonModel model) {
        //int pos= buttonModelList.size()/2;
        buttonModelList.add(model);
        notifyDataSetChanged();
    }

    class RvHolder extends RecyclerView.ViewHolder {
        ImageButton ibtQty;
        TextView tvQty;

        RvHolder(@NonNull View itemView) {
            super(itemView);
            ibtQty = itemView.findViewById(R.id.ibt_qty_btn);
            tvQty = itemView.findViewById(R.id.tv_qty_text);
            //ibtQty.setOnTouchListener(getMyItemTouchListener());

        }

        void bind(int qtyRes, final int qty, final QuantityButtonClickListener listener, final boolean isLast) {

            ibtQty.setImageResource(qtyRes);
            tvQty.setText(String.format(Locale.getDefault(), "%d ml", qty));
            ibtQty.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    if (isLast) {
                        listener.onAddNewItemClick();
                    } else {
                        listener.onItemClick(qty);
                    }

                    showButtonPressAnimation(view);

                }
            });
        }

    }


    interface QuantityButtonClickListener {
        void onItemClick(int qty);

        void onAddNewItemClick();
    }

    public static void showButtonPressAnimation(View view) {
        final float shrinkTo = 0.90f;
        final long duration = 100;
        ScaleAnimation grow = new ScaleAnimation(
                shrinkTo, 1,
                shrinkTo, 1,
                Animation.RELATIVE_TO_SELF, 0.5f,
                Animation.RELATIVE_TO_SELF, 0.5f);

        grow.setDuration(duration / 2);


        ScaleAnimation shrink = new ScaleAnimation(
                1, shrinkTo,
                1, shrinkTo,
                Animation.RELATIVE_TO_SELF, 0.5f,
                Animation.RELATIVE_TO_SELF, 0.5f);
        shrink.setDuration(duration / 2);

        grow.setStartOffset(duration / 2);
        AnimationSet set = new AnimationSet(true);
        set.setInterpolator(new LinearInterpolator());
        set.addAnimation(shrink);
        set.addAnimation(grow);
        view.startAnimation(set);
    }

}

没有仔细阅读您的对话源代码,但我预计不会有任何问题。

我猜问题是 adpButtons.addItemInCentre(data);,您没有在问题中显示,但是 addItemInCentre 实现正在调用 List.add 某些缺少 add实施。

也许它是一些反序列化的列表,或者一些不可变的列表,无法从头脑中回忆起任何示例,但我认为有一些可以从 XML 中的字符串创建,等等...

要使 add 可用,您需要类似于 ArrayList 的列表实例,等等...

因此,如果不显示这部分源代码,列表(您尝试 add 到)是如何定义和实例化的,很难准确地说出问题所在。

Edit:从 OP 响应中不清楚究竟有什么帮助,但在上面已经提到的方法(反序列化、反射)中,可以获得 List<> 实例固定大小也通过使用 Arrays.asList(..) 实用程序,它不会生成 ArrayList,而是 List<> 的专门实现,它将原始数组与 List<> 接口桥接(您可以修改通过 List<>.set(index, value) 调用的原始数组),因此它确实拒绝执行类似 add/remove 的操作,因为底层经典 Type[] 数组是固定大小的,然后 add/remove 需要新的数组,使原始引用无效 = 对 asList 功能没有意义。

即如果您需要 java.util.List<E> interface which supports also add and remove like functionality of List, you must make sure the instance of your List<> is provided by class capable of these operations, like java.util.ArrayList<E> or java.util.Vector<E> 的实现,并避免其他不支持 add/remove 的实现(例如 Arrays.asList(..) 调用产生的实现)。

此外,如果您正在编写适配器,您知道您将需要 List<> 的 add/remove 功能,您有时可能希望强制执行特定的 List 实现,例如 ArrayList,在 API 级别,因此当您稍后尝试将该适配器与其他实现一起使用时,它将在编译时失败。 (然后再次具有这样的特定 API 也会阻止使用其他支持 add/remove 的实现,不幸的是 List<E> 接口没有直接接口派生,这将确保 add/remove 是强制性的(它们在原始 List<E> 界面中是可选的),并且可以用来缩小 API 的范围。