概要:
AlertDialog,平常用的很多,今天走了一遍内部实现,记录一下。
解析:
一AlertDialog的常用调用:
new AlertDialog.Builder(mContext)
.setTitle("title")
.setMessage("message")
.create()
.show();
这是一个典型的Builder的构建者模式,Builder作为AlertDialog的内部类,专门用于构建AlertDialog对象。
Builder的链式调用,能够很好的简化代码。
AlertDialog与Builder的内部实现:
public class AlertDialog extends Dialog implements DialogInterface {
private AlertController mAlert;
AlertDialog(Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
super(context, createContextThemeWrapper ? resolveDialogTheme(context, themeResId) : 0,
createContextThemeWrapper);
mWindow.alwaysReadCloseOnTouchAttr();
mAlert = AlertController.create(getContext(), this, getWindow());
}
@Override
public void setTitle(CharSequence title) {
super.setTitle(title);
mAlert.setTitle(title);
}
public void setMessage(CharSequence message) {
mAlert.setMessage(message);
}
public static class Builder {
private final AlertController.AlertParams P;
public Builder(Context context, int themeResId) {
P = new AlertController.AlertParams(new ContextThemeWrapper(
context, resolveDialogTheme(context, themeResId)));
}
public Builder setTitle(CharSequence title) {
P.mTitle = title;
return this;
}
public Builder setMessage(@StringRes int messageId) {
P.mMessage = P.mContext.getText(messageId);
return this;
}
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);
dialog.setCancelable(P.mCancelable);
if (P.mCancelable) {
dialog.setCanceledOnTouchOutside(true);
}
dialog.setOnCancelListener(P.mOnCancelListener);
dialog.setOnDismissListener(P.mOnDismissListener);
if (P.mOnKeyListener != null) {
dialog.setOnKeyListener(P.mOnKeyListener);
}
return dialog;
}
public AlertDialog show() {
final AlertDialog dialog = create();
dialog.show();
return dialog;
}
}
}
通过上面的代码分析:
1,创建AlertDialog.Builder的时候,会创建AlertController.AlertParams对象P,P里面封装了所有的Dialog
的属性。
2,我们在调用Builder的setTitle,setMessage的时候,其实就是在将方法的参数值赋值给AlertParams P;
3,在Builder的create方法里面,会构建AlertDialog对像dialog。其中P.apply(dialog.mAlert);就是将P的属性
全部设置到Dialog的属性变量mAlert中:
public void apply(AlertController dialog) {
if (mCustomTitleView != null) {
dialog.setCustomTitle(mCustomTitleView);
} else {
if (mTitle != null) {
dialog.setTitle(mTitle);
}
if (mIcon != null) {
dialog.setIcon(mIcon);
}
if (mIconId != 0) {
dialog.setIcon(mIconId);
}
if (mIconAttrId != 0) {
dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId));
}
}
if (mMessage != null) {
dialog.setMessage(mMessage);
}
//省略了次要代码
}
4,Builder的create方法,最后返回了AlertDialog对象dialog。所以最后show方法,是dialog.show();
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) {
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();
mDecor = mWindow.getDecorView();
if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
final ApplicationInfo info = mContext.getApplicationInfo();
mWindow.setDefaultIcon(info.icon);
mWindow.setDefaultLogo(info.logo);
mActionBar = new WindowDecorActionBar(this);
}
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;
}
mWindowManager.addView(mDecor, l);
mShowing = true;
sendShowMessage();
}
5,这里会调用dispatchOnCreate(null),最终会回调AlertDialog的onCreate方法。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAlert.installContent();
}
//一下是AlertController类中方法。
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;
}
// TODO: use layout hint side for long messages/lists
return mAlertDialogLayout;
}
6,mAlert.installContent,即是在初始化AlertDialog的控件。(即通过fingViewById,关联控件)
7,最后通过mWindowManager.addView(mDecorView, l);加载到屏幕上。
注:
1,由上分析可知,我们如果想要自定义Dialog,继承AlertDialog,重新onCreate方法(给个建议)。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initViews(getContext());
}
private void initViews(Context context){
setContentView(R.layout.dialog_horizontal);
tv_title = getWindow().findViewById(R.id.tv_title);
tv_size = getWindow().findViewById(R.id.tv_size);
progressBar = getWindow().findViewById(R.id.progress);
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.width = DensityUtil.dp2px(context.getApplicationContext(), 320);
// lp.width = WindowManager.LayoutParams.MATCH_PARENT;
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
getWindow().setAttributes(lp);
}
2,其中mWindow是PhoneWindow。在AlertDialog的构造方法中生成。
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
if (createContextThemeWrapper) {
if (themeResId == ResourceId.ID_NULL) {
final TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
themeResId = outValue.resourceId;
}
mContext = new ContextThemeWrapper(context, themeResId);
} else {
mContext = context;
}
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
final Window w = new PhoneWindow(mContext);//
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setOnWindowSwipeDismissedCallback(() -> {
if (mCancelable) {
cancel();
}
});
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}