Android Dialog 源码研究

来源:互联网 发布:淘宝网衬衫连衣裙女 编辑:程序博客网 时间:2024/05/24 05:20

Android Dialog 源码研究

在阅读Dialog源代码之前,我给自己提出了4个问题,带着这4个问题的疑问,我才开始看源码,这样的好处能让阅读中有了侧重点,往往能更快的理解某些代码的意图,这也正是为什么总说提出问题本身,要比解决问题困难

我下面要思考的4个问题是:

  1. Dialog到底是什么东西?
  2. Dialog到底是如何被显示到屏幕上?
  3. Dialog的构建,有哪些类参与,都有什么作用?
  4. Dialog的主要API,是如何让Dialog发生变化的?

首先,我们先得搞清楚Dialog的继承体系,整体把握下Dialog的设计结构。下图即为Dialog的继承体系树结构:

这里写图片描述

对Dialog有了整体认识之后,我们带着上面4个问题来阅读源码,并从中找到问题的答案。当把所有问题都解决掉之后,Dialog的源码也基本上都看懂了,对之后的自定义Dialog有很大帮助。

Dialog本质上是什么?

先看Dialog类的源码的构造函数,发现不同的构造内部最后都会调用3个参数的构造Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper)

我们来看下,该构造中形参的作用,第一个参数上下文,就不用解释啦。

1.themeResId

a style resource describing the theme to use for the window, or 0 to use the default dialog theme。

在源码中,关于themeResId的解释如上,说明该入参设置了window的主题,看到这里我好像明白了Dialog到底是个什么玩意,我们再看下源码是如何使用这个参数的:

其中,createContextThemeWrapper在源码中是固定值true,因此这个构造的if语句一定会被执行,我们可以看到context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);表示默认情况下会自动引用系统主题。

很显然,通过重写某些参数会创建自定义样式的Dialog,请读者自行测试。

2.createContextThemeWrapper
由构造直接传入true值,没什么好讲的。

3.Window
在前面分析的构造方法中,后面还有一小段代码非常重要,它传达了非常强烈的信息:Dialog其实本质上就是一个Window

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

注意到,这里new出一个PhoneWindow对象并赋值给Dialog的成员变量mWinodw。这里,设置了3个监听器,从而有了上面的Dialog继承体系结构,当看到w.setGravity(Gravity.CENTER);我立刻明白了,原来这个PhoneWindow其实就是我们的Dialog,这也解释了为什么Dialog为什么会显示在屏幕的正中间。

当然,现在直接说Dialog就是Window可能你还不信,那接下来问题2的答案就让你确信无疑啦。

Dialog到底是怎样被显示到屏幕或移除的?

我们已经知道,要想Dialog显示出来,就应该调用show()方法,而移除时要调用dismiss()

1.show( )

从上面部分代码来看,show( )方法还真做了不少事,而onStart( )只有一行代码,可以复写来进行一些初始化工作。

其中,最引人注目的是mDecor对象,前面说过mWinodw就是new出来的PhoneWindow,根据Window类的官方描述:

Abstract base class for a top-level window look and behavior policy. An instance of this class should be used as the top-level view added to the window manager. It provides standard UI policies such as a background, title area, default key processing, etc.The only existing implementation of this abstract class is android.view.PhoneWindow, which you should instantiate when needing a Window.

mDecor就是获取Window的View形式下的对象。这样可以方便WindowManager通过方法addView(...)把内容绘制到Window上,这样就完成Dialog的显示,当然具体是如何被添加绘制到屏幕上,这牵涉到底层UI绘制原理,不在本文分析范围内。

2.dismiss( )
下面,我们再来看看Dialog如何从屏幕上移除的,主要看方法dismiss()的实现。

因为,在dismiss()中增加了线程是否为主线程的条件判断,因此是可以在任何线程中调用dismiss()来移除Dialog。

综上所诉,我们可以得出:其实Dialog就是一个Window,显示与移除都是通过窗口管理器WindowManager完成的,如果需要完全自定义Dialog的内容,则需要通过Window设置其ContentView,再通过Window.getDecorView()获取到这个View,最后通过WindowManager进行添加与移除。

未完待续~~~

原创粉丝点击