Android UI

View的绘制流程总结以及view.post()和handler

2021-08-18  本文已影响0人  编程的猫

概览

  1. View的坐标图解
  2. View的绘制流程
  3. view.post()和handler.post()
  4. ViewGroup事件分发概述
20181017154355554.png

View的绘制流程:

performResumeActivity() —> windowManager.addView() —> windowManagerImpl中addView() —> mGlobal.addView() —> viewRootImpl.setView() —> requestLayout() —> scheduleTraversals() —> TraversalRunnable的回调中doTraversal() —> performTraversals()

在performTraversals()中执行performMeasure()、performLayout()、performDraw(),并且回调getRunQueue().executeActions(mAttachInfo.mHandler);

在Activity的onResume生命周期执行完毕后,获取windowManager对象调用addView()将decorView添加到ViewRootImpl中,在WindowManagerGlobal中创建viewRootImpl调用setView(),然后依次调用requestLayout(),scheduleTraversals(),doTraversal(),performTraversals(),然后一次执行performMeasure()、performLayout()、performDraw()完成View的绘制。

performMeasure流程.png
    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
        int specMode = MeasureSpec.getMode(spec);
        int specSize = MeasureSpec.getSize(spec);

        int size = Math.max(0, specSize - padding);

        int resultSize = 0;
        int resultMode = 0;

        switch (specMode) {
        // Parent has imposed an exact size on us
        case MeasureSpec.EXACTLY:
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size. So be it.
                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent has imposed a maximum size on us
        case MeasureSpec.AT_MOST:
            if (childDimension >= 0) {
                // Child wants a specific size... so be it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size, but our size is not fixed.
                // Constrain child to not be bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent asked to see how big we want to be
        case MeasureSpec.UNSPECIFIED:
            if (childDimension >= 0) {
                // Child wants a specific size... let him have it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size... find out how big it should
                // be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size.... find out how
                // big it should be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }
        //noinspection ResourceType
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }

子View自身的大小和模式是由父布局的模式和子View自身的大小综合确定的,以子View的width为例:伪代码

int childWidthDimension;
int size = Math.max(0 , parentWidth - parantPadding);
int childWidth = 0;
int childMode;
if (父布局的mode == MeasureSpec.EXACTLY){

        if(childWidthDimension >= 0){

              childWidth = childWidthDimension;
              childMode = MeasureSpec.EXACTLY;

        } else if(childWidthDimension == LayoutParams.MATCH_PARENT){
        
               childWidth = size;
              childMode = MeasureSpec.EXACTLY;
              
        }else if(childWidthDimension == LayoutParams.WRAP_CONTENT){
        
               childWidth = size;
              childMode = MeasureSpec.AT_MOST;
              
        }

}else if (父布局的mode == MeasureSpec.EXACTLY){

        ... 同理 ...

}

view.post()和handler.post():

view.post()添加一个任务到view的任务队列mRunQueue中,在view的绘制流程performTraversals()方法中调用getRunQueue().executeActions(mAttachInfo.mHandler)执行,拿到mAttachInfo.mHandler,mAttachInfo是在主线程,所以view.post()添加的任务会在主线程中执行。(Android是handler队列中事件驱动的,view.post()任务会在view的测量布局之后执行)。

Handler.post()添加一个任务到队列,排队执行,最终执行的线程不一定是主线程,取决于looper创建的线程。

ViewGroup事件分发概述

事件分发:
  • ViewGroup不拦截的情况下,由ViewGroup下发到子View的onTouchEvent()方法,如果在子View的onTouchEvent()方法中返回true拦截了事件,那么该次事件序列的后续事件都由子View接收。
  • 如果子View不消费传递到onTouchEvent()方法中的事件,那么事件序列将沿着事件分发链向上回传到ViewGroup的onTouchEvent()方法。
  • ViewGroup拦截的情况下,该序列的事件将由ViewGroup的onTouchEvent()方法接收,并且事件不会向下传递给子View。
    如果要让事件继续往下传递,必须调用super.dispatchTouchEvent()方法。
  1. ViewGroup的dispatchTouchEvent()返回true,事件不往下传递 < 事件往下传递必须调用super.dispatchTouchEvent()方法 >,但是会陆续调用自己dispatchTouchEvent()方法;ViewGroup的dispatchTouchEvent()false,事件不往下传递,但是只会调用一次自己的dispatchTouchEvent()方法。
  2. ViewGroup的dispatchEvent() super.dispatchTouchEvent(),方法onInterceptTouchEvent()返回true或者false,事件不往子View传递,但是会调用自己的onTouchEvent() < View调用自身的onTouchEvent()方法,必须先执行dispatchTouchEvent()方法 >。
  3. ViewGroup的dispatchTouchEvent()方法和onInterceptTouchEvent()方法不做处理,onTouchEvent()方法返回true,事件不会往下传递;onTouchEvent()方法返回false,事件会往下传递一次。
  4. ViewGroup的dispatchTouchEvent()方法、onInterceptTouchEvent()方法和onTouchEvent()方法不做处理,子View的dispatchTouchEvent()方法返回true,事件会往下传递,但是不回调用子View的onTouchEvent()方法;子View的dispatchTouchEvent()方法返回false,事件会往下传递一次,但是不回调用子View的onTouchEvent()方法。
  5. ViewGroup的dispatchTouchEvent()方法、onInterceptTouchEvent()方法和onTouchEvent()方法不做处理,子View的dispatchTouchEvent()方法调用super.dispatchTouchEvent()方法:onTouchEvent()方法返回true,事件往下传递,且依次会调用ViewGroup的dispatchTouchEvent()、onInterceptTouchEvent()、View的dispatchTouchEvent()、和View的onTouchEvent(),但是不会调用ViewGroup的onTouchEvent()方法;onTouchEvent()方法返回false,事件往下传递,且依次会调用ViewGroup的dispatchTouchEvent()、onInterceptTouchEvent()、View的dispatchTouchEvent()、和View的onTouchEvent(),以及ViewGroup的onTouchEvent()方法,但是事件只会往下传递一次。
上一篇下一篇

猜你喜欢

热点阅读