View的绘制流程

文章目录

  • 前言
  • ViewRootImpl 简单介绍
  • performMeasure
    • MeasureSpec
    • 测量大小源码分析
  • performLayout
  • performDraw
  • setWillNotDraw
  • 多次invalidate会怎么样
  • 参考

前言

本文基于Android 21源码讲解
我们的首先简单了解Activity创建流程

//ActivityThread.java

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
     
	//创建一个activity对象,内部会调用activity的oncreate函数
	Activity a = performLaunchActivity(r, customIntent);
	//后文再继续分析 
	handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed);
                    
}

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
     
 

        Activity activity = null;
        
        //Activity的context真正实现类,Activity虽然继承了Context类但是并没有实现具体方法而是交给自身的Context mBase;属性实现
    	Context appContext = createBaseContextForActivity(r, activity);
    	   
        java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
        //创建一个activity对象
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
                

        //调用attach构建内部的一个mWindows属性对象实例  
        activity.attach(appContext, this, getInstrumentation(), r.token,
                r.ident, app, r.intent, r.activityInfo, title, r.parent,
                r.embeddedID, r.lastNonConfigurationInstances, config,
                r.voiceInteractor);
	//调用activity的oncreate函数
	 if (r.isPersistable()) {
     
		mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); 				
    } else {
           
     	mInstrumentation.callActivityOnCreate(activity, r.state);
  	}        

   return activity;
}   

我们继续看activity,attach函数

//Activity.java
class Activity{
     
    private Window mWindow;
    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, IVoiceInteractor voiceInteractor) {
     
        //给自己的Context mBase;赋值    
        attachBaseContext(context);
		//我继续跟如PolicyManager
        mWindow = PolicyManager.makeNewWindow(this);
		
	//WindowManager的赋值其实现类为WindowManagerImpl
		mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),mToken, mComponent.flattenToString(),(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
       	
        mWindowManager = mWindow.getWindowManager();
	}
}
//PolicyManager.java

public final class PolicyManager {
     
    private static final String POLICY_IMPL_CLASS_NAME =
        "com.android.internal.policy.impl.Policy";
    
    private static final IPolicy sPolicy;
    
    static {
     
			//反射创建com.android.internal.policy.impl.Policy对象
            Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
            sPolicy = (IPolicy)policyClass.newInstance();
        
    }
    
    private PolicyManager() {
     }
   
    public static Window makeNewWindow(Context context) {
     
    	//sPolicy实现类为com.android.internal.policy.impl.Policy
        return sPolicy.makeNewWindow(context);
    }
    public static LayoutInflater makeNewLayoutInflater(Context context) {
     
        return sPolicy.makeNewLayoutInflater(context);
    }
    public static WindowManagerPolicy makeNewWindowManager() {
     
        return sPolicy.makeNewWindowManager();
    }
    public static FallbackEventHandler makeNewFallbackEventHandler(Context context) {
     
        return sPolicy.makeNewFallbackEventHandler(context);
    }
}

网上的一些在线源码贴出的PolicyManager.java并不正确,具体看下面的google仓库链接PolicyManager.java源码链接

PolicyManager.java任然是代理类实际实现是com.android.internal.policy.impl.Policy

Policy.java源码链接

//Policy.java
public class Policy implements IPolicy {
     
    private static final String TAG = "PhonePolicy";
    private static final String[] preload_classes = {
     
        "com.android.internal.policy.impl.PhoneLayoutInflater",
        "com.android.internal.policy.impl.PhoneWindow",
        "com.android.internal.policy.impl.PhoneWindow$1",
        "com.android.internal.policy.impl.PhoneWindow$ContextMenuCallback",
        "com.android.internal.policy.impl.PhoneWindow$DecorView",
        "com.android.internal.policy.impl.PhoneWindow$PanelFeatureState",
        "com.android.internal.policy.impl.PhoneWindow$PanelFeatureState$SavedState",
    };
    static {
     
        // For performance reasons, preload some policy specific classes when
        // the policy gets loaded.
        for (String s : preload_classes) {
     
            try {
     
                Class.forName(s);
            } catch (ClassNotFoundException ex) {
     
                Log.e(TAG, "Could not preload class for phone policy: " + s);
            }
        }
    }
    public PhoneWindow makeNewWindow(Context context) {
     
        return new PhoneWindow(context);
    }
    public PhoneLayoutInflater makeNewLayoutInflater(Context context) {
     
        return new PhoneLayoutInflater(context);
    }
    public PhoneWindowManager makeNewWindowManager() {
     
        return new PhoneWindowManager();
    }
}

综上Activity中的mWindow对象实现类为PhoneWindow

我们继续Activity的onCreate函数

class MainActivity : Activity() {
     
  override fun onCreate(savedInstanceState: Bundle?) {
     
    super.onCreate(savedInstanceState)
    //跟入
    setContentView(R.layout.activity_main)

  }
}

//Activity.java
 public void setContentView(int layoutResID) {
     
    //getWindow()返回 Window mWindow;也就是PhoneWindow
    getWindow().setContentView(layoutResID);
    //初始化标题栏相关,这里和本文无关联
    initWindowDecorActionBar();
 }

//PhoneWindow.java
class PhoneWindow{
     
	
	//mContentParent是mDecor的子view
	private ViewGroup mContentParent;
	//DecorView对象继承自FrameLayout,有一个子view,子view为mContentParent
	private DecorView mDecor;

    @Override
    public void setContentView(int layoutResID) {
     
    	//第一次当然为空   
        if (mContentParent == null) {
     
        	//很显然这里必须要给mContentParent赋值
        	//我们进入看看
        	//这里就是给mDecor和mContentParent赋值
            installDecor();
        } 

       //填充布局,并插入mContentParent作为子视图
       //布局填充器内容比较简单,读者可以访问博主的其他文章
       mLayoutInflater.inflate(layoutResID, mContentParent);
       //回调监听器,这里会回调到activity的onContentChanged函数哦
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
     
        	//onContentChanged函数回调时布局一定是被设置的了.
        	//你可以随意findViewById等操作
            cb.onContentChanged();
        }
    }
    
}

我们具体分析下installDecor函数

//PhoneWindow.java
class PhoneWindow{
     
	private ViewGroup mContentParent;
	private DecorView mDecor;
	private void installDecor() {
     
		 //第一次进入时mDecor必然为空
		 if (mDecor == null) {
     
            mDecor = generateDecor();
          }
				
	}
	private void installDecor() {
     

        if (mDecor == null) {
     
        	//就是直接new了一个DecorView返回
        	//DecorView是FrameLayout,也就是说他是一个布局
            mDecor = generateDecor();  
        }
        
        if (mContentParent == null) {
     
        	//重点函数,给FrameLayout添加一个子view,
        	//这个子view会通过generateLayout返回
            mContentParent = generateLayout(mDecor);    
        }
    }
		
	protected DecorView generateDecor() {
     
        return new DecorView(getContext(), -1);
    }

    protected ViewGroup generateLayout(DecorView decor) {
     
        // Apply data from current theme.

        //获取当前风格
        TypedArray a = getWindowStyle();

      
        //当前是浮窗模式
        mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);

        int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
                & (~getForcedWindowFlags());

        //根据是否为浮窗模式设置一些属性
        if (mIsFloating) {
     
            setLayout(WRAP_CONTENT, WRAP_CONTENT);
            setFlags(0, flagsToUpdate);
        } else {
     
            setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
        }
        //根据当前是否需要标题栏设置属性
        if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
     
            requestFeature(FEATURE_NO_TITLE);
        } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
     
            // Don't allow an action bar if there is no title.
            requestFeature(FEATURE_ACTION_BAR);
        }
        //其他属性的判断设置.可参阅其他文章
        if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) {
     
            requestFeature(FEATURE_ACTION_BAR_OVERLAY);
        }
        //.....
        //其他属性的判断设置.可参阅其他文章略

        
     

        int layoutResource;

        int features = getLocalFeatures();
        //requestFeature会改变getLocalFeatures返回值哦
        //getLocalFeatures可以到当前设置的一些窗口属性,如无标题
        //这里通过窗口的一些特点设置
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
     
            layoutResource = R.layout.screen_swipe_dismiss;
        } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
     
            if (mIsFloating) {
     
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleIconsDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
     
                layoutResource = R.layout.screen_title_icons;
            }
            removeFeature(FEATURE_ACTION_BAR);
            // System.out.println("Title Icons!");
        } 
        //...省略其他else if
        else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
     
            layoutResource = R.layout.screen_simple_overlay_action_mode;
        } else {
     
            //默认情况下得到的布局
            layoutResource = R.layout.screen_simple;
           
        }

        //填充view
        View in = mLayoutInflater.inflate(layoutResource, null);
        //添加decorview中
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        //mContentRoot就是我们填充的布局
        mContentRoot = (ViewGroup) in;
        //ID_ANDROID_CONTENT为 com.android.internal.R.id.content
        //这里可以得出一个结论不过你设置了何种窗口风格得到布局,一定会有一个子view,id为ID_ANDROID_CONTENT为
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
       

       

        return contentParent;
    }

}

通过上面的源码分析我们就可以到一个非常经典的图:

View的绘制流程_第1张图片
图片转载自(实在懒得画)
Android应用层View绘制流程与源码分析

顺带看一眼默认布局




<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
    <FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
LinearLayout>


上面的内容可以让我们对Android视图层次体系有一个具体的概念.
继续回头看看handleLaunchActivity函数哦

//ActivityThread.java

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
     
	//上文已经讲解
	Activity a = performLaunchActivity(r, customIntent);
	//继续深入分析 
	handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed);
                    
}
  final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume) {
     
       
        //执行activity的onresume函数
        ActivityClientRecord r = performResumeActivity(token, clearHide);

        if (r != null) {
     
            final Activity a = r.activity;

            if (r.window == null && !a.mFinished && willBeVisible) {
     
                r.window = r.activity.getWindow();

                View decor = r.window.getDecorView();
                
                decor.setVisibility(View.INVISIBLE);
                //WindowManager 继承 ViewManager
                //还记得我们怎么得到WindowManager吗?可以回顾上文
                ViewManager wm = a.getWindowManager();
                
                WindowManager.LayoutParams l = r.window.getAttributes();
                
                a.mDecor = decor;
                
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                
                if (a.mVisibleFromClient) {
     
                    a.mWindowAdded = true;
                    //WindowManager的实现类是WindowManagerImpl,
                    wm.addView(decor, l);
                }

        } 
    }
//WindowManagerImpl.java
public final class WindowManagerImpl implements WindowManager {
     
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();


    @Override
    public void addView(View view, ViewGroup.LayoutParams params) {
     
    //发现还是一个代理,进入WindowManagerGlobal
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }

}
//WindowManagerGlobal.java
public final class WindowManagerGlobal {
     
	private final ArrayList<View> mViews = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
  
       
    //view是decorview哦
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
     
        
        //布局参数
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;

        //根据一些条件设置布局参数
        if (parentWindow != null) {
     
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
     
            if (context != null
                    && context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
     
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

        ViewRootImpl root;

        View panelParentView = null;
        //ViewRootImpl包含绘制绘制的开始
        root = new ViewRootImpl(view.getContext(), display);
        //给decorview设置布局参数
        view.setLayoutParams(wparams);

        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);
       
        root.setView(view, wparams, panelParentView);
       
    }
}

ViewRootImpl内部包含绘制流程的开始,会在下一个vssync信号到达的时候进行绘制.

不过我们先总结上面源码.

综上所得我们Activity在oncreateonresume回调的时候根本没有走绘制流程,所以我们经常遇到获取高度为0的情况.


class MainActivity : Activity() {
     
  override fun onCreate(savedInstanceState: Bundle?) {
     
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    val view: View = findViewById(R.id.root)
    
    log("onCreate:"+view.width)
  }

  override fun onResume() {
     
    super.onResume()
    val view:View = findViewById(R.id.root)
    log("onResume:"+view.width)
  }

  
  fun log(msg:String){
     
    Log.e("MainActivity","${msg}")
  }
}

输出

onCreate:0
onResume:0

ViewRootImpl 简单介绍

上文我们最后看到构造了ViewRootImpl然后调用setview函数将decorview放入

 //view为decorview
 root = new ViewRootImpl(view.getContext(), display);
 //给decorview设置布局参数
 view.setLayoutParams(wparams);
 root.setView(view, wparams, panelParentView);
//ViewRootImpl.java
public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks{
     
}

可以看到ViewRootImpl实现了ViewParent,但是并没有继承view,这个需要特别注意,所以我们在使用view.getParent函数不可轻易强转为ViewGroup/View等.

在执行绘制操作的时候会回调ViewRootImpl.performTraversals.但是这个函数特别冗长,网上许多源码分析对函数进行一些精简,但是这样会丢失一些优化逻辑,比如是否要执行performDraw等判断.

class ViewRootImpl{
     
  private void performTraversals() {
     

     //--------------------------[performMeasure]--------------------
      boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
              (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
      //根据一些情况执行performMeasure
      if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
              || mHeight != host.getMeasuredHeight() || contentInsetsChanged) {
     

          int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
          int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

           //执行测量
          performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
      }
      //--------------------------[performLayout]--------------------
      final boolean didLayout = layoutRequested && !mStopped;
        
      boolean triggerGlobalLayoutListener = didLayout|| mAttachInfo.mRecomputeGlobalAttributes;
      //根据一些情况执行
      if (didLayout) {
     
          //执行布局
          performLayout(lp, desiredWindowWidth, desiredWindowHeight);
      }


      //--------------------------[performDraw]--------------------    
        boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() ||viewVisibility != View.VISIBLE;
//根据一些情况执行
      if (!cancelDraw && !newSurface) {
     
          if (!skipDraw || mReportNextDraw) {
     
              //执行绘制
              performDraw();
          }
      }
  }
}

上面不需要只需要理解执行绘制不一定会完整执行performMeasure,performLayout,performDraw这三个函数.

不过为了方便我理解大体流程我们先忽略这些优化,于是乎上面的代码简化为:

//ViewRootImpl.java
class ViewRootImpl{
     
  private void performTraversals() {
     

      //--------------------------[performMeasure]--------------------
       //执行测量
       int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
       int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
       
       performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    
      //--------------------------[performLayout]--------------------
      //执行布局
      performLayout(lp, desiredWindowWidth, desiredWindowHeight);

      //--------------------------[performDraw]--------------------    
      //执行测绘
      performDraw();
     
  }
}

performMeasure

在了解测量布局之前我们需要一些前置知识MeasureSpec.

MeasureSpec

假设我们想用一个Int类型标识两种不同事物怎么办?
我们就会用将32位bit进行拆分使用,比如前2位作为一个变量使用,后30位bit作为另一个变量使用.这样我们就可以减少一个变量的创建,但是却要求程序员用位操作取出对应数据.
View的绘制流程_第2张图片
这种思想被放入View的宽高设置上

    
    <View
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    
    <View
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    
    <View
        android:layout_width="35dp"
        android:layout_height="35dp" />

我们这里有一个小需求就是用一个int变量去表示android:layout_width是哪种类型的,并且这个类型的长度,比如:
android:layout_width="35dp"我们将写死宽度的视为EXACTLY类型,然后在放入35dp在一个变量中.
View的绘制流程_第3张图片

MeasureSpec就是帮我们做了这样事情
我们细看其类声明

//View.java
public static class MeasureSpec {
     
		//用于移动位,这里第30位作为大小,前两位作为模式mode
        private static final int MODE_SHIFT = 30;
        //掩码 用于获得当前是哪一种模式
        private static final int MODE_MASK  = 0x3 << MODE_SHIFT;

        /**
         * 父view并不限制子view的大小,子view可以无条件无限制声明自己所需的大小
         */
        public static final int UNSPECIFIED = 0 << MODE_SHIFT;

        /**
        * 父view已经有明确的大小给子view去使用,如果子view是EXACTLY,你只能使用小于等于父给定范围
        */
        public static final int EXACTLY  = 1 << MODE_SHIFT;

        /**
         * 父view不约子view大小,但是不能超过父view 的范围
         */
        public static final int AT_MOST     = 2 << MODE_SHIFT;

        //根据传入的大小和模式构造一个符合MeasureSpec的变量
        public static int makeMeasureSpec(int size, int mode) {
     
            if (sUseBrokenMakeMeasureSpec) {
     
                return size + mode;
            } else {
     
                return (size & ~MODE_MASK) | (mode & MODE_MASK);
            }
        }

     	//得到当前变量的模式
        public static int getMode(int measureSpec) {
     
            return (measureSpec & MODE_MASK);
        }

       //当前变量所设置的大小
        public static int getSize(int measureSpec) {
     
            return (measureSpec & ~MODE_MASK);
        }
		//调整measureSpec内部的大小,内部的大小加上delta然后返回
		//你可以传一个负数减少大小哦
        static int adjust(int measureSpec, int delta) {
     
            final int mode = getMode(measureSpec);
            if (mode == UNSPECIFIED) {
     
                // No need to adjust size for UNSPECIFIED mode.
                return makeMeasureSpec(0, UNSPECIFIED);
            }
            int size = getSize(measureSpec) + delta;
            if (size < 0) {
     
             
                size = 0;
            }
            return makeMeasureSpec(size, mode);
        }

       	//返回一个供阅读的字符串,方便调试
        public static String toString(int measureSpec) {
     
            int mode = getMode(measureSpec);
            int size = getSize(measureSpec);

            StringBuilder sb = new StringBuilder("MeasureSpec: ");

            if (mode == UNSPECIFIED)
                sb.append("UNSPECIFIED ");
            else if (mode == EXACTLY)
                sb.append("EXACTLY ");
            else if (mode == AT_MOST)
                sb.append("AT_MOST ");
            else
                sb.append(mode).append(" ");

            sb.append(size);
            return sb.toString();
        }
    }

测量大小源码分析

当我们理解MeasureSpec后在回头看下源码

//ViewRootImpl.java
class ViewRootImpl{
     
  private void performTraversals() {
     

      //--------------------------[performMeasure]--------------------
       //执行测量,mWidth可以理解为windows窗口的大小,多数情况为扣除状态栏的屏幕大小
       //lp.width 一般都为ViewGroup.LayoutParams.MATCH_PARENT
       //getRootMeasureSpec返回符合MeasureSpec规范的尺寸变量
       int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
       int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
       
       //我们重点关心这个函数
       performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
  }
  //比较简单读者可以自己看下
  private static int getRootMeasureSpec(int windowSize, int rootDimension) {
     
        int measureSpec;
        switch (rootDimension) {
     
        case ViewGroup.LayoutParams.MATCH_PARENT:
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
            break;
       	//略
        }
        return measureSpec;
    }
	 
	 private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
     
		//mView是Decorview
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
       
    }
}

回过头我们看下Decorviewmeasure函数

//PhoneWindow.java
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
     

}

由于DecorView继承FrameLayout,而measure函数位于View.java中且不可以重写,所以我们直接看View.java

//View.java
class View{
     
	public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
     
				//onMeasure一般由子类重写,这里我们看看默认view的实现
                onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
    
   protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }
}

为了简化分析流程我们先分析getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec)在分析setMeasuredDimension

class View{
     
    private int mMinWidth;
    protected int getSuggestedMinimumWidth() {
     
    	//如果有背景就取背景大小和mMinWidth的最大值,这里可以理解为返回了0
        return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
    }
	//size可以理解为0
	//measureSpec是父亲传下来
	 public static int getDefaultSize(int size, int measureSpec) {
     
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
		//从getRootMeasureSpec可以得知specMode为EXACTLY
        switch (specMode) {
     
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
               
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
        	//specSize就是父布局的大小,也就是除了状态栏以外的大小(全屏或者窗口等除外)
            result = specSize;
            break;
        }
        return result;
    }
}

最后我们回到setMeasuredDimension函数

//View.java
class View{
     
  protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
     
        setMeasuredDimensionRaw(measuredWidth, measuredHeight);
    }
    
    int mMeasuredWidth;
    int mMeasuredHeight;
    
    private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
     
    	//最终是将measuredWidth和measuredHeight保存到自身属性中
        mMeasuredWidth = measuredWidth;
        mMeasuredHeight = measuredHeight;

        mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
    }
}

我们看完默认的onMeasure实现,我们最后看下DecorView的实现

//PhoneWindow.java
class DecorView{
     
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     
            final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
            //当前是否为竖屏
            final boolean isPortrait = metrics.widthPixels < metrics.heightPixels;

            final int widthMode = getMode(widthMeasureSpec);
            final int heightMode = getMode(heightMeasureSpec);

            boolean fixedWidth = false;

            if (widthMode == AT_MOST) {
     

                //横竖屏处理,适当的修改widthMeasureSpec

            }

            if (heightMode == AT_MOST) {
     
               //横竖屏处理,适当的修改 heightMeasureSpec
            }

            //调用父类的函数也就是FrameLayout
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);

            //略

           
        }
}

既然调用了父类的onMeasure我们跟入FrameLayout

//FrameLayout.java
   class FrameLayout{
     

        private final ArrayList<View> mMatchParentChildren = new ArrayList<View>(1);

        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     

            int count = getChildCount();

            //如果父布局传传入的widthMeasureSpec 和heightMeasureSpec都不是EXACTLY返回true
            //这里decorview传入的都是EXACTLY,所以这里为true
            final boolean measureMatchParentChildren =
                    MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
                    MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
            //这个集合,用于存放要进行测绘的子view
            mMatchParentChildren.clear();

            int maxHeight = 0;
            int maxWidth = 0;
            int childState = 0;

            //遍历所有子view
            for (int i = 0; i < count; i++) {
     
                final View child = getChildAt(i);
                 //如果mMeasureAllChildren为true或者child可见时,子view会被进行测绘
                if (mMeasureAllChildren || child.getVisibility() != GONE) {
     

                    //略
                    //将子view添加都集合中
                    mMatchParentChildren.add(child);
                    //略
                   
                }
            }

            //略....

            //设置自己高度,这里是 FrameLayout相关逻辑,我们不必进行深入   
            setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                    resolveSizeAndState(maxHeight, heightMeasureSpec,
                            childState << MEASURED_HEIGHT_STATE_SHIFT));

            count = mMatchParentChildren.size();
            //取出所有子view遍历
            if (count > 1) {
     
                for (int i = 0; i < count; i++) {
     
                    
                    final View child = mMatchParentChildren.get(i);

                    //得到布局参数
                    final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
                   
                    int childWidthMeasureSpec;
                    int childHeightMeasureSpec;
                    
                    //子view想跟FrameLayout一样大
                    if (lp.width == LayoutParams.MATCH_PARENT) {
     
                        //这里减去自己左右pading和子view的margin然后设置
                        childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() -
                                getPaddingLeftWithForeground() - getPaddingRightWithForeground() -
                                lp.leftMargin - lp.rightMargin,
                                MeasureSpec.EXACTLY);
                    } else {
     
						//getChildMeasureSpec是一个非常重要的函数
						//根据自身的MeasureSpec和子view的LayoutParams生成一个对应MeasureSpec给子view进行测量
                        childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                                getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
                                lp.leftMargin + lp.rightMargin,
                                lp.width);
                    }
                    //同上
                    if (lp.height == LayoutParams.MATCH_PARENT) {
     
                        childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() -
                                getPaddingTopWithForeground() - getPaddingBottomWithForeground() -
                                lp.topMargin - lp.bottomMargin,
                                MeasureSpec.EXACTLY);
                    } else {
     
                        childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
                                getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
                                lp.topMargin + lp.bottomMargin,
                                lp.height);
                    }
                    //最后调用子viewmeasure
                    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
                }
            }
        }
    }

//ViewGroup.java
class ViewGroup{
     
	//参数padding: 如果是测量宽,那么padding应该为 左pading+右pading+子view左边距+子view右边距
	//也就是说pading是不可用的宽度
    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
     
        int specMode = MeasureSpec.getMode(spec);
        int specSize = MeasureSpec.getSize(spec);
		//size为当前viewGroup最大可用宽/高
		//specSize - padding减去 pading和子view边距
        int size = Math.max(0, specSize - padding);

        int resultSize = 0;
        int resultMode = 0;

        switch (specMode) {
     
        //当前ViewGroup的布局宽高是明确的 
        case MeasureSpec.EXACTLY:
        	//如果子view的android:width或者android:height是一个明确的数值
        	//比如android:width="35dp"
            if (childDimension >= 0) {
     
            	//设置子view的 宽/高 度
                resultSize = childDimension;
                //模式为EXACTLY
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
     
                // 如果子view 想和当前viewGrou高度一致
                //设置父布局的宽高度
                resultSize = size;
                //模式为EXACTLY
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
     
                //子view想自行决定宽高
                //那么宽高为自身ViewGroup的上限宽高 
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        //当前ViewGroup的父布局让其自行决定大小,但是不能超过父亲
        case MeasureSpec.AT_MOST:
            if (childDimension >= 0) {
     
            	//子view有一个明确的大小
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
     
                //子view想跟ViewGroup一样大,但是ViewGroup不是固定的
                //所以约束子view不超过父布局即可
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
     
                // 子view想自行决定大小,但是不能超过父view
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // ViewGroup父布局不限制子view宽高,
        case MeasureSpec.UNSPECIFIED:
            if (childDimension >= 0) {
     
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
     
                //由于ViewGroup宽高是不受限的,而子view也想和我们一样.
                //所以这里会下沉UNSPECIFIED
                resultSize = 0;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
     
              //由于ViewGroup宽高是不受限的,所以不限制子view所想要的高度.
              //所以这里会下沉UNSPECIFIED
                resultSize = 0;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }
        //利用上面的大小和模式构造变量返回
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }
}

当理解getChildMeasureSpec函数后对于UNSPECIFIED,AT_MOST,EXACTLY这个三个模式的理解应该很深刻.

我们最后做一下performMeasure的总结:
Decorview首先会调用measure函数,在调用onMeasure.
最后是给自己设置宽高信息到 mMeasuredWidthmMeasuredHeight,并计算出子viewMeasureSpec,然后调用子view的measure函数.

performLayout

//ViewRootImpl.java
    private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
            int desiredWindowHeight) {
     
        mLayoutRequested = false;
        mScrollMayChange = true;
        mInLayout = true;

        final View host = mView;
        
       //Decorview继承FramLayout,而FramLayout继承View
		//我们直接看view的实现
        host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());

        mInLayout = false;
    }

//View.java
class View{
     
	 public void layout(int l, int t, int r, int b) {
     



        int oldL = mLeft;
        int oldT = mTop;
        int oldB = mBottom;
        int oldR = mRight;
		//给自己mLeft mTop mBottom mRight 设置
		//并且根据情况调用onsizechange函数
        boolean changed =  setFrame(l, t, r, b);

        if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
     
            //调用onlayout函数,这个一般由子类实现,由于decorview继承FramLayout,所以我们待会看着类即可
            onLayout(changed, l, t, r, b);

            mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;

            //回调函数,您可以注册一个函数拿到view的宽高哦    
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnLayoutChangeListeners != null) {
     
                ArrayList<OnLayoutChangeListener> listenersCopy =
                        (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
                int numListeners = listenersCopy.size();
                for (int i = 0; i < numListeners; ++i) {
     
                    listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
                }
            }
        }

        mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
        mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
    }
	protected int mLeft;
	protected int mRight;
	protected int mTop;
	protected int mBottom;

	protected boolean setFrame(int left, int top, int right, int bottom) {
     
	    
	    boolean changed = false;
	    //mLeft mRight  mTop mBottom还未初始化所以都是0
	    //所以这里自然返回true进入if中
	
	    if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
     
	        changed = true;
	
	        // Remember our drawn bit
	        int drawn = mPrivateFlags & PFLAG_DRAWN;
	
	        int oldWidth = mRight - mLeft;
	        int oldHeight = mBottom - mTop;
	        int newWidth = right - left;
	        int newHeight = bottom - top;
	
	        //很明显不一样,sizeChanged为true
	        boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
	
	        //标记缓存视图失效,因为宽高改变
	        invalidate(sizeChanged);
	
	
	        //初始化变量
	        mLeft = left;
	        mTop = top;
	        mRight = right;
	        mBottom = bottom;
	
	        //设置这个视图绘制的位置,这个函数会调用native函数,这里我们可以不用关心
	        //只需要知道这个view超过这个范围是无法绘制的
	        mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
	
	        mPrivateFlags |= PFLAG_HAS_BOUNDS;
	
	
	        //如果大小改变调用自己sizeChange函数,而sizeChange又会调用onSizeChanged
	        if (sizeChanged) {
     
	            sizeChange(newWidth, newHeight, oldWidth, oldHeight);
	        }
	
	        //如果视图可见那么进行重绘,
	        if ((mViewFlags & VISIBILITY_MASK) == VISIBLE) {
     
	          
	            mPrivateFlags |= PFLAG_DRAWN;
	            invalidate(sizeChanged);
	            invalidateParentCaches();
	        }
	
	        // Reset drawn bit to original value (invalidate turns it off)
	        mPrivateFlags |= drawn;
	
	        mBackgroundSizeChanged = true;
	
	        notifySubtreeAccessibilityStateChangedIfNeeded();
	    }
	    return changed;
	}
}


我们最后看FramLayoutOnlayout函数

class FramLayout{
     


    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
     
        layoutChildren(left, top, right, bottom, false /* no force left gravity */);
    }

    void layoutChildren(int left, int top, int right, int bottom,
                                  boolean forceLeftGravity) {
     

        final int count = getChildCount();

        final int parentLeft = getPaddingLeftWithForeground();

        final int parentRight = right - left - getPaddingRightWithForeground();

        final int parentTop = getPaddingTopWithForeground();

        final int parentBottom = bottom - top - getPaddingBottomWithForeground();

        mForegroundBoundsChanged = true;

        //遍历所有子view 然后根据子view的gravity和自身的padding确定view的摆放位置
        for (int i = 0; i < count; i++) {
     
            
            final View child = getChildAt(i);
            

            //视图可见在进行操作
            if (child.getVisibility() != GONE) {
     


                /*
                * 根据布局参数和自身padding数值等确定子view的位置
                * 然后调用子view的layout
                */
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();

                final int width = child.getMeasuredWidth();
                final int height = child.getMeasuredHeight();

                int childLeft;
                int childTop;

                int gravity = lp.gravity;
                if (gravity == -1) {
     
                    gravity = DEFAULT_CHILD_GRAVITY;
                }

                final int layoutDirection = getLayoutDirection();
                final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
                final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;

                switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
     
                    case Gravity.CENTER_HORIZONTAL:
                        childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
                        lp.leftMargin - lp.rightMargin;
                        break;
                    case Gravity.RIGHT:
                        if (!forceLeftGravity) {
     
                            childLeft = parentRight - width - lp.rightMargin;
                            break;
                        }
                    case Gravity.LEFT:
                    default:
                        childLeft = parentLeft + lp.leftMargin;
                }

                switch (verticalGravity) {
     
                    case Gravity.TOP:
                        childTop = parentTop + lp.topMargin;
                        break;
                    case Gravity.CENTER_VERTICAL:
                        childTop = parentTop + (parentBottom - parentTop - height) / 2 +
                        lp.topMargin - lp.bottomMargin;
                        break;
                    case Gravity.BOTTOM:
                        childTop = parentBottom - height - lp.bottomMargin;
                        break;
                    default:
                        childTop = parentTop + lp.topMargin;
                }

                //最终调用子view的layout函数    
                child.layout(childLeft, childTop, childLeft + width, childTop + height);
            }
        }
    }

}

总结:
performLayout其实就是设置mLeft mTop mRight mBottom;,这个几个数值由父亲根据自身的一些特点决定.
对比performMeasure仅仅用来声明想要的大小,而最终决定大小的还是performLayout函数.

performDraw

//ViewRootImpl.java
class ViewRootImpl{
     
 	private void performDraw() {
     
        draw(fullRedrawNeeded);
    }
    
    private void draw(boolean fullRedrawNeeded) {
     
        if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) 
            return;
              
    }
    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty) {
     

       
        final Canvas canvas;
    
        final int left = dirty.left;
        final int top = dirty.top;
        final int right = dirty.right;
        final int bottom = dirty.bottom;
        
        //dirty表示绘制区域,如果传入null将绘制整个surface
        canvas = mSurface.lockCanvas(dirty);

         //最终调用到decorview的draw函数,这里会调用view的draw函数
        mView.draw(canvas);
        
        surface.unlockCanvasAndPost(canvas);
       
        return true;
    }
}

ViewDraw被写到烂了,不过为了文章完整性再写一遍,其实注释写的很好

class View{
     
	public void draw(Canvas canvas) {
     
	    
        final int privateFlags = mPrivateFlags;
        final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
        mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;

        /*
         * 绘制执行若干个步骤,每个步骤根据适当顺序执行
         *  
         * Draw traversal performs several drawing steps which must be executed
         * in the appropriate order:
         *	    1.绘制背景
         *      1. Draw the background
         *      
         * 		2. 如果有必要的话,就保存canvas的图层用于绘制过度边缘滚动/拉扯效果
         * 		2. If necessary, save the canvas' layers to prepare for fading
         * 		
         * 		3.绘制view 的内容
         *      3. Draw view's content
         *      
         * 		4. 绘制子view
         * 		4. Draw children
         * 		
         * 		5.如果有必要的话,绘制过度边缘滚动/拉扯效果然后恢复canvas图层
         *      5. If necessary, draw the fading edges and restore layers
         *      
         * 		6.绘制一些装饰内容 如滚动条
         * 		6. Draw decorations (scrollbars for instance)
         */

        // Step 1, draw the background, if needed
        // 第一步 绘制背景色
        int saveCount;
        if (!dirtyOpaque) {
     
            drawBackground(canvas);
        }

        // skip step 2 & 5 if possible (common case)
        final int viewFlags = mViewFlags;
        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
        //不需要绘制过度滚动/拉扯 效果
        if (!verticalEdges && !horizontalEdges) {
     
            // Step 3, draw the content
            // 第3步 绘制view的内容
            if (!dirtyOpaque) onDraw(canvas);

            // Step 4, draw the children
            //第四步 绘制子view
            dispatchDraw(canvas);

            // Step 6, draw decorations (scrollbars)
            //第六步 绘制滚动条
            onDrawScrollBars(canvas);

            if (mOverlay != null && !mOverlay.isEmpty()) {
     
                mOverlay.getOverlayView().dispatchDraw(canvas);
            }

            // 返回
            return;
        }

        /* 
         * 下面是一个是一个完整绘制流程,因为不常用所以我们这里不分析
         * 
         * Here we do the full fledged routine...
         * (this is an uncommon case where speed matters less,
         * this is why we repeat some of the tests that have been
         * done above)
         */

        boolean drawTop = false;
        boolean drawBottom = false;
        boolean drawLeft = false;
        boolean drawRight = false;

        float topFadeStrength = 0.0f;
        float bottomFadeStrength = 0.0f;
        float leftFadeStrength = 0.0f;
        float rightFadeStrength = 0.0f;

        // Step 2, save the canvas' layers
        int paddingLeft = mPaddingLeft;

        final boolean offsetRequired = isPaddingOffsetRequired();
        if (offsetRequired) {
     
            paddingLeft += getLeftPaddingOffset();
        }

        int left = mScrollX + paddingLeft;
        int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
        int top = mScrollY + getFadeTop(offsetRequired);
        int bottom = top + getFadeHeight(offsetRequired);

        if (offsetRequired) {
     
            right += getRightPaddingOffset();
            bottom += getBottomPaddingOffset();
        }

        final ScrollabilityCache scrollabilityCache = mScrollCache;
        final float fadeHeight = scrollabilityCache.fadingEdgeLength;
        int length = (int) fadeHeight;

        // clip the fade length if top and bottom fades overlap
        // overlapping fades produce odd-looking artifacts
        if (verticalEdges && (top + length > bottom - length)) {
     
            length = (bottom - top) / 2;
        }

        // also clip horizontal fades if necessary
        if (horizontalEdges && (left + length > right - length)) {
     
            length = (right - left) / 2;
        }

        if (verticalEdges) {
     
            topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
            drawTop = topFadeStrength * fadeHeight > 1.0f;
            bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
            drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
        }

        if (horizontalEdges) {
     
            leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
            drawLeft = leftFadeStrength * fadeHeight > 1.0f;
            rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
            drawRight = rightFadeStrength * fadeHeight > 1.0f;
        }

        saveCount = canvas.getSaveCount();

        int solidColor = getSolidColor();
        if (solidColor == 0) {
     
            final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;

            if (drawTop) {
     
                canvas.saveLayer(left, top, right, top + length, null, flags);
            }

            if (drawBottom) {
     
                canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
            }

            if (drawLeft) {
     
                canvas.saveLayer(left, top, left + length, bottom, null, flags);
            }

            if (drawRight) {
     
                canvas.saveLayer(right - length, top, right, bottom, null, flags);
            }
        } else {
     
            scrollabilityCache.setFadeColor(solidColor);
        }

        // Step 3, draw the content
        if (!dirtyOpaque) onDraw(canvas);

        // Step 4, draw the children
        dispatchDraw(canvas);

        // Step 5, draw the fade effect and restore layers
        final Paint p = scrollabilityCache.paint;
        final Matrix matrix = scrollabilityCache.matrix;
        final Shader fade = scrollabilityCache.shader;

        if (drawTop) {
     
            matrix.setScale(1, fadeHeight * topFadeStrength);
            matrix.postTranslate(left, top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(left, top, right, top + length, p);
        }

        if (drawBottom) {
     
            matrix.setScale(1, fadeHeight * bottomFadeStrength);
            matrix.postRotate(180);
            matrix.postTranslate(left, bottom);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(left, bottom - length, right, bottom, p);
        }

        if (drawLeft) {
     
            matrix.setScale(1, fadeHeight * leftFadeStrength);
            matrix.postRotate(-90);
            matrix.postTranslate(left, top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(left, top, left + length, bottom, p);
        }

        if (drawRight) {
     
            matrix.setScale(1, fadeHeight * rightFadeStrength);
            matrix.postRotate(90);
            matrix.postTranslate(right, top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(right - length, top, right, bottom, p);
        }

        canvas.restoreToCount(saveCount);

        // Step 6, draw decorations (scrollbars)
        onDrawScrollBars(canvas);

        if (mOverlay != null && !mOverlay.isEmpty()) {
     
            mOverlay.getOverlayView().dispatchDraw(canvas);
        }
    }

}

上面的代码我只需要关注几个函数的调用
onDraw,dispatchDraw,onDrawScrollBars这个几个函数根据自己的业务需求进行重写,
onDraw由子view实现,dispatchDraw一般由viewGroup重写

class ViewGroup{
     
	    protected void dispatchDraw(Canvas canvas) {
     

        boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
        final int childrenCount = mChildrenCount;
        final View[] children = mChildren;
        int flags = mGroupFlags;

        if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
     
            final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;

            final boolean buildCache = !isHardwareAccelerated();
            //如果没有开启硬件加速那么如果存在缓存时,利用缓存绘制子view,内部利用bitmap存储视图
            //这里没必要深究
            for (int i = 0; i < childrenCount; i++) {
     
                final View child = children[i];
                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
     
                    final LayoutParams params = child.getLayoutParams();
                    attachLayoutAnimationParameters(child, params, i, childrenCount);
                    bindLayoutAnimation(child);
                    if (cache) {
     
                        child.setDrawingCacheEnabled(true);
                        if (buildCache) {
     
                            child.buildDrawingCache(true);
                        }
                    }
                }
            }
        }


        int clipSaveCount = 0;
        final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
        //裁剪canvas画布给子view用,不让子view绘制约束区域.这里是扣除自身的padding大小和滚动距离
        if (clipToPadding) {
     
            clipSaveCount = canvas.save();
            canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
                    mScrollX + mRight - mLeft - mPaddingRight,
                    mScrollY + mBottom - mTop - mPaddingBottom);
        }

        // We will draw our child's animation, let's reset the flag
        mPrivateFlags &= ~PFLAG_DRAW_ANIMATION;
        mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;

        boolean more = false;
        final long drawingTime = getDrawingTime();

        if (usingRenderNodeProperties) canvas.insertReorderBarrier();
     
        final ArrayList<View> preorderedList = usingRenderNodeProperties
                ? null : buildOrderedChildList();

        final boolean customOrder = preorderedList == null
                && isChildrenDrawingOrderEnabled();

         //遍历子view然后通过drawChild让子view执行绘制       
        for (int i = 0; i < childrenCount; i++) {
     
        
            int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
        
            final View child = (preorderedList == null)
                    ? children[childIndex] : preorderedList.get(childIndex);
            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
     
            	//我们跟进去看看实现
                more |= drawChild(canvas, child, drawingTime);
            }
        }



        if (preorderedList != null) preorderedList.clear();

        // 绘制一些消失动画,这里完全不需要关心
        if (mDisappearingChildren != null) {
     
            final ArrayList<View> disappearingChildren = mDisappearingChildren;
            final int disappearingCount = disappearingChildren.size() - 1;
            // Go backwards -- we may delete as animations finish
            for (int i = disappearingCount; i >= 0; i--) {
     
                final View child = disappearingChildren.get(i);
                more |= drawChild(canvas, child, drawingTime);
            }
        }
        if (usingRenderNodeProperties) canvas.insertInorderBarrier();

        //恢复图层
       
        if (clipToPadding) {
     
            //略
            canvas.restoreToCount(clipSaveCount);
        }
		//略
    }
	
}

我们看下上面drawChild(canvas, child, drawingTime);

class ViewGroup{
     
  protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
     
        return child.draw(canvas, this, drawingTime);
    }
}
class View{
     
    boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
     
      


        boolean usingRenderNodeProperties = mAttachInfo != null && mAttachInfo.mHardwareAccelerated;
       

        if (!usingRenderNodeProperties) {
     
            //裁剪子view 关于android:clipChildren用法网上有很多.如果不设置默认为true
            //从里我们可以知道子view的canvas画布大小为什么受限
            if ((flags & ViewGroup.FLAG_CLIP_CHILDREN) == ViewGroup.FLAG_CLIP_CHILDREN
                    && cache == null) {
     

                if (offsetForScroll) {
     
                    canvas.clipRect(sx, sy, sx + (mRight - mLeft), sy + (mBottom - mTop));
                } else {
     
                    if (!scalingRequired || cache == null) {
     
                        //裁剪到父布局定义的宽高
                        canvas.clipRect(0, 0, mRight - mLeft, mBottom - mTop);
                    } else {
     
                        canvas.clipRect(0, 0, cache.getWidth(), cache.getHeight());
                    }
                }
            }

            if (mClipBounds != null) {
     
                canvas.clipRect(mClipBounds);
            }
        }

      
 		if (usingRenderNodeProperties) {
     
 			//函数内部会触发computeScroll函数
            renderNode = getDisplayList();  
        }
        if (hasNoCache) {
     
           
            if (!layerRendered) {
     
                if (!hasDisplayList) {
     
                    //根据flag判断是否要跳过绘制
                    //如果跳过那么直接分发绘制到子view
                    if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
     
                        mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                        dispatchDraw(canvas);
                        if (mOverlay != null && !mOverlay.isEmpty()) {
     
                            mOverlay.getOverlayView().draw(canvas);
                        }
                    } else {
     
                         //绘制内容   
                        draw(canvas);
                    }
                }                
            }
        } else if (cache != null) {
     
           //略
        }

   

        return more;
    }

}
//view.java
 public RenderNode getDisplayList() {
     
        updateDisplayListIfDirty();
        return mRenderNode;
    }
  private void updateDisplayListIfDirty() {
     
  	   //方法中进行滚动相关操作
       computeScroll();
   }

上面便是整个绘制流程

setWillNotDraw

我们在很多时候ViewGroup是不需要绘制也就是不需要调用onDraw函数.对于此google提供了
setWillNotDraw函数.

//View.java
class View{
     
 //传入true时,将不会回调onDraw函数
 public void setWillNotDraw(boolean willNotDraw) {
     
        setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
 }

    void setFlags(int flags, int mask) {
     
		//略...
		//设置一个标志位
		mPrivateFlags |= PFLAG_SKIP_DRAW;
	}
    
}

回顾我们view的绘制分发流程,

draw(Canvas canvas, ViewGroup parent, long drawingTime)内部会调用draw(Canvas canvas).

class View{
     
	 boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
     
	 	  			//根据flag判断是否要跳过绘制
                    //如果跳过那么直接分发绘制到子view
                    if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
     
                        mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                        dispatchDraw(canvas);
                        if (mOverlay != null && !mOverlay.isEmpty()) {
     
                            mOverlay.getOverlayView().draw(canvas);
                        }
                    } else {
     
                         //绘制内容   
                        draw(canvas);
                    }
	 }
}

可以看到draw函数判断PFLAG_SKIP_DRAW在进行函数跳转,如果设置了跳过draw函数,而后直接掉用dispatchDraw(canvas);进行绘制事件下沉

默认情况ViewGroup构造函数中设置了不需要进行绘制

class ViewGroup{
     
	  public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
     
        super(context, attrs, defStyleAttr, defStyleRes);
        initViewGroup();
        initFromAttributes(context, attrs, defStyleAttr, defStyleRes);
    }
      private void initViewGroup() {
     
        // ViewGroup doesn't draw by default
        if (!debugDraw()) {
     
            setFlags(WILL_NOT_DRAW, DRAW_MASK);
        }
     }
}

多次invalidate会怎么样

本来想写的后来发现发现一篇挺好的

每日一问 | View invalidate() 相关的一些细节探究~

参考

Android View的绘制流程

面试官带你学安卓 - 从 View 的绘制流程说起

Android应用层View绘制流程与源码分析

你可能感兴趣的:(android源码)