Dialog的Window创建过程

  • Dialog的Window的创建过程和Activity类似
  • 首先看一下dialog构造方法里面代码
 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);
		//  创建window
        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);
    }

从上面的代码可以看出,通过final Window w = new PhoneWindow(mContext);创建window对象,接下来设置各种回调

  • 接下来要做的就是初始化DecorView并将Dialog的视图添加到DecorView中,接下来看一下Dialog的setContentView方法
public void setContentView(@LayoutRes int layoutResID) {
        mWindow.setContentView(layoutResID);
    }

直接调用的window的setContentView方法。和Activity的逻辑基本是一样的了,最后在dialog的show()方法中将DecorView添加到Window中代码如下

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;
        }
		// 添加 mDecor
        mWindowManager.addView(mDecor, l);
        mShowing = true;

        sendShowMessage();
    }

Dialog的Window创建和Activity的Window创建过程很类似,二者几乎没有什么区别。当Dialog被关闭时,它会通过WindowManager来移除DecorView: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();
        }
    }

Dialog有个特殊之处,如果用applicationContext来启动的话会报错报错如下:

android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
        at android.view.ViewRootImpl.setView(ViewRootImpl.java:809)
        at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:356)
        at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:94)
        at android.app.Dialog.show(Dialog.java:330)
        at com.demo.myapplication.MainActivity.showMyDialog(MainActivity.kt:26)
        at com.demo.myapplication.MainActivity.access$showMyDialog(MainActivity.kt:9)
        at com.demo.myapplication.MainActivity$onCreate$1.onClick(MainActivity.kt:19)
        at android.view.View.performClick(View.java:6311)
        at android.view.View$PerformClick.run(View.java:24833)
        at android.os.Handler.handleCallback(Handler.java:794)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:173)
        at android.app.ActivityThread.main(ActivityThread.java:6653)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:821)

错误的原因就是找不到token这个标识,而应用token一般只有Activity才有所以只要用Activity作为Context来显示Dialog即可。
另外系统Window比较特殊,它可以不需要token。WindowManager.LayoutParams中的type表示Window的类型,而系统Window的层级范围是2000~2999,这些层级范围就对应着type参数。系统Window的层级有很多值,对于本例来说,可以选用TYPE_SYSTEM_OVERLAY,TYPE_SYSTEM_ERROR 来指定对话框的Window类型为系统Window,并在AndroidManifest文件中声明权限从而可以使用系统Window

dialog.window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR)
//或者 dialog.window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY)
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值