美文网首页
Android Dialog源码分析

Android Dialog源码分析

作者: Y小圆脸 | 来源:发表于2019-09-29 10:17 被阅读0次

    1.当我们查看android dialog源码,首先查看dialog的构造方法,初始化相关数据。由源码可知Dialog获取当前的themeId,通过 new ContextThemeWrapper(context, themeResId);设置主题返回Context对象。通过new PhoneWindow()创建Window对象,设置Window相关方法,

     public Dialog(@NonNull Context context) {
            this(context, 0, true);
    }
        
     public Dialog(@NonNull Context context, @StyleRes int themeResId) {
            this(context, themeResId, true);
    }
     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);
        }  
        
    

    Dialog创建类似activity创建,通过new PhoneWidow对象,我们查看以下源码调用setContentView()方法可知通过Window实现类PhoneWindow将布局文件添加到DectorView中。

    //以下是Window相关的代码
    public void setContentView(@LayoutRes int layoutResID) {
            mWindow.setContentView(layoutResID);
        }
      
      //以下是PhoneWindow的相关的代码  
     public void setContentView(int layoutResID) {
           
            if (mContentParent == null) {
                installDecor();
            } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
                mContentParent.removeAllViews();
            }
    
            if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
                final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                        getContext());
                transitionTo(newScene);
            } else {
                mLayoutInflater.inflate(layoutResID, mContentParent);
            }
            mContentParent.requestApplyInsets();
            final Callback cb = getCallback();
            if (cb != null && !isDestroyed()) {
                cb.onContentChanged();
            }
            mContentParentExplicitlySet = true;
        }
    

    2.查看dialog的show()方法,在show()方法中,判断当前dialog是否已经显示,调用oncreate(),onStart(),获取getDecorView(),通过windowManager添加DectorView,发送消息给主线程通知弹窗已经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 {
               
                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();
        }
    

    当我们查看dismissDisDialog()方法时,可知通过widowManager移除DectorView,mWindowManager.removeViewImmediate(mDecor);

    void dismissDialog() {
            if (mDecor == null || !mShowing) {
                return;
            }
    
            if (mWindow.isDestroyed()) {
                Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!");
                return;
            }
    
            try {
                mWindowManager.removeViewImmediate(mDecor);
            } finally {
                if (mActionMode != null) {
                    mActionMode.finish();
                }
                mDecor = null;
                mWindow.closeAllPanels();
                onStop();
                mShowing = false;
    
                sendDismissMessage();
            }
        }
    

    以下是消息处理,弹窗显示,取消,消失。

    Private static final class ListenersHandler extends Handler {
            private final WeakReference<DialogInterface> mDialog;
    
            public ListenersHandler(Dialog dialog) {
                mDialog = new WeakReference<>(dialog);
            }
    
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case DISMISS:
                        ((OnDismissListener) msg.obj).onDismiss(mDialog.get());
                        break;
                    case CANCEL:
                        ((OnCancelListener) msg.obj).onCancel(mDialog.get());
                        break;
                    case SHOW:
                        ((OnShowListener) msg.obj).onShow(mDialog.get());
                        break;
                }
            }
        }
    

    以上就是Dialog创建和消失的过程,普通Dialog的Context必须通过Activity持有的Context才可以,不然会报异常,报错信息是没有应用token所致,而应用token是Activity持有的,系统弹窗不需要token,在WindowManager.LayoutParams中的type表示window的类型,系统层级范围在2000~2999。若传Application 的Context对象,可以使用如下所示:

      dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
    

    在Manifest声明权限可以使用系统Window

     <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    

    相关文章

      网友评论

          本文标题:Android Dialog源码分析

          本文链接:https://www.haomeiwen.com/subject/cbkauctx.html