目录
  1. 1. 屏幕如何显示的
    1. 1.0.1. Surface Flinger:
    2. 1.0.2. Window
    3. 1.0.3. ViewGroup
    4. 1.0.4. Activity
  • 2. 界面显示流程
    1. 2.0.1. 概念
    2. 2.0.2. 绘制
    3. 2.0.3. 事件分发
  • 屏幕如何显示的

    屏幕如何显示的

    首先了解几个名词

    Surface Flinger:

    HAL (硬件抽象层)之上的 ART(运行时层)的一个 C++ 封装服务 Surface Flinger 用来 UI 内容的渲染

    Window

    窗口,负责可视化内容的排版,然后将排版结果,通过WindowManager,通过进程通信的方式,去与后台服务 WindowManagerService 通信,最终递交到 Surface Flinger 来输出和呈现
    Surface Flinger 为每一个 Window 都映射了一块 Surface,来用于管理和渲染屏幕内容

    ViewGroup

    组合模式,而能够在自身内部存在更多的 View 或 ViewGroup,从结构上来看,就像套娃。为了解决window的排版负担,
    通过递归,ViewGroup排版工作:Measure、Layout、Draw,可以自身通过递归,自下往上地完成。然后 Window 可以直接拿着排版结果,去向WindowManager交差

    Activity

    视图控制器,对系统来说,本质仍是被管理的窗口。系统能够管理Activity和其他窗口的切换和通信避免直接操控Window。通过Activity可以控制 View 以想要的方式进行排版,并且在特殊状况下保存和恢复 View 的排版内容
    一个应用不可能只存在一个window,多窗口它就涉及到窗口间的切换、通信等等。如果开发者直接操控windows那就惨了。通过模板方法模式的方式将windows重新封装,并且编写一套管理窗口的任务和返回栈机制,那开发者就只需继承Activity,而得到一个简练的配置模板,从而在模板上面输入定制内容,以得到想要的结果。

    Surface Flinger 的出现是为了更加方便地完成 UI 渲染与硬件交互。

    Window 的出现是为了管理 UI 内容的排版。

    Window 不堪重负于是将责任下发到 View 身上。

    View 通过组合模式,在递归的帮助下蹭蹭蹭地完成排版工作。

    Activity 的出现是为了满足多窗口管理和傻瓜式视图管理的需要。

    界面显示流程

    概念

    Activty启动通过setContentView 设置一个内容视图
    刚也说了 windows是界面的根本,因此Activity 包含一个Window,Window是一个抽象基类,是 Activity 和整个 View 系统交互的接口。具体到 Activity中 通过PhoneWindow这个Window的实现类来控制。
    Activity中的attach()方法中

    1
    2
    private Window mWindow;
    mWindow = new PhoneWindow(this, window, activityConfigCallback);

    一个PhoneWindow 对应一个 DecorView 跟 一个 ViewRootImpl。
    DecorView 本质上是一个 FrameLayout,是ViewTree 里面的顶层布局(Activity 中所有 View 的祖先)
    ViewRootImpl 就是建立 DecorView 和 Window 之间的联系,ViewRootImpl中的ViewRootHandler 继承了Handler类,可以接收事件并分发,Android的所有触屏事件、按键事件、界面刷新等事件都是通过ViewRoot进行分发的。以下称为ViewRoot。

    绘制

    如何从setContentView到ViewRootImpl触发performTraversals 方法这里就不具体说明了 可以看这里

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    private void performTraversals() {
    ...
    int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
    int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
    ...
    //执行测量流程
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    ...
    //执行布局流程
    performLayout(lp, desiredWindowWidth, desiredWindowHeight);
    ...
    //执行绘制流程
    performDraw();
    }

    performMeasure调用view的measure方法在measure中调用onMeasure方法 onMeasure就是核心方法,也是子类中能继承覆盖的方法。
    从上到下的调用,从下到上的完成布局。完整内容

    事件分发

    详情见这里
    硬件中断 -> ViewRootImpl -> DecorView -> PhoneWindow -> Activity
    硬件(手点击了屏幕)触发 ViewRootImpl的dispatchInputEvent方法,
    如何把view 的dispatchInputEvent传递到Activity中。
    通过Activity继承的Window.Callback

    1
    2
    3
    4
    5
    6
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
    final Callback cb = getCallback();
    return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
    : super.dispatchTouchEvent(ev);
    }

    事件分三种
    dispatchTouchEvent
    分发(Dispatch)
    方法:public boolean dispatchTouchEvent(MotionEvent ev)
    其中调用了onInterceptTouchEvent()和onTouchEvent(),一般不重写该方法

    onInterceptTouchEvent
    拦截(Intercept)
    方法:public boolean onInterceptTouchEvent(MotionEvent ev)
    Activity和view 中没有这个方法,也就是说 Activity 没有拦截的能力
    ViewGroup类中的源码实现就是{return false;}表示不拦截该事件,
    事件将向下传递(传递给其子View);
    若手动重写该方法,使其返回true则表示拦截,事件将终止向下传递,
    事件由当前ViewGroup类来处理,就是调用该类的onTouchEvent()方法

    onTouchEvent
    消费(Consume)
    方法:public boolean onTouchEvent(MotionEvent event)

    在个一个view setOnClickListener后。 触发屏幕后,先会到这个Activity的 dispatchTouchEvent 然后到viewGroup的dispatchTouchEvent然后再到view的dispatchTouchEvent (前提是 viewGroup中的onInterceptTouchEvent 没有返回true的)
    可以写个小demo 试一下, 这边一个Activity 一个FirstLinearLayout继承LinearLayout(ViewGroup) 一个SecondLinearLayout继承LinearLayout(ViewGroup) 一个ThirdTextView 继承TextView(View)
    结构是FirstLinearLayout内嵌套SecondLinearLayout内嵌套ThirdTextView
    然后重写 Activity 、FirstLinearLayout、SecondLinearLayout、ThirdTextView的dispatchTouchEvent方法可以清楚的看到

    1
    2
    3
    4
    5
                     Activity: dispatchTouchEvent
       TestFirstLinearLayout: dispatchTouchEvent
    SecondFirstLinearLayout: dispatchTouchEvent
               ThirdTextView: dispatchTouchEvent
                onClick: hello

    onClick是 ThirdTextView 的onClick方法
    但是不知道为啥 除了 onClick 其他都各打印了4次
    在 FirstLinearLayout 和 SecondLinearLayout 加入onInterceptTouchEvent方法

    1
    2
    3
    4
    5
    6
                      Activity: dispatchTouchEvent
           FirstLinearLayout: dispatchTouchEvent
          FirstLinearLayout: onInterceptTouchEvent
          SecondLinearLayout: dispatchTouchEvent
          SecondLinearLayout: onInterceptTouchEvent
               ThirdTextView: dispatchTouchEvent

    意思就是 Actvity收到事件,给下面的FirstLinearLayout 然后 FirstLinearLayout收到事件问自己 要不要自己来整 不整给SecondLinearLayout整吧,然后SecondLinearLayout收到事件 也问自己要不要整,想想还是给下面整吧 然后给到了ThirdTextView,卑微的ThirdTextView发现没有下面的人能给自己整了 只能自己整了 那就自己整吧。然后就整了。执行了onClick

    话说关于打印4次的问题。
    是因为 dispatchTouchEvent、onInterceptTouchEvent的方法参数是 MotionEvent 。 down,move,up都会触发这个事件。
    在增加MotionEvent的打印后就会发现 这4次 分别是 down,move, move, up 按下的时候免不了会有所抖动的。

    说到这个 就要说说onTouchEvent 这玩意了
    onTouchEvent是在view中定义的一个方法。处理传递到view 的手势事件。手势事件类型包括ACTION_DOWN,ACTION_MOVE,ACTION_UP,ACTION_CANCEL四种事件。
    return true: 消费当前事件。
    return false: false是不消费,则向上传递给父控件的onTouchEvent;
    调用顺序是:view.onTouchEvent->viewgroup.onTouchEvent,自下往上
    不过 这里 你在view 中不实现onTouchEvent 或者onClick onTouch方法 dispatchTouchEvent就不会传递下来了。 只会在 Activity中处理, 不会下发因为下发也没意义 都没人想要处理。
    顺带执行顺序 onTouch > onClick > onTouchEvent 。

    文章作者: sovwcwsfm
    文章链接: http://sovwcwsfm.com/blog/page/displayonscreen.html
    版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 _L