Android创建窗口(二)创建Dialog

Android创建窗口(一)创建应用窗口中介绍了应用窗口的创建过程,本文将介绍一下Dialog的创建过程。创建Dialog和创建应用窗口的过程有很多类似之处,因为前一篇文件介绍了,这里相似的地方就跳过了。

首先看看如何显示一个Dialog:

Dialog dialog=new Dialog(MainActivity.this);
dialog.setContentView(R.layout.dialog);
dialog.show();

显示Dialog的代码很简单,下面我们来一步一步的分析一下。

1.创建应用窗口,首先是创建一个Activty对象,那么创建Dialog肯定也是创建一个Dialog对象了,我们先去看看Dialog的构造函数:

Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
    ...
    mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

    final Window w = new PhoneWindow(mContext);
    mWindow = w;
    w.setCallback(this);
    w.setOnWindowDismissedCallback(this);
    w.setWindowManager(mWindowManager, null, null);
    w.setGravity(Gravity.CENTER);

    mListenersHandler = new ListenersHandler(this);
}

果然和应用窗口一样,先创建了一个Window对象。

2.接下来是设置contentview:

public void setContentView(@LayoutRes int layoutResID) {
    mWindow.setContentView(layoutResID);
}

Dialog把这个任务交给了window来完成,和Activity类似,都是通过window来添加布局文件的。PhoneWindow的setContentView方法:

public void setContentView(int layoutResID) {
    // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
    // decor, when theme attributes and the like are crystalized. Do not check the feature
    // before this happens.
    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;
}

Android创建窗口(一)创建应用窗口中介绍了这一方法,这里就不多说了。这一步就是把布局文件添加到decorview上。

3.将DecorView添加到Window上并显示出来

这一步是在Dialog的show方法中完成的,来看看源码

/**
 * Start the dialog and display it on screen.  The window is placed in the
 * application layer and opaque.  Note that you should not override this
 * method to do initialization when the dialog is shown, instead implement
 * that in {@link #onStart}.
 */
public void show() {
    if (mShowing) {
        if (mDecor != null) {//如果decorview已经存在就把它显示出来,
            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();
  //对decorview进行初始化操作
    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添加到window上
    mWindowManager.addView(mDecor, l);
    mShowing = true;

    sendShowMessage();
}

show方法首先判断mDecor是否存为null,如果不为null就显示出来,如果为null,就对它初始化,并把decorview添加到window上。

以上三个步骤可以发现,创建Dialog和创建Activity的过程有很多类似之处.

我们再看看Dialog的dismiss

/**
 * 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关闭时,会通过WindowManager把decorview从window上移除。

通过源码分析Android窗口的创建:
Android创建窗口(一)创建应用窗口
Android创建窗口(二)创建Dialog
Android 创建窗口(三) 创建Toast

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容