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 的范围。
所以我有这个回收站视图,其中最后一个项目应该在单击时显示一个对话框。该对话框还包含 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 的范围。