在日常开发过程中,Dialog的使用频率应该是仅次于Activity,不管是出于UI交互原因还是业务需求,页面内经常会需要各种弹窗。这篇文章中并不是讲Dialog的使用也不是如何自定义各式各样弹窗。而是从源码的角度分析Dialog是如何展示在我们的屏幕中,在Android系统中Dialog如何创建视图、如何填充数据,如何添加Window中去。通过这篇文章我算是记录自己对Android视图结构的笔记。
那就直接开始,从具体的使用实例开始入手:
public void showDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);//步骤一
//步骤二
builder.setTitle("Title").
setMessage("message").
setIcon(R.drawable.ic_launcher_background).
setPositiveButton("Button1", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
}).
setNeutralButton("Button2", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
}).
setNegativeButton("Button3", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
}).
create(). //步骤三
show(); //步骤四
}
通过对上诉的代码的观察,AlertDialog明显是个Builder构建模式,AlertDialog.Builder同时扮演builder、ConcreteBuild、Director的角色,把AlertDialog对象的构建过程和创建过程分开。具体是怎么做的,我把上述分成了四给步骤:
步骤一 :通过AlertDialog.Builder构建了AlertController.AlertParams(以下简称为P)对象,从命名上可以看出AlertParams——弹窗的各种参数。
public Builder(Context context) {
this(context, resolveDialogTheme(context, ResourceId.ID_NULL));
}
public Builder(Context context, int themeResId) {
P = new AlertController.AlertParams(new ContextThemeWrapper(
context, resolveDialogTheme(context, themeResId)));
}
步骤二:Builder的一些列setXXX方法,给第一步中构建的P设置参数。
public Builder setTitle(CharSequence title) {
P.mTitle = title;
return this;
}
步骤三:Builder的create方法,到这里才真正创建AlertDialog对象,并且在其构造函数中创建了AlertController实例,然后再调用P的apply方法,把变量P中保存的各类参数设置到AlertDialog的AlertController对象中,在Dialog的构造方法中还获取了一个重要的对象WindowManager,在后面我们需要拿WindowManager添加视图到Window 上。
public AlertDialog create() {
// Context has already been wrapped with the appropriate theme.
final AlertDialog dialog = new AlertDialog(P.mContext, 0, false);
P.apply(dialog.mAlert);
//代码...
return dialog;
}
AlertDialog(Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
//在父类Dialog构造函数获得一个重要的对象WindowManager
super(context, createContextThemeWrapper ? resolveDialogTheme(context, themeResId) : 0,
createContextThemeWrapper);
mWindow.alwaysReadCloseOnTouchAttr();
mAlert = AlertController.create(getContext(), this, getWindow());
}
public void apply(AlertController dialog) {
if (mCustomTitleView != null) {
dialog.setCustomTitle(mCustomTitleView);
} else {
//代码...
}
if (mMessage != null) {
dialog.setMessage(mMessage);
}
//代码...
}
第四步:调用AlertDialog的show方法显示弹窗,也是我们分析的重点,主要做了如下几个事情;
- 通过dispatchOnCreate调用AlertDialog的onCreate方法:
- 然后在调用AlertDialog的onStart
- 最后将Dialog的DecorView添加到WindowManager
public void show() {
if (mShowing) {
if (mDecor != null) {
if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
}
mDecor.setVisibility(View.VISIBLE);
}
return;
}
mCanceled = false;
if (!mCreated) {
//调用生命周期onCreate
dispatchOnCreate(null);
} else {
// Fill the DecorView in on any configuration changes that
// may have occured while it was removed from the WindowManager.
final Configuration config = mContext.getResources().getConfiguration();
mWindow.getDecorView().dispatchConfigurationChanged(config);
}
//调用onStart
onStart();
mDecor = mWindow.getDecorView();
//代码...
WindowManager.LayoutParams l = mWindow.getAttributes();
//代码...
//windowManager.addView 添加视图
mWindowManager.addView(mDecor, l);
if (restoreSoftInputMode) {
l.softInputMode &=
~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
}
mShowing = true;
sendShowMessage();
}
在show方法中执行了Dialog的一系列生命周期方法,按照以往Activity的经验,而AlertDialog的内容视图构建也应该在onCreate方法中,进入AlertDialog 的onCreate方法
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAlert.installContent();
}
public void installContent() {
int contentView = selectContentView();
mWindow.setContentView(contentView);
setupView();
}
private int selectContentView() {
if (mButtonPanelSideLayout == 0) {
return mAlertDialogLayout;
}
if (mButtonPanelLayoutHint == AlertDialog.LAYOUT_HINT_SIDE) {
return mButtonPanelSideLayout;
}
return mAlertDialogLayout;
}
其中主要调用AlertController的installContent方法,在installContent中调用我们熟悉的setContentView方法,这里和Activity的设置视图一模一样,最终都是调用window的setContentView。而这里的contentView,也是就是mAlertDialogLayout弹窗视图布局,其值是在AlerController构造函数中进行的初始化。
protected AlertController(Context context, DialogInterface di, Window window) {
mContext = context;
mDialogInterface = di;
mWindow = window;
mHandler = new ButtonHandler(di);
//代码...
//alert_dialog.xml
final TypedArray a = context.obtainStyledAttributes(null,
R.styleable.AlertDialog, R.attr.alertDialogStyle, 0);
mAlertDialogLayout = a.getResourceId(
R.styleable.AlertDialog_layout, R.layout.alert_dialog);
//代码...
}
在setContentView中主要做的事情是创建一个DecorView,根据Theme,Feature添加了对应的布局文件,再把我们contentView添加到DecorView中。接下来再看setupView方法:
private void setupView() {
// 获取初始化内容视图
final View parentPanel = mWindow.findViewById(R.id.parentPanel);
//获取title区域
final View defaultTopPanel = parentPanel.findViewById(R.id.topPanel);
final View defaultContentPanel = parentPanel.findViewById(R.id.contentPanel);
final View defaultButtonPanel = parentPanel.findViewById(R.id.buttonPanel);
// Install custom content before setting up the title or buttons so
// that we can handle panel overrides.
final ViewGroup customPanel = (ViewGroup) parentPanel.findViewById(R.id.customPanel);
setupCustomContent(customPanel);
final View customTopPanel = customPanel.findViewById(R.id.topPanel);
final View customContentPanel = customPanel.findViewById(R.id.contentPanel);
final View customButtonPanel = customPanel.findViewById(R.id.buttonPanel);
// Resolve the correct panels and remove the defaults, if needed.
final ViewGroup topPanel = resolvePanel(customTopPanel, defaultTopPanel);
final ViewGroup contentPanel = resolvePanel(customContentPanel, defaultContentPanel);
final ViewGroup buttonPanel = resolvePanel(customButtonPanel, defaultButtonPanel);
/*初始化各视图*/
setupContent(contentPanel);
setupButtons(buttonPanel);
setupTitle(topPanel);
//代码...
a.recycle();
}
在setupView中就是初始化Alert Dialog布局中的各个部分,如标题栏、按钮栏、内容区域等等,在这个方法调用完后整个Dialog视图内容部分也就完成了。最后再把在通过WindowManager把DecorView添加到添加到Window上,并显示出来,正Dialog就出现在屏幕当中。
到这里已经把Dialog的主要流程分析完了。
网友评论