本文基于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;
}
}
通过上面的源码分析我们就可以到一个非常经典的图:
图片转载自(实在懒得画)
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在oncreate
和onresume
回调的时候根本没有走绘制流程,所以我们经常遇到获取高度为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
然后调用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();
}
}
在了解测量布局之前我们需要一些前置知识MeasureSpec
.
假设我们想用一个Int
类型标识两种不同事物怎么办?
我们就会用将32位bit
进行拆分使用,比如前2位
作为一个变量使用,后30位bit
作为另一个变量使用.这样我们就可以减少一个变量的创建,但是却要求程序员用位操作取出对应数据.
这种思想被放入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
在一个变量中.
而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);
}
}
回过头我们看下Decorview
的measure
函数
//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
.
最后是给自己设置宽高信息到 mMeasuredWidth
和mMeasuredHeight
,并计算出子view
的MeasureSpec
,然后调用子view的measure
函数.
//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;
}
}
我们最后看FramLayout
的Onlayout
函数
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
函数.
//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;
}
}
View
的Draw
被写到烂了,不过为了文章完整性再写一遍,其实注释写的很好
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();
}
上面便是整个绘制流程
我们在很多时候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);
}
}
}
本来想写的后来发现发现一篇挺好的
每日一问 | View invalidate() 相关的一些细节探究~
Android View的绘制流程
面试官带你学安卓 - 从 View 的绘制流程说起
Android应用层View绘制流程与源码分析