由做android应用到做android手机也有些时间了。总是觉得自己成长的太慢。
后仔细想来,自己没有好好总结。学而不思则罔。今天就来好好总结下android里面的Dialog。
相信大家,都对用Dialog十分熟悉。有两种方法,
一种是在Activity里调用showDialog(int),然后在Dialog onCreateDialog里创建Dialog返回,交由Activity来管理。
一种是自己直接用AlertDialog.Builder自己Create,然后自己调用show(),然后显示。
我把话先说在前头,建议用第一种方法去做,这会让你省去很多麻烦,因为Activity已经帮我们管理Dialog,做了很多有用的事情。
还有几个,很重要的概念,相信大家都也应该清楚:
1. 我们所有的应用程序所用到的dialog都是继承自AlertDialog的,包括ProgressDialog,TimePickerDialog,DatePickerDialog等。
2. Dialog不同于Activity,它有自己的Window。
接下来,我们从两种创建Dialog方法来分析吧。以ProgressDialog为例。
首先,我们来看下,ProgressDialog显示出来的几个部分吧。
总的来说,分三部分。
1. Message,Title,等。
2. 进度条,显示百分比的字符等。
3.下面被隐藏的Button。
为什么这么说呢。请跟我看代码,从第一种方法说起。即ActivitShowDialog(int)
public final void showDialog(int id) {
showDialog(id, null);
}
public final boolean showDialog(int id, Bundle args) {
if (mManagedDialogs == null) {
mManagedDialogs = new SparseArray<ManagedDialog>();
}
ManagedDialog md = mManagedDialogs.get(id);
if (md == null) {
md = new ManagedDialog();
md.mDialog = createDialog(id, null, args);
if (md.mDialog == null) {
return false;
}
mManagedDialogs.put(id, md);
}
md.mArgs = args;
onPrepareDialog(id, md.mDialog, args);
md.mDialog.show();
return true;
}
private Dialog createDialog(Integer dialogId, Bundle state, Bundle args) {
final Dialog dialog = onCreateDialog(dialogId, args);
if (dialog == null) {
return null;
}
dialog.dispatchOnCreate(state);
return dialog;
}
这里面可以很明显的看出来,Activity首先去列表里面去查找,看这个id的Dialog是否创建过,不存在就先调用createDialog(),创建dialog。否则就直接调用了onPrepareDialog,最后调用show()(这里面有文章,放在后面讲)。
我们也可以看到,在createDialog里面,先调用了onCreateDialog(),拿到dialog对象,然后去触发dialog的,onCreate函数。
这里一直没发现,我们设置的一些参数setMessage,setTitle,setButton去哪儿了。我们去ProgressDialog.onCreate看看吧。
protected void onCreate(Bundle savedInstanceState) {
LayoutInflater inflater = LayoutInflater.from(mContext);
if (mProgressStyle == STYLE_HORIZONTAL) {
/* Use a separate handler to update the text views as they
* must be updated on the same thread that created them.
*/
mViewUpdateHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
/* Update the number and percent */
int progress = mProgress.getProgress();
int max = mProgress.getMax();
double percent = (double) progress / (double) max;
String format = mProgressNumberFormat;
mProgressNumber.setText(String.format(format, progress, max));
SpannableString tmp = new SpannableString(mProgressPercentFormat.format(percent));
tmp.setSpan(new StyleSpan(android.graphics.Typeface.BOLD),
0, tmp.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
mProgressPercent.setText(tmp);
}
};
View view = inflater.inflate(R.layout.alert_dialog_progress, null);
mProgress = (ProgressBar) view.findViewById(R.id.progress);
mProgressNumber = (TextView) view.findViewById(R.id.progress_number);
mProgressNumberFormat = "%d/%d";
mProgressPercent = (TextView) view.findViewById(R.id.progress_percent);
mProgressPercentFormat = NumberFormat.getPercentInstance();
mProgressPercentFormat.setMaximumFractionDigits(0);
setView(view);
} else {
View view = inflater.inflate(R.layout.progress_dialog, null);
mProgress = (ProgressBar) view.findViewById(R.id.progress);
mMessageView = (TextView) view.findViewById(R.id.message);
setView(view);
}
............................
setIndeterminate(mIndeterminate);
onProgressChanged();
super.onCreate(savedInstanceState);
public Builder setView(View view) {
P.mView = view;
P.mViewSpacingSpecified = false;
return this;
}
可以看到ProgresDialog只是,做了个主要setView(),实际上就是给mView赋值,然后调用父类AlertDialog的onCreate。也来看看吧。
好了,我们来总结下,这第一种创建方法的顺序吧。showDialog()------->createDialog()---------------->onCreateDialog()---------->onCreate()--------------->setupView()-------------->onPrepareDialog()-------------->show().protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mAlert.installContent(); } 进入AlertController public void installContent() { /* We use a custom title so never request a window title */ mWindow.requestFeature(Window.FEATURE_NO_TITLE); if (mView == null || !canTextInput(mView)) { mWindow.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); } mWindow.setContentView(com.android.internal.R.layout.alert_dialog); setupView(); } private void setupView() { LinearLayout contentPanel = (LinearLayout) mWindow.findViewById(R.id.contentPanel); setupContent(contentPanel); boolean hasButtons = setupButtons(); LinearLayout topPanel = (LinearLayout) mWindow.findViewById(R.id.topPanel); TypedArray a = mContext.obtainStyledAttributes( null, com.android.internal.R.styleable.AlertDialog, com.android.internal.R.attr.alertDialogStyle, 0); boolean hasTitle = setupTitle(topPanel); View buttonPanel = mWindow.findViewById(R.id.buttonPanel); if (!hasButtons) { buttonPanel.setVisibility(View.GONE); } FrameLayout customPanel = null; if (mView != null) { customPanel = (FrameLayout) mWindow.findViewById(R.id.customPanel); FrameLayout custom = (FrameLayout) mWindow.findViewById(R.id.custom); custom.addView(mView, new LayoutParams(MATCH_PARENT, MATCH_PARENT)); if (mViewSpacingSpecified) { custom.setPadding(mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight, mViewSpacingBottom); } if (mListView != null) { ((LinearLayout.LayoutParams) customPanel.getLayoutParams()).weight = 0; } } else { mWindow.findViewById(R.id.customPanel).setVisibility(View.GONE); } /* Only display the divider if we have a title and a * custom view or a message. */ if (hasTitle && ((mMessage != null) || (mView != null))) { View divider = mWindow.findViewById(R.id.titleDivider); divider.setVisibility(View.VISIBLE); 。。。。。。 看到了吗?我们前面的mView只是整个Dialog的一部分而已。
接下来,我们再来看看,第二创建方法吧。
AlertDialog.Builder builder = new Builder(Main.this); builder.setMessage("确认退出吗?"); builder.setTitle("提示"); builder.setPositiveButton("确认", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); Main.this.finish(); } }); builder.setNegativeButton("取消", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }); builder.create().show();
public AlertDialog create() { final AlertDialog dialog = new AlertDialog(P.mContext); P.apply(dialog.mAlert); dialog.setCancelable(P.mCancelable); dialog.setOnCancelListener(P.mOnCancelListener); if (P.mOnKeyListener != null) { dialog.setOnKeyListener(P.mOnKeyListener); } return dialog; }
看到没有。create完,直接调show().
那我不知道你们没有同样的疑问。像我们第一种方法里,像Title,Message,mView都是在AlertDialog的onCreate()里面放进去的。那我们又没有调用onCreate()函数,他们最后是怎样显示出来的呢?
呵呵。我们最后来看看
builder.create()产生的是AlertDialog对象,但它自己没有定义show().最后调到了Dialog.show()
public void show() { if (mShowing) { if (mDecor != null) { mDecor.setVisibility(View.VISIBLE); } return; } if (!mCreated) { dispatchOnCreate(null); } onStart(); mDecor = mWindow.getDecorView(); WindowManager.LayoutParams l = mWindow.getAttributes(); if ((l.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) { WindowManager.LayoutParams nl = new WindowManager.LayoutParams(); nl.copyFrom(l); nl.softInputMode |= WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; l = nl; } try { mWindowManager.addView(mDecor, l); mShowing = true; sendShowMessage(); } finally { } }
看到这里。为什么Tilte,message,为什么会出现,知道了吧。
第二种方法的流程是这样的。
show()------------>onCreat()----------->setupView().
总算简要的分析完了。
提个问题。如果,你想修改Dialog,title的字体,是不是只能在onPrepareDialog()里面来做啊?第二种方法是不是做不到啊?