在使用AlertDialog的时候,经常会遇到这样的需求:点击【确认】之后,如果dialog内的输入内容有误,则dialog不dismiss。但实际情况是,无论点击【确认】还是【取消】,dialog都会执行dismiss操作。
查看源码如下:
View.OnClickListener mButtonHandler = new View.OnClickListener() {
public void onClick(View v) {
Message m = null;
if (v == mButtonPositive && mButtonPositiveMessage != null){
m = Message.obtain(mButtonPositiveMessage);
} else if (v == mButtonNegative && mButtonNegativeMessage != null) {
m = Message.obtain(mButtonNegativeMessage);
} else if (v == mButtonNeutral && mButtonNeutralMessage != null) {
m = Message.obtain(mButtonNeutralMessage);
}
if (m != null) {
m.sendToTarget();
}
// Post a message so we dismiss after the above handlers are executed
mHandler.obtainMessage(ButtonHandler.MSG_DISMISS_DIALOG, mDialogInterface).sendToTarget();
}
};
上面的源码为AlertController中自定义的button点击监听事件,从代码中可以看到,点击按钮一共执行了两次handler的send操作,一次是按钮的点击send:m.sendToTarget()
,执行按钮的点击事件,紧接着又执行了一条handler的send操作:mHandler.obtainMessage(ButtonHandler.MSG_DISMISS_DIALOG, mDialogInterface).sendToTarget();
操作。因此我们得到,不论你点击dialog的哪个按钮,最后都会执行dialog的dismiss操作。
因此,基于上面的需求,我们需要修改点击事件的执行过程。通过查找资料,一共找到两种方式解决这一问题:
方法一:使用自定义的按钮,不使用dialog的setNegativeButton和setPositiveButton,并为自定义的按钮添加点击事件,在自定义的按钮点击监听事件中,对dialog的dismiss进行管理;
方法二:更多时候,我们的dialog实现一个很简单的功能,并不用大费周章的自定义一个button。所以我们可以使用反射的方式来实现上面的需求,具体实现代码如下:
final EditText et = new EditText(mContext);
et.setInputType(InputType.TYPE_CLASS_NUMBER);
et.setText(PreferenceUtil.getInstance().getDefaultInt(ConstValue.TEST_DATA_LENGTH, 2000) + "");
et.setTextColor(mContext.getResources().getColor(R.color.color_black));
TextView tv = new TextView(mContext);
tv.setText("Title测试");
tv.setTextColor(mContext.getResources().getColor(R.color.red_normal));
tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 25);
new AlertDialog.Builder(mContext)
.setCustomTitle(tv)
.setView(et)
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
String input = et.getText().toString();
if (StringUtil.isEmpty(input)) { // 不满足条件式时,提示,并不dismiss该dialog
CommonUtil.customToast(mContext, "数据包长度不能为空", Toast.LENGTH_SHORT, Gravity.TOP);
try {
Field field = dialog.getClass().getSuperclass().getDeclaredField("mShowing");
field.setAccessible(true);
field.set(dialog, false);
} catch (Exception e) {
e.printStackTrace();
}
} else {
int length = Integer.valueOf(input);
if (length < 500) {
CommonUtil.customToast(mContext, "数据包长度至少500", Toast.LENGTH_SHORT, Gravity.TOP);
try {
Field field = dialog.getClass().getSuperclass().getDeclaredField("mShowing");
field.setAccessible(true);
field.set(dialog, false);
} catch (Exception e) {
e.printStackTrace();
}
} else { // 满足条件式时,使该dialog
可以dismiss PreferenceUtil.getInstance().persist(ConstValue.TEST_DATA_LENGTH, length);
try {
Field field = dialog.getClass().getSuperclass().getDeclaredField("mShowing");
field.setAccessible(true);
field.set(dialog, true);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
try {
Field field = dialog.getClass().getSuperclass().getDeclaredField("mShowing");
field.setAccessible(true);
field.set(dialog, true);
} catch (Exception e) {
e.printStackTrace();
}
}
})
.show();
这样就可以实现我们上面的需求了。
注:在开始实现反射这样的方法时,报了这样的错误:
java.lang.NoSuchFieldException: No field mShowing in class Landroid/support/v7/app/AppCompatDialog; (declaration of 'android.support.v7.app.AppCompatDialog' appears in /data/app/com.rencarehealth.mirhythm-1/split_lib_dependencies_apk.apk:classes19.dex)
从错误描述可知,mShowing名字的field并不存在AppCompatDialog的class中,所以需要将你代码中引入的Dialog类修改为非support_v7中的Dialog。