Dialog是Android里面用于让用户确认或输入信息的简单的UI展现形式,本文将通过Demo讲解如何使用Dialog。通常,我们不应该直接使用Dialog,而是应该使用Dialog的子类AlertDialog或DatePickerDialog以及TimePickerDialog。这三个子类对Dialog进行了封装,并定义了它们各自的外观结构。其中[AlertDialog]的样式通常包含了确定和取消按钮,以及标题和一小段描述文字。DatePickerDialog和TimePickerDialog用于选择日期和时间。
通常我们应该使用Dialogfragment作为Dialog的容器,Dialogfragment提供了对Dialog的封装以及生命周期的管理,可以自动处理屏幕旋转后DialogFragment的重建(Dialog则不能),下面给出了一个DialogFragment的典型用法。
简单的对话框
public class MyDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// 设置Dialog样式和theme
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), 0);
builder.setTitle("提示")
.setMessage("确定继续?")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
communicateInterface.positiveClicked();
dismiss();
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
communicateInterface.negativeClicked();
dismiss();
}
});
Dialog dialog = builder.create();
return dialog;
}
}
含列表的对话框
可以在对话框里添加简单的列表:
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.pick_color)
.setItems(R.array.colors_array, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// The 'which' argument contains the index position
// of the selected item
}
});
return builder.create();
}
自定义view样式
如果需要定制对话框的展现样式,可以通过自定义view的方式实现。方法有两种,调用AlertDialog.Builder的setView方法,或者重写Fragment的onCreateView方法。
方法一:重写onCreateDialog,调用AlertDialog.Builder的setView方法
这种方法不能修改PositiveButton或NegativeButton样式。
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// 设置Dialog样式和theme
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), 0);
builder.setTitle("提示")
.setView(getActivity().getLayoutInflater().inflate(R.layout.custom_view, null))
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dismiss();
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dismiss();
}
});
Dialog dialog = builder.create();
return dialog;
}
方法二:重写Fragment的onCreateView方法,这样就不会使用默认的Dialog样式,而是完全的自定义样式
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = getActivity().getLayoutInflater().inflate(R.layout.custom_view_2, container, false);
TextView cancel = (TextView) view.findViewById(R.id.cancel_action);
TextView confirm = (TextView) view.findViewById(R.id.confirm_action);
cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
confirm.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
return view;
}
Dialog和Activity交互
通过DialogFragment的onAttach方法,可以将宿主Activity/Fragment的实例传进来,从而能在DialogFragment中调用宿主Activity/Fragment的函数。
public interface CommunicateInterface {
void positiveClicked();
void negativeClicked();
}
public class MainActivity extends AppCompatActivity implements CommunicateInterface {
...
@Override
public void positiveClicked() {
Toast.makeText(this, "继续下一关", Toast.LENGTH_SHORT).show();
}
@Override
public void negativeClicked() {
Toast.makeText(this, "取消", Toast.LENGTH_SHORT).show();
}
...
}
public class MyDialogFragment2 extends DialogFragment {
private CommunicateInterface communicateInterface;
...
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
communicateInterface = (CommunicateInterface) activity;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
...
cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
communicateInterface.negativeClicked();
dismiss();
}
});
confirm.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
communicateInterface.positiveClicked();
dismiss();
}
});
return view;
}
}
Dialog展示
DialogFragment既可以作为Dialog占用一部分屏幕空间展示,也可以作为一个Fragment全屏展示,通过不同的添加方式实现这两种展示。
通过DialogFragment的show方法,会使Dialog占用一部分屏幕空间展示:
@OnClick(R.id.show_as_dialog_tv)
public void onShowAsDialogTVClicked(View v) {
DialogFragment dialogFragment = new MyDialogFragment2();
dialogFragment.show(getFragmentManager(), "dialog3");
}
通过FragmentTransation添加DialogFragment可以实现全屏展示:
@OnClick(R.id.show_as_fragment_tv)
public void onShowAsFragmentTVClicked(View v) {
DialogFragment dialogFragment = new MyDialogFragment2();
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.add(dialogFragment, "diglog4")
.addToBackStack(null)
.commit();
}
这种情况有个限制,只能通过重写onCreateView方法实现自定义view,而不能通过AlertDialog.build方式。
[Note]:** 虽然Android Developer文档提到,可以通过FragmentTransation添加DialogFragment实现全屏,但是实际测试结果表明,展示的Dialog仍然不是全屏,可参照Demo里的实现运行查看效果。具体原因未知。如果要全屏展示直接使用Fragment就行了,也不需要使用DialogFragment,所以此处未深究原因。
普通的Activity也可以作为Dialog展示,比如在超大屏幕上,不适合满屏展示的情况下。如下设置theme即可。
<activity android:theme="@android:style/Theme.Holo.DialogWhenLarge" >
Dialog取消展示
可以通过dismiss函数停止Dialog的展示,或者通过cancel取消Dialog的展示,前者发生在点击确定或取消按钮的时候,后者发生在按返回键或者点击Dialog之外的屏幕范围的时候。通过在DialogFragment中重写onDismiss和onCancel方法,可以加入对这两种事件的处理。注意,这里不能直接调用Dialog的setOnCancelListener或者setOnDismissListener方法设置回调函数,必须重写onDismiss或onCancel,否则报错:
java.lang.IllegalStateException: You can not set Dialog's OnCancelListener or OnDismissListener
Android Develop文档的描述:
Note:
DialogFragment own the Dialog.setOnCancelListener and Dialog.setOnDismissListener callbacks.
You must not set them yourself.
To find out about these events, override onCancel(DialogInterface) and onDismiss(DialogInterface)
当调用DialogFragment的cancel方法时,两个回调函数都会被调用;当调用dismiss方法时,只调用onDismiss会被调用。
Dialog展示WebView,监听后退键
如果在Dialog里嵌入WebView进行页面展示,那需要对返回键进行监听,以确定按返回键的时候是后退到上一个页面还是退出dialog,通过重写onDismiss或onCancel都无法实现这个功能,会直接退出dialog。正确的写法是对dialog进行设置onKeyListener,代码如下:
dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
@Override
public boolean onKey(DialogInterface dialogInterface, int keyCode, KeyEvent keyEvent) {
if (keyCode == KeyEvent.KEYCODE_BACK
&& keyEvent.getAction() == MotionEvent.ACTION_UP
&& webView.canGoBack()) {
webView.goBack();
return true; // pretend we've processed it
}
else
return false; // pass on to be processed as normal
}
});
参考:
https://developer.android.com/guide/topics/ui/dialogs.html
https://developer.android.com/reference/android/app/DialogFragment.html