由做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。也来看看吧。
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的一部分而已。
好了,我们来总结下,这第一种创建方法的顺序吧。showDialog()------->createDialog()---------------->onCreateDialog()---------->onCreate()--------------->setupView()-------------->onPrepareDialog()-------------->show().
接下来,我们再来看看,第二创建方法吧。
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()里面来做啊?第二种方法是不是做不到啊?