Android开发笔记之:Dialog的使用详解
程序员文章站
2023-11-20 17:40:10
dialog是任何系统都必须有的一个控件,作为辅助窗口,用于显示一些消息,或请求用户采取一引起操作等。在android中也不例外,基本使用可能参看文档。使用时的注意事项1....
dialog是任何系统都必须有的一个控件,作为辅助窗口,用于显示一些消息,或请求用户采取一引起操作等。
在android中也不例外,基本使用可能参看文档。
使用时的注意事项
1. back键能取消掉对话框(dismiss),但是却不会触发其onokey和oncancel回调接口,所以如果你的对话框会改某些状态,一定要注意还有第三种方式取消对话框。
2. 尽量少用模态对话框(model dialog),如果dialog.setcancellable(false),就变成了一个模态对话框,除了程序内部把其dismiss,否则按什么键都无法将其取消。这是极差的用户体验,对话框本身就是一种干扰,再无法取消会把用户搞疯的。所以除非特别有必要,也即当执行某个操作时不希望被打破,才可以使用模态对话框。
3. 尽量少用对话框,它对用户是一种干扰,除非需要用户做操作,或者做出选择。通常的一般性的通知用toast或者notification就足够了。
4. 不要使用对话框风格的activity,也即把activity变成一个对话框。因为这样是自已定义的布局,与系统dialog的风格可能会不一致。最严重的是当系统风格发生变化,dialog的子类会变化,但activity式的对话框就不会变化。可以在ics中找一找activity对话框,你会发现其ok是在左边,而ics中系统dialog的ok都是在右边的。
5. 尽量保证dialog对象活在activity的生命周期之内,也即至多是在oncreate()和ondestroy()之间。
6. 要想到和测试到activity在其dialog.dismiss()之前死掉的情况。因为activity必须依附于某个正在显示的activity实例,当显示和取消的时候其activity实例必须存在,否则就会有"illegalargumentexception: view not attached to window manager"。
05-15 02:45:26.320: e/androidruntime(1161): java.lang.illegalargumentexception: view not attached to window manager
05-15 02:45:26.320: e/androidruntime(1161): at android.view.windowmanagerimpl.findviewlocked(windowmanagerimpl.java:355)
05-15 02:45:26.320: e/androidruntime(1161): at android.view.windowmanagerimpl.removeview(windowmanagerimpl.java:200)
05-15 02:45:26.320: e/androidruntime(1161): at android.view.window$localwindowmanager.removeview(window.java:432)
05-15 02:45:26.320: e/androidruntime(1161): at android.app.dialog.dismissdialog(dialog.java:278)
05-15 02:45:26.320: e/androidruntime(1161): at android.app.dialog.access$000(dialog.java:71)
05-15 02:45:26.320: e/androidruntime(1161): at android.app.dialog$1.run(dialog.java:111)
05-15 02:45:26.320: e/androidruntime(1161): at android.app.dialog.dismiss(dialog.java:268)
05-15 02:45:26.320: e/androidruntime(1161): at com.hilton.effectiveandroid.app.dialogdemo$1.handlemessage(dialogdemo.java:26)
05-15 02:45:26.320: e/androidruntime(1161): at android.os.handler.dispatchmessage(handler.java:99)
7. dialog.show()必须在主线程里调用,但dialog.dismiss()却可以在任何线程中调用。
三种使用方式比较
1. 直接创建一个局部的dialog对象
优点是变量是局部的容易理解和维护。缺点是dialog对象难以控制,容易引发runtimeexception。
2. 把dialog对象变成activity的域
优点是dialog对象可以重复利用,且activity可以控制以保证dialog不会在activity生命周期外显示。是推荐的使用方式。
3. 用activity的方法oncreatedialog(), showdialog()和dismissdialog()
优点是frameworks会帮忙照看dialog,在大多数情况下这是推荐的做法。但是对于activity提前死掉的情况,此方法必有runtimeexception,且无法回避。
实例
public class dialogdemo extends activity {
private static final int dismiss_dialog = 1;
private progressdialog mbetterdialog;
private handler mmainhandler = new handler() {
@override
public void handlemessage(message msg) {
switch (msg.what) {
case dismiss_dialog:
dialog dialog = (dialog) msg.obj;
dialog.dismiss();
break;
default:
break;
}
}
};
@override
protected void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.dialog_demo);
final button sucking = (button) findviewbyid(r.id.sucking);
sucking.setonclicklistener(new view.onclicklistener() {
public void onclick(view v) {
final activity activity = dialogdemo.this;
final progressdialog dialog = new progressdialog(activity);
dialog.settitle("worst dialogging");
dialog.setmessage("this is the worst dialogging scheme, never use it. this dialog is easy to " +
"run out of its attached activity, yielding windowmanager#badtokenexception if the activity is gone when dismissing");
dialog.setindeterminate(true);
dialog.setcancelable(true);
// you must do the show in main thread anyway
dialog.show();
new thread(new runnable() {
public void run() {
systemclock.sleep(10000);
/*
* illegalargumentexception: view not attached to window manager
* if the activity showing the dialog was killed before dismiss() out of rotation or locale changed,
* the dialog will gone with activity, but when dismiss() yields "illegalargumentexception: view not attached to
* window manager".
* checking isshowing() won't help.
* checking activity.isfinishing() won't help, either.
* dismiss it in main thread also won't give any help.
*/
// this won't work
// if (dialog.isshowing()) {
// dialog.dismiss();
// }
// if (!activity.isfinishing()) {
// dialog.dismiss();
// }
message msg = message.obtain();
msg.what = dismiss_dialog;
msg.obj = dialog;
mmainhandler.sendmessage(msg);
}
}).start();
}
});
final button better = (button) findviewbyid(r.id.better);
better.setonclicklistener(new view.onclicklistener() {
public void onclick(view v) {
mbetterdialog = new progressdialog(dialogdemo.this);
mbetterdialog.settitle("better dialogging");
mbetterdialog.setmessage("this dialogging can be used. the dialog object is a field of its activity, so activity can" +
" control it to make sure dialog only lives within activity lifecircle");
mbetterdialog.setindeterminate(true);
mbetterdialog.setcancelable(true);
// you must do the show in main thread anyway
mbetterdialog.show();
new thread(new runnable() {
public void run() {
systemclock.sleep(10000);
/*
* this is much better, mbetterdialog is a field of its activity, so activity can take care of it in order
* to make sure dialog only live within activity's life circle, to avoid any unexpected exceptions.
*/
// this really works
if (mbetterdialog != null && mbetterdialog.isshowing()) {
mbetterdialog.dismiss();
}
}
}).start();
}
});
final button optional = (button) findviewbyid(r.id.optional);
optional.setonclicklistener(new view.onclicklistener() {
@suppresswarnings("deprecation")
public void onclick(view v) {
showdialog(0);
new thread(new runnable() {
public void run() {
systemclock.sleep(10000);
/*
* this way works best for most of time, except if activity died before dismissing, exception must be
* thrown: "illegalargumentexception: view not attached to window manager".
* although activity takes care of its belonging dialog, there is no way to operate it manually any more.
* first you do not have reference to dialog object and second, any manual operation only interferences
* and breaks state maintained by frameworks.
*/
dismissdialog(0);
}
}).start();
}
});
}
@override
protected dialog oncreatedialog(int id) {
progressdialog d = new progressdialog(this);
d.settitle("optional dialogging");
d.setmessage("this dialogging scheme works best for most times, the dialogs are all taken care of by activitys and frameworks" +
". except for activity being killed during dialog showing");
d.setindeterminate(true);
d.setcancelable(true);
return d;
}
@override
protected void ondestroy() {
super.ondestroy();
// activity is dying, all its belonging dialogs should be dismissed, of course.
if (mbetterdialog != null && mbetterdialog.isshowing()) {
mbetterdialog.dismiss();
mbetterdialog = null;
}
// for dialogs showed via showdialog(int), no way to stop it in ondestroy()
// dismissdialog(0); // cause "illegalargumentexception: no dialog with id 0 was ever shown via activity#showdialog"
// this is because activity has to manage its dialog during onpause() and onresume() to restore
// dialogs' state. so if you manually dismiss it in ondestroy(), it will cause je.
// removedialog(0);// cause "illegalargumentexception: no dialog with id 0 was ever shown via activity#showdialog", when
// dismissing in thread.
// this is because activity has to manage its dialog during onpause() and onresume() to restore
// dialogs' state. so if you manually dismiss it in ondestroy(), it will cause je.
}
}
在android中也不例外,基本使用可能参看文档。
使用时的注意事项
1. back键能取消掉对话框(dismiss),但是却不会触发其onokey和oncancel回调接口,所以如果你的对话框会改某些状态,一定要注意还有第三种方式取消对话框。
2. 尽量少用模态对话框(model dialog),如果dialog.setcancellable(false),就变成了一个模态对话框,除了程序内部把其dismiss,否则按什么键都无法将其取消。这是极差的用户体验,对话框本身就是一种干扰,再无法取消会把用户搞疯的。所以除非特别有必要,也即当执行某个操作时不希望被打破,才可以使用模态对话框。
3. 尽量少用对话框,它对用户是一种干扰,除非需要用户做操作,或者做出选择。通常的一般性的通知用toast或者notification就足够了。
4. 不要使用对话框风格的activity,也即把activity变成一个对话框。因为这样是自已定义的布局,与系统dialog的风格可能会不一致。最严重的是当系统风格发生变化,dialog的子类会变化,但activity式的对话框就不会变化。可以在ics中找一找activity对话框,你会发现其ok是在左边,而ics中系统dialog的ok都是在右边的。
5. 尽量保证dialog对象活在activity的生命周期之内,也即至多是在oncreate()和ondestroy()之间。
6. 要想到和测试到activity在其dialog.dismiss()之前死掉的情况。因为activity必须依附于某个正在显示的activity实例,当显示和取消的时候其activity实例必须存在,否则就会有"illegalargumentexception: view not attached to window manager"。
复制代码 代码如下:
05-15 02:45:26.320: e/androidruntime(1161): java.lang.illegalargumentexception: view not attached to window manager
05-15 02:45:26.320: e/androidruntime(1161): at android.view.windowmanagerimpl.findviewlocked(windowmanagerimpl.java:355)
05-15 02:45:26.320: e/androidruntime(1161): at android.view.windowmanagerimpl.removeview(windowmanagerimpl.java:200)
05-15 02:45:26.320: e/androidruntime(1161): at android.view.window$localwindowmanager.removeview(window.java:432)
05-15 02:45:26.320: e/androidruntime(1161): at android.app.dialog.dismissdialog(dialog.java:278)
05-15 02:45:26.320: e/androidruntime(1161): at android.app.dialog.access$000(dialog.java:71)
05-15 02:45:26.320: e/androidruntime(1161): at android.app.dialog$1.run(dialog.java:111)
05-15 02:45:26.320: e/androidruntime(1161): at android.app.dialog.dismiss(dialog.java:268)
05-15 02:45:26.320: e/androidruntime(1161): at com.hilton.effectiveandroid.app.dialogdemo$1.handlemessage(dialogdemo.java:26)
05-15 02:45:26.320: e/androidruntime(1161): at android.os.handler.dispatchmessage(handler.java:99)
7. dialog.show()必须在主线程里调用,但dialog.dismiss()却可以在任何线程中调用。
三种使用方式比较
1. 直接创建一个局部的dialog对象
优点是变量是局部的容易理解和维护。缺点是dialog对象难以控制,容易引发runtimeexception。
2. 把dialog对象变成activity的域
优点是dialog对象可以重复利用,且activity可以控制以保证dialog不会在activity生命周期外显示。是推荐的使用方式。
3. 用activity的方法oncreatedialog(), showdialog()和dismissdialog()
优点是frameworks会帮忙照看dialog,在大多数情况下这是推荐的做法。但是对于activity提前死掉的情况,此方法必有runtimeexception,且无法回避。
实例
复制代码 代码如下:
public class dialogdemo extends activity {
private static final int dismiss_dialog = 1;
private progressdialog mbetterdialog;
private handler mmainhandler = new handler() {
@override
public void handlemessage(message msg) {
switch (msg.what) {
case dismiss_dialog:
dialog dialog = (dialog) msg.obj;
dialog.dismiss();
break;
default:
break;
}
}
};
@override
protected void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.dialog_demo);
final button sucking = (button) findviewbyid(r.id.sucking);
sucking.setonclicklistener(new view.onclicklistener() {
public void onclick(view v) {
final activity activity = dialogdemo.this;
final progressdialog dialog = new progressdialog(activity);
dialog.settitle("worst dialogging");
dialog.setmessage("this is the worst dialogging scheme, never use it. this dialog is easy to " +
"run out of its attached activity, yielding windowmanager#badtokenexception if the activity is gone when dismissing");
dialog.setindeterminate(true);
dialog.setcancelable(true);
// you must do the show in main thread anyway
dialog.show();
new thread(new runnable() {
public void run() {
systemclock.sleep(10000);
/*
* illegalargumentexception: view not attached to window manager
* if the activity showing the dialog was killed before dismiss() out of rotation or locale changed,
* the dialog will gone with activity, but when dismiss() yields "illegalargumentexception: view not attached to
* window manager".
* checking isshowing() won't help.
* checking activity.isfinishing() won't help, either.
* dismiss it in main thread also won't give any help.
*/
// this won't work
// if (dialog.isshowing()) {
// dialog.dismiss();
// }
// if (!activity.isfinishing()) {
// dialog.dismiss();
// }
message msg = message.obtain();
msg.what = dismiss_dialog;
msg.obj = dialog;
mmainhandler.sendmessage(msg);
}
}).start();
}
});
final button better = (button) findviewbyid(r.id.better);
better.setonclicklistener(new view.onclicklistener() {
public void onclick(view v) {
mbetterdialog = new progressdialog(dialogdemo.this);
mbetterdialog.settitle("better dialogging");
mbetterdialog.setmessage("this dialogging can be used. the dialog object is a field of its activity, so activity can" +
" control it to make sure dialog only lives within activity lifecircle");
mbetterdialog.setindeterminate(true);
mbetterdialog.setcancelable(true);
// you must do the show in main thread anyway
mbetterdialog.show();
new thread(new runnable() {
public void run() {
systemclock.sleep(10000);
/*
* this is much better, mbetterdialog is a field of its activity, so activity can take care of it in order
* to make sure dialog only live within activity's life circle, to avoid any unexpected exceptions.
*/
// this really works
if (mbetterdialog != null && mbetterdialog.isshowing()) {
mbetterdialog.dismiss();
}
}
}).start();
}
});
final button optional = (button) findviewbyid(r.id.optional);
optional.setonclicklistener(new view.onclicklistener() {
@suppresswarnings("deprecation")
public void onclick(view v) {
showdialog(0);
new thread(new runnable() {
public void run() {
systemclock.sleep(10000);
/*
* this way works best for most of time, except if activity died before dismissing, exception must be
* thrown: "illegalargumentexception: view not attached to window manager".
* although activity takes care of its belonging dialog, there is no way to operate it manually any more.
* first you do not have reference to dialog object and second, any manual operation only interferences
* and breaks state maintained by frameworks.
*/
dismissdialog(0);
}
}).start();
}
});
}
@override
protected dialog oncreatedialog(int id) {
progressdialog d = new progressdialog(this);
d.settitle("optional dialogging");
d.setmessage("this dialogging scheme works best for most times, the dialogs are all taken care of by activitys and frameworks" +
". except for activity being killed during dialog showing");
d.setindeterminate(true);
d.setcancelable(true);
return d;
}
@override
protected void ondestroy() {
super.ondestroy();
// activity is dying, all its belonging dialogs should be dismissed, of course.
if (mbetterdialog != null && mbetterdialog.isshowing()) {
mbetterdialog.dismiss();
mbetterdialog = null;
}
// for dialogs showed via showdialog(int), no way to stop it in ondestroy()
// dismissdialog(0); // cause "illegalargumentexception: no dialog with id 0 was ever shown via activity#showdialog"
// this is because activity has to manage its dialog during onpause() and onresume() to restore
// dialogs' state. so if you manually dismiss it in ondestroy(), it will cause je.
// removedialog(0);// cause "illegalargumentexception: no dialog with id 0 was ever shown via activity#showdialog", when
// dismissing in thread.
// this is because activity has to manage its dialog during onpause() and onresume() to restore
// dialogs' state. so if you manually dismiss it in ondestroy(), it will cause je.
}
}