AlertDialog 源码解析一

概要:

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);
    }











你可能感兴趣的:(Android进阶之路)