乐趣区

关于android:理解安卓的视图体系结构

原文链接 了解安卓的视图体系结构

当咱们想要写一个页面的时候,通过一个 Activity,而后调用其 setContentView 办法,把一个布局文件当作一个参数传递过来,而后一个页面就好了,然而除此之外,咱们还须要与一些组件打交道,比方像 Window,WindowManager,那么这些货色到底 与咱们的页面布局有什么关系,明天就来学习一下,以便对整体窗口有个更分明的认知。

布局是一颗 View tree

先从一个最简略的例子登程,平时咱们写一个页面,都从一个布局文件登程。这其实是在构建一个 View tree,为啥肯定是 tree 呢,因为咱们的布局文件,无论有如许的简单,都是从一个根(通常是一个 ViewGroup 对象)开始的,父布局外面再写子布局,比方这样的:

<LinearLayout id="app_root">
  <TextView id="label"/>
  <Button id="submit"/>
</LinearLayout>

这会造成一个树状构造:

| app_root<br/>
   |- label<br/>
   |- submit<br/>
作为一个开发者,写布局是咱们再相熟不过的了,次要就是用所相熟的各种 Layout 和 View 一起来构建想要的页面。

所写的布局,最终会生成一颗 View tree,是一个树状的数据结构,每一个节点都是一个 View 对象(ViewGroup 和 View)。因而,布局优化的一个是感觉重要的点就是要先缩小 View tree 的深度(也即平时所说的缩小布局的嵌套),再想方法缩小广度(缩小个数)。

那么,咱们写的布局的父布局又是哪里呢?这就又波及两个货色,一个叫做 decorView 和 contentView 的货色。

DecorView 与 ContentView

咱们平时所见的屏幕窗口的根布局是一个叫做 DecorView 的货色,它是咱们通常意义上整个屏幕的根节点,它蕴含了下面的 Status bar 和下方的 Navigation bar,以及属于应用程序的两头局部。它的源码门路是 frameworks/base/core/java/com/android/internal/policy/DecorView.java。它是一个实在的 view,它是 FrameLayout 的子类。

它上面有一个 id 为 android.R.id.content 的 FrameLayout,咱们平时在 Activity 中调用 setContent 时所传过来的布局文件所生成的 View tree 都是增加在这个 FrameLayout 上面,所以,通常对于咱们一个 Activity 来说,这个 FrameLayout 是间接意义上的根节点,咱们所写的布局都是增加它上面的。

ContentView 所引申进去的奇技淫巧

布局优化技巧

首先,一个是布局的优化技巧,能够缩小 View tree 的层级:如果你写的布局中根节点也是一个 FrameLayout,那么能够间接用 merge 节点,把子 view 全副都间接加挂到后面提到的零碎创立的 Activity 的根布局下面。

<merge>
  <Text />
  <Button />
</merge>

这能够把 View tree 缩小一个层级(深度减 1)。

页面内即插即用的弹窗

每个 Activity 都被回挂在一个 id 是 android.R.id.content 的 FrameLayout 上面,利用这一点,能够做一些即插即用的弹窗,即插即用的意思是,不必写在布局外面,而且显示的工夫是不固定的,可能很多时候都不显示,在某个特定的逻辑或者工夫才显示。就好比某些电商特定节日的弹窗一样,这种货色,一年也显示不了几回,如果间接增加在布局外面(哪怕你用 ViewStub),不够优雅,毕竟不是惯例逻辑下会呈现的页面,这时能够利用 content 来做一些即时弹窗:

FrameLayout container = activity.findViewById(android.R.id.content);
View pop = <create or inflate your own view>;
container.addView(pop);

只有你能取得到 Activity 的实例(这个并不难),那么就能够十分优雅的增加弹窗,逻辑代码和布局文件都会相当独立,甚至能够用插件模式来异步加载。再进一步,如果 增加一个 WebView,那么就能够做得更加的前端化,实时化和定制化,好多电商的弹窗就是这么干的。

Window 与 WindowManager

作为利用开发者,咱们看一个 View tree 其实就是一坨布局,这是站在一个十分小的角度去看的,但如果站在整体零碎架构角度来看的话,就会发现应用程序所在的 view tree 仅是零碎可视化窗口架构中的末端,View 只是用来构建视图的根本砖块而已。对于整体 View tree 是如何渲染的,何时渲染,这就波及到了整体零碎架构层面的重量级组件了。

对于现化代的视图窗口架构(Modern GUI),都有一个 window server,作来治理视图窗口的外围组件,比方 X11,Android 当中也不例外。在 Android 外面,WindowManager 就是专门用于治理视图窗口的,它是零碎级别的 server 叫 window manager server 是一个零碎级别的常驻过程,由 init.rc 启动。而 Window 则是一个根本的窗口的逻辑上的形象。对于 Window 以及 WindowManager 自身就是相当大的话题,都能够独自写本书,这时不做过多的探讨,对于咱们利用开发者来说,理解一下根本的常识就够用了。

每一个 Activity,都有一个 Window 对象,所有所有与 GUI 无关的事件,都委派给了 Window 对象,Actvity 自身并不参加 GUI 的具体流程,比方像下面提到的 DecorView,ContentView 等 View tree 的构建与治理,View tree 的渲染,以及像事件的解决,都是 Window 对象解决的。Window 是 WindowManager 的根本对象,与其 server 之间通过 IPC 通信,Window 是供应用程序端应用的,其实真正所有都把握在 window server 手中。Activity 和 Dialog 应用的对象都是 PhoneWindow,它在 frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java,Window 对象会具体负责创立像 DecorView 之类的一些基础设施。最为要害的一个办法就是其 PhoneWindow#installDecor()办法,这个办法外面会先调用 generateDecor()创立 mDecor,它就是后面讲到的 DecorView 对象,再通过 generateLayout()创立 mContentParent 对象,它就是后面讲到的 id 是 android.R.id.content 的那个 FrameLayout,Activity 或者 Dialog 通过 setContentView 送过来的 View tree 就是加在它的上面的。

WindowManager 是一个接口(Android 零碎的代码接口用的特地多,很多要害的架构层面的组件 都是接口,理论应用的都是其一个实现。)理论应用的是 WindowManagerImpl 对象,而它也没干啥,它把事件 又委派给另外一个叫做 WindowManagerGlobal 的对象,这个 WindowManagerGlobal 则是 GUI 端的最初一站,它负责与 wms(WindowManagerServer)通信。它在 frameworks/base/core/java/android/view/WindowManagerGlobal.java

须要留神 WindowManagerGlobal 是一个单例,也就是说每一个应用程序(严格来说是每一个过程只有一个实例,但安卓下面带有 GUI 的应用程序只能存活在一个过程,所以能够了解 为一个应用程序)只有一个实例,所以它治理着一个应用程序中的所有的 View tree。从它的成员中便可看出,它有一坨 ViewRootImpl 对象(一个列表),而每一个 ViewRoot 对象治理着一颗 View tree。

最为要害的一个办法就是 WindowManagerGlobal#addView,每一个 Window 的持有者对象(如 Activity 或者 Dialog)都是通过这个办法将其 DecorView 对象增加给 WindowManager 的。addView 办法,会先创立一个 ViewRootImpl 对象,而后把要增加的 view 以及刚创立进去的 ViewRootImpl 都放进它的列表中,最初再调用 ViewRootImpl#setView(view),这就把几大要害对象建设好了连贯,接下来的事件就归 ViewRootImpl 了。这里还有一个相当要害的对象,那就是 LayoutParams,WindowManagerGlobal 也有一个列表外面存着每个 Viewtree 根节点(也就是 Decor view)的 LayoutParams。

ViewRootImpl 又是个啥

Window 是从手机零碎角度来对待的窗口的概念,而 View tree 则是从应用程序角度构建 GUI 页面的概念,view tree 是 Window 的一部分,Window 对象持有 mView,而这个 mView 就是下面提到的 DecorView,也即是 View tree 的根节点。这里又要波及另外一个对象 ViewRootImpl,它并非是 View tree 的一部分,尽管名字上比拟容易混同,因为它并不是 View 的子类,所以它不是任何一个 View tree 的节点,它的职责是治理 View tree,像渲染以及事件派发,都是 Window 间接通过 ViewRootImpl 来进行的。在代码中理论应用的是 ViewRootImpl 对象,它实现了 ViewParent 接口。

所以,ViewRootImpl 对象是值得细细钻研的,因为实际上是它在治理着 GUI 零碎 –view tree 的治理,渲染的三大步(measure, layout 和 draw)以及事件的派发,最源头的逻辑都在这个对象外面,当然 它也是非常复杂的,源码大略有 1 万行左右。

ViewParent 又是个啥

它是一个接口,行使的职责是治理子 View,也就是说在 View tree 当中治理子 View 的行为的汇合便是 ViewParent 接口。View tree 的节点都是 View 的子类,所以,你看 ViewRootImpl 实现了 ViewParent 接口,它是负责管理 Window 外面的 View tree 的。另外一个就是 ViewGroup,ViewGroup 是 View 的子类,所以它是 Viewtree 的一部分,父节点都是 ViewGroup,它外围就两样货色一个是子 View 的列表,另外就是 ViewGroup 也实现了 ViewParent 的接口,因为它也要治理它的子节点(也即子 View)。

Activity 到底是个啥货色

它是零碎的四大外围组件之一,如果想构建 GUI 页面,则 Activity 是绕不开的。如果再具体一点,Activity 是一个零碎给你的交融了利用生命周期治理,组件级别复用(Intent 相干)和窗口治理的组件,生命周期也即 ActivityManager 干的事件,它通过 Activity 的回调通知你;而 GUI 则是通过 Activity 的 Window 对象帮你实现(Activity 的布局和事件的解决都是委派给其持有的 Window 对象来解决)。

如果,把 Activity 的 Window 对象拿掉,那么它跟一个 Service 组件就基本上没有差异了。如果把 Activity 的 Intent 相干拿掉,那么它跟一个 Dialog 就没啥区别了。

Fragment 又是个啥

坦白说,Fragment 是 Google 挖的一个大坑,这玩意儿不合乎 Android 的外围设计思维,因为 Android 入世的时候并没有它,是起初 Google 跟水果平台抄来的一个不三不四的货色,后果全是坑。在它刚进去的一些年,Google 竭力的举荐应用 Fragment,然而近一两年,又不举荐了。

Fragment 实质上就是一个强加了生命周期函数回调的 View,因为显示 Fragmeng 时,都是把它替换一个 View 或者增加到一个 ViewGroup 下面,所以它就是一个 View,或者说一个 View tree 中的节点。然而强加了生命周期的回调。光是这两点,其实也没有啥,毕竟生命周期对于 View 是重要的,个别时候咱们要在 onResume 与 onPause 之间才让 View 处于 active 状态。

Fragment 最大的问题在于它的异步机制和状态复原机制,也就是说用 FragmentManager#commit 了当前,具体啥时候 Fragment 会真正显示进去,咱们是无法控制的,这是相当的坑;它的状态复原机制就更加的坑,状态复原这个货色如果全让程序员来负责也还好,就像 Activity 的设计一样,然而如果框架帮你做了一些事件,但又不残缺,这就坑了,对于状态复原的坑能够参考这篇文章来具体的理解。

DialogFragment

这个实质上是 Dialog,然而被包了一层 Fragment,所以它会有 Fragment 的个性,然而 Window 和 View tree 则是属于 Dialog 的。

留神:FragmentTransaction#add(Fragment fragment, String tag)有一个办法是不须要提供父布局,这是为没有惯例布局筹备的,因为无奈把布局增加到 Activity 的现有 View tree 之中。个别状况下,咱们是不会应用这个办法的,目前看仅在 DialogFramgment 中应用这个办法,那是因为 Dialog 自身有 Window 和 view tree。

不在 Activity view tree 外面的窗口控件

一般来讲,咱们想要显示的页面都会放进布局外面,也就是说大部分时候咱们的页面都由 Activity 的 view tree 来实现。然而有些非凡的场景,却不是在 view tree 外面,比方弹窗,像 Dialog,PopupWindow 以及 Toast,这些货色个别是用于弹出式的页面,由特定的逻辑触发,它与惯例页面最为显著的区别就是,它们与 Activity 的 Window 和 View tree 是独立开来的,它们并不是增加在以后 Activity 的 view tree 下面的。它们本人有独立的 view tree,或者换句话说,它们是独立的 Window。

咱们这里重点探讨它们与 Window 和以后 Activity 之间的关系,至于它们的根本应用办法,能够参阅其余文章。

Dialog

这里不说根本应用办法。

通过查阅源码,能够发现 Dialog 与 Activity 的实现相当相似,它外部也有一个独立的 Window,也是通过 WindowManager#addView 把其 ContentView(咱们提供的布局)加到屏幕下来的。因而,它与 Activity 也是互相独立的,是两个 Window,两棵 View tree。Dialog 类外面还有 getActionBar,OptionsMenu 等相干的办法,但仿佛在理论应用当中比拟少用到。

Dialog 最为外围的两个办法一个是其构造方法,这其中会创立 Window 对象,另外一个就是 #show,外面能够看到,它是通过 WindowManager#addView()办法,来把它的 mDecorView 增加到窗口体系当中的,这与 Activity 其实是一样的。

为啥显示 Dialog 肯定须要 Activity,个别 Context 却不能够

应用过 Dialog 的人都晓得,创立 Dialog 时肯定要传递 Activity 为其参数,只管构造方法外面申明的是 Context。后面提到,Dialog 有本人的 Window 和 View tree,实践上它跟 Activity 是没有关系的。

如果,用一个非 Activity 作为 Context 传给 Dialog,报错,是 WindowManager 抛出来的异样,说:

android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
        at android.view.ViewRootImpl.setView(ViewRootImpl.java:1093)
        at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:409)
        at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:110)
        at android.app.Dialog.show(Dialog.java:342)

而 Dialog#setOwnerActivity(Activity)办法在创立 Dialog 之后再把相干 Activity 塞过来,也是不行的,必须传入的 Context 参数要是一个 Activity 实例才能够。

最后认为,能够从它的构造方法中看出为啥肯定须要 Activity,就是因为须要 theme. 但其实并不是,因为 theme 是能够通过 resource id 传进去的。

关键点仍在于 ViewRootImpl 对象,因为这个异样是 ViewRootImpl 在其 setView 办法中抛出的,后面讲过,向一个 Window 增加布局最终会走到 WindowManagerGlobal#addView,而它又是通过 ViewRootImpl#setView 来做具体事件 的,这个办法外面,会先获取以后的 WindowSession,而后再把以后的 Viewtree 转化为窗口对象,增加给 wms。所以最外围的中央还得看 WindowManagerService#addWindow()这个办法,这个办法也相当之简单,然而还是能大略看懂它的意思。

艰深的来了解这是安卓零碎自身加的限度,也就是说窗口自身也是有逻辑关系的,能够简略了解 为树状关系,一个 Activity 是主 Window,而由此 Activity 衍生进去的属于此 Window 的子 Window,因而在增加子 Window 的时候,必须 要晓得它从属于哪个父 Window,因而,你必须 传 Activity 实例给 Dialog 的构造方法,因为只有 Activity 才是有主 Window 的。然而这个具体的逻辑连贯却比拟奇怪,从下面的过程形容来看,WindowManager#addView 到 ViewRootImpl#setView,其实,都没有明确的把父 Window,也就是 Activity 的 Window 传进来,那么 WindowManagerService 又是从哪里去找这个父 Window 呢?

WindowManagerService#addWindow 办法,并没有传递父 Window 参数 进来,那就只能是它从传进来的参数取得的。这里一个很重要的货色就是 token,它是一个 IBinder 对象,它是一个 Window 的标识,它存在 Window 的 attris 对象外面,这个就是 WindowManager#LayoutParams 对象,它的作用就是存储 Window 的特征参数,比方你要扭转 Window 的一些个性(艰深来说就是定制一下 Window),那么通过扭转 LayoutParams,就能够了。这个其实不难理解,咱们对 View 不就是通过其 LayoutParams 来扭转 View 的特征参数 么。都 是一样的。

Dialog 对象在 show()时会把其 mDecor 增加到 WindowManagerService 中去,其并未传父 Window,只传了一个 LayoutParams 过来,其实玄机也就在 LayoutParams 之中,窗口的 token,父 token(标识着父窗口)以及像窗口的 type 都是在 LayoutParams 中。那么这个 LayoutParams 是哪里创立的呢?它是来自于 Window 对象的,而 Dialog 的 mWindow 成员实例是在结构时创立的,创立的是一个 PhoneWindow 对象,并且把结构 Dialog 传进来的 Context 对象传给了 PhoneWindow 的对象,LayoutParams 对象则是通过 mWindow.getAttributes()得来的。因而啊,能够判定,PhoneWindow 在生成 LayoutParams 时,会从传给其结构的上下文对象 mContext 中获取一些信息,如窗口的类型或者父窗口信息,而只有 Activity 对象才有窗口信息,并且能够作为父窗口,而一般 的 Context 对象是没有窗口的,由此能够解答咱们的纳闷了。

也能够显示独立于任何 Activity 的 Dialog

窗口是有很类型的,WindowManagerService 为了方便管理,所以针对 Activity 及其从属于子窗口(Dialog 和 PopWindow)做了相似 tree 构造的逻辑上的整顿,所以一般 的 Dialog 必须要能找到其主窗口(或者叫父窗口)。

但其实,咱们常常能见到一些十分牛逼的 Dialog,能够显示 在任何 Activity 之上,如电源没了,或者音量调节,等等。这些是叫作 system dialog,须要非凡权限 能力显示进去的。治理来了解,零碎级别的组件 才有权限 显示 system dialog。

其实,想一想也正当,作为一个应用程序,你在本人的生命周期内,显示内容给用户足够的信息就能够了。当用户来到了你的利用,你也没有必要再显示 Dialog 了。

:利用在后盾时,想在前台显示信息有其余的形式,如 Notification 等,这属于另外的话题,不做过多探讨。

能够弄个全屏的 Dialog 吗?

一般来讲呢,Actiivty 都是全屏的,Dialog 个别是非全屏的,能够把一个 Activity 弄成非全屏的,长的像 Dialog 一样,当成 Dialog 来应用,就在设置 Activity 的 Theme 时,用 Theme.Dialog 就能够了。

那么,反过来搞可不可呢,就是可不可以把惯例的 Dialog 弄成一个全屏的呢?

从 Dialog 的实现上来看,它有 Window 对象,甚至连 Actionbar 和 OptionsMenu 都有,所以从实现上来看,Dialog 并不一定非要像咱们平时所应用的那样是一个对话框,它能做的事件 不比 Activity 少。默认 Dialog 的 style 就是一个平时的对话框,但其实,设置不同的 style,就能够失去全屏的 dialog。

     private void showFullscreenDialog() {
        // Theme_Material_NoActionBar_Fullscreen is real full screen, i.e. hide the status bar.
        Dialog dialog = new Dialog(this, android.R.style.Theme_Material_NoActionBar);
        dialog.setContentView(R.layout.fullscreen_dialog);
        dialog.findViewById(R.id.okay).setOnClickListener(view -> {dialog.dismiss();
        });
        dialog.show();}

:这里有点歧义,全屏意思是指铺满整个父 Activity,严格意义上的全屏是要把状态栏也要暗藏掉。

PopupWindow

PopupWindow 是一个独立的类,并不是 View 的子类,因而,它跟惯例的 widget 不一样,无奈间接增加到现有的 View tree 之中,这也导致它的实现形式比较复杂。

PopupWindow 它并没有创立 Window 对象,然而它有一个相似于 Window 对象的 DecorView 的货色,它的根节点是一个叫做 PopupDectorView 的货色,其实是一个 FrameLayout,咱们让 PopupWindow 显示的布局就是加在这个 PopupDectorView 上面。最重要的两个办法一个是 preparePopup() 这个办法会创立根节点 PopupDecorView,而后把咱们须要显示的 mContentView 以及还有一个 PopupBackgroundView(也是一个 FrameLayout,包裹在要显示的 ContentView 里面),放在 PopupDecorView 的上面,所以实在的构造是根节点是 PoupDecorView,包了 PopupBackgroundView,再包上要显示的 mContentView,一共三层。

另外,一个办法就是 invokePopup,外围逻辑是调用 WindowManager#addView,把 mDecorView 增加到窗口零碎中以显示进去,前面的过程跟下面提到的 Dialog 的显示过程是一样的。那么 PopupWindow 又是如何找到 Activity 的主 Window 的呢?答案还是在 LayoutParams 中,办法 preparePopup()的参数 是 LayoutParams,如后面所述 LayoutParams 是最终会传递给 WindowManagerService 的,而这外面就蕴含了主窗口的信息。而这个 LayoutParams 对象是通过办法 createPopupLayoutParams()得来的,而这个办法的参数 是一个 IBinder 对象,咱们晓得这个 IBinder 对象就标识着一个主窗口。那么 PopupWindow 的 IBinder 对象又从何而来呢?是通过 View.getWindowToken()得来的,PopupWindow 的显示 办法都要提供一个 View 如 showAsDropDown,外面的参数是一个 View,而这个 View 必须 是已显示的 View tree 中的一个节点,当初应该晓得一个窗口有一颗 View tree,那么此 View tree 中的节点必定 晓得本人属于哪个窗口啊,由此便找到了主窗口。

另留神,PopupMenu,也是基于 PopupWindow 的,只不过弄成了 Menu 的样子(其实就是一个 ListView)。

能够弄个全屏的 PopupWindow 吗?

当然 能够,只须要在结构 PopupWindow 时传入 MATCH_PARENT 作为其宽和高就能够了,不过这样做当前前面再抉择哪种 show 形式就不影响了,都是铺满 Activity 来显示。

     private void showFullscreenPopup() {final View content = LayoutInflater.from(this).inflate(R.layout.fullscreen_dialog, null, false);
        PopupWindow popup = new PopupWindow(content, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        final View anchor = findViewById(R.id.fullscreen_popup);
        // Key is the width and height passed to constructor, show does not affect anything.
//        popup.showAtLocation(anchor, Gravity.NO_GRAVITY, 0, 0);
        popup.showAsDropDown(anchor, 100, 200);
        content.findViewById(R.id.okay).setOnClickListener(view -> {popup.dismiss();
        });
    }

Toast 又是个啥

这个大家都十分相熟了,每天都用到,用以给出一些十分弱的提醒。

它其实也是有独立 Window 的。Toast 类自身比较简单,但它也是有一个专门的 Server 的叫 NotificationManager,Toast 也是一个客户端,间接做工作的是另一端的服务,这也是为何即便咱们的利用退到了后盾仍然能够 show 一个 Toast。咱们用的最多的就是让其显示一段文字,但其实那只是它的一个十分根底的用法。从 Toast 的办法就可以看进去,它是能够承受一个 View 的,所以把一个布局的根节点传进去,那这个布局不就能够显示了么?

Toast 能够显示简单布局吗?

尽管,通常咱们都是应用 Toast.makeText 办法,但这并意味着它只能显示纯文字,它是可发接管一个 View 作为其 Content 的,就通过其 setView 办法:

     private void showComplexToast() {Toast toast = new Toast(this);
        final View dialog = LayoutInflater.from(this).inflate(R.layout.fullscreen_dialog, null, false);
        toast.setDuration(Toast.LENGTH_LONG);
        toast.setView(dialog);
        // This does not work, Toast cannot receive focus, i.e. it won't receive events from WMS
        dialog.findViewById(R.id.okay).setOnClickListener(view -> {toast.cancel();
        });
        toast.show();}

不过呢,尽管 Toast 能够展现更为简单的布局,然而它是无奈接管用户事件,也就是说它是无奈解决点击事件的,你想有用户交互的话,是不能够的。

如此,如果你想显示一个相似 Toast 的,然而能够交互 的,那只能用 PopupWindow 或者 Dialog 来模仿,但这又只能是在利用在前台时显示;如果在后盾时,又想要有交互行为,那只能用 Notification 和 PendingIntent 了。

综合论断

说了这么多,心愿还没有看晕,总结一下:

  1. Window 也是有构造 关系的,相似于 View 一样,像一样 tree
  2. 每一个 Window 都有一颗 View tree,DecorView 是其根节点
  3. ViewRootImpl 是用来治理 View tree 的
  4. Dialog 和 PopupWindow 能够用以显示铺满 Activity,甚至全屏的 View
  5. Toast 也能够展现简单布局

实战倡议

Activity 应该只用于显示一个页面内的次要的,逻辑上都能够触达的布局,比方一上来用户就可见的所有货色,以及惯例操作能够触发的(如折叠开展等)。

Activity 的 View tree 要尽可能的小,这样能力保障最好的渲染性能,其余的,很多一次性的,即插即用的,鲜有逻辑才会有触发的,这种布局,要尽可能的独立于 Activity 的 View tree 之外,以保障其布局和逻辑上的独立,也更不便保护,更能缩小 Activity 的 view tree 的体积。因为 Dialog 和 PopupWindow 也能够铺满整个 Activity,所以,像一些用户疏导,新人疏导,经营流动,分享,等等一些惯例逻辑走不到的页面,都能够思考用 Dialog 和 PopupWindow 来实现。

原创不易,打赏 点赞 在看 珍藏 分享 总要有一个吧

退出移动版