Dialog 的 Window 创建过程

2019-12-26 15:29:27 浏览数 (1)

Dialog 的 Window 创建过程

首先在Dialog 的构造方法中创建Window实例final Window w = new PhoneWindow(mContext);

代码语言:javascript复制
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);
    }

再初始化DecorView 并将Dialog 布局添加到DecorView中

代码语言:javascript复制
/**
     * Set the screen content from a layout resource.  The resource will be
     * inflated, adding all top-level views to the screen.
     * 
     * @param layoutResID Resource ID to be inflated.
     */
    public void setContentView(@LayoutRes int layoutResID) {
        mWindow.setContentView(layoutResID);
    }

DecorView添加到Window中,并调用show方法进行显示

代码语言:javascript复制
 public void show() {
 //判断是否已经显示,并且mDecor已经初始化过就直接返回,让当前view进行显示
        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;
        }
//将DecorView添加到WindowManager中
        mWindowManager.addView(mDecor, l);
        mShowing = true;
 
        sendShowMessage();
    }

Dialoghide与dismiss方法,hide只是隐藏当前窗体,但是并没有不显示它,还是存在的,只是看不见;dismiss是完全不显示,并且当前WindowManager会移除当前DecorViewmWindowManager.removeViewImmediate(mDecor);

代码语言:javascript复制
/**
     * Hide the dialog, but do not dismiss it.
     */
    public void hide() {
        if (mDecor != null) {
            mDecor.setVisibility(View.GONE);
        }
    }
 
    /**
     * Dismiss this dialog, removing it from the screen. This method can be
     * invoked safely from any thread.  Note that you should not override this
     * method to do cleanup when the dialog is dismissed, instead implement
     * that in {@link #onStop}.
     */
    @Override
    public void dismiss() {
        if (Looper.myLooper() == mHandler.getLooper()) {
            dismissDialog();
        } else {
            mHandler.post(mDismissAction);
        }
    }
 
    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();
        }
    }

普通的Dialog只能采用Activity的Context,如果采用Application的Context就会报错。是由于没有token导致的,而token只有Activity有,所以只能采用Activity的Context,但是可以将窗体升级为系统类型的弹窗,就不会报错。 getWindow.setType(LayoutParams.TYPE_SYSTEM_ERROR); 并加入权限使用系统Window: <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

0 人点赞