在分析Activity启动过程中:
Activity启动流程会执行startSpecificActivityLocked ActivityThread.performLaunchActivity //onCreate -> onStart SetContentView创建DecorView(DecorView = ContentView(用户需要显示的组件) + 系统其它组件) ActivityThread.handleResumeActivity //onResume WindowManager.addView WindowManagerImpl.addView (将DecorView保存到WindowManagerImpl.mViews,将ViewRootImpl保存到mRoots) ViewRootImpl.setView (将DecorView保存到ViewRootImpl.mView)
ViewTree的创建过程首先从onCreate开始分析:
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
public class AppCompatActivity extends FragmentActivity implements AppCompatCallback,
TaskStackBuilder.SupportParentable, ActionBarDrawerToggle.DelegateProvider {
public void setContentView(@LayoutRes int layoutResID) {
getDelegate().setContentView(layoutResID);
}
}
class AppCompatDelegateImplV9 extends AppCompatDelegateImplbase
implements MenuBuilder.Callback, LayoutInflater.Factory2 {
public void setContentView(int resId) {
ensureSubDecor();
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
LayoutInflater.from(mContext).inflate(resId, contentParent);
mOriginalWindowCallback.onContentChanged();
}
}
1.1 分析ensureSubDecor
class AppCompatDelegateImplV9 extends AppCompatDelegateImplbase
implements MenuBuilder.Callback, LayoutInflater.Factory2 {
private void ensureSubDecor() {
if (!mSubDecorInstalled) {
mSubDecor = createSubDecor();
}
private ViewGroup createSubDecor() {
TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
mIsFloating = a.getBoolean(R.styleable.AppCompatTheme_android_windowIsFloating, false);
final LayoutInflater inflater = LayoutInflater.from(mContext);
ViewGroup subDecor = null;
if (!mWindowNoTitle) {
if (mIsFloating) {
// If we're floating, inflate the dialog title decor
subDecor = (ViewGroup) inflater.inflate( //根据R.styleable属性加载不同的布局文件
R.layout.abc_dialog_title_material, null);
// Floating windows can never have an action bar, reset the flags
mHasActionBar = mOverlayActionBar = false;
} else if (mHasActionBar) {
...
}
} else {
...
}
final ContentframeLayout contentView = (ContentframeLayout) subDecor.findViewById(
R.id.action_bar_activity_content);
final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
if (windowContentView != null) {
// There might be Views already added to the Window's content view so we need to
// migrate them to our content view
while (windowContentView.getChildCount() > 0) {
final View child = windowContentView.getChildAt(0);
windowContentView.removeViewAt(0);
contentView.addView(child);
}
// Change our content frameLayout to use the android.R.id.content id.
// Useful for fragments.
windowContentView.setId(View.NO_ID);
contentView.setId(android.R.id.content); //设置subDecor的contentView的id为android.R.id.content
}
// Now set the Window's content view with the decor
mWindow.setContentView(subDecor); //根据加载的布局文件创建的view - subDecor设置为PhoneWindow的contentView
return subDecor;
}
}
根据R.styleable属性加载不同的布局文件,根据布局文件创建ViewGroup并保存到mSubDecor
将加载的布局文件创建的view - subDecor设置为PhoneWindow的contentView,即mWindow.setContentView(subDecor);
其中subDecor的contentView的id为android.R.id.content,因此创建的subDecor为ViewGroup,subDecor中真正的内容是id为android.R.id.content的用户自定义View
public abstract class LayoutInflater {
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
final XmlResourceParser parser = res.getLayout(resource); //resource = R.layout.activity_main
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
final Context inflaterContext = mContext;
final AttributeSet attrs = Xml.asAttributeSet(parser); //attrs为布局文件的attr参数
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
View result = root;
try {
// Look for the root node.
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_document) {
// Empty
}
if (type != XmlPullParser.START_TAG) {
throw new InflateException(parser.getPositionDescription()
+ ": No start tag found!");
}
final String name = parser.getName(); //获取根节点的标签名
if (TAG_MERGE.equals(name)) { //如果是merge标签,一般走else
...
} else {
// Temp is the root view that was found in the xml
//根据根节点的标签名生成root view --- temp
final View temp = createViewFromTag(root, name, inflaterContext, attrs); //root是id为android.R.id.content的contentView,name为布局文件R.layout.activity_main的根节点的标签名,attrs为布局文件的attr参数
ViewGroup.LayoutParams params = null;
if (root != null) {
// Create layout params that match root, if supplied
//创建root view的布局属性params,例如宽高、字体大小
params = root.generateLayoutParams(attrs);
if (!attachToRoot) { //如果root不为null且attachToRoot设为false
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
temp.setLayoutParams(params); //将布局文件最外层的所有layout属性设置到布局文件的根节点temp
}
}
// Inflate all children under temp against its context.
rInflateChildren(parser, temp, attrs, true); //parser为根节点,temp为根节点对应的view,attrs为根节点对应的属性
// We are supposed to attach all the views we found (int temp)
// to root. Do that now.
if (root != null && attachToRoot) {
root.addView(temp, params);
}
// Decide whether to return the root that was passed in or the
// top view found in xml.
if (root == null || !attachToRoot) { //如果id为android.R.id.content的root为空,或者attachToRoot为false
result = temp; //直接返回布局文件的根节点对应的view --- temp
}
}
}
return result;
}
}
}
public abstract class LayoutInflater {
final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException {
rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
}
void rInflate(XmlPullParser parser, View parent, Context context,
AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
final int depth = parser.getDepth(); //获取子节点的深度
int type;
boolean pendingRequestFocus = false;
while (((type = parser.next()) != XmlPullParser.END_TAG || //使用while循环
parser.getDepth() > depth) && type != XmlPullParser.END_document) {
final String name = parser.getName(); //获取子节点的name
final View view = createViewFromTag(parent, name, context, attrs); //创建子节点对应的view
final ViewGroup viewGroup = (ViewGroup) parent; //viewGroup即为root view
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflateChildren(parser, view, attrs, true); //递归调用
viewGroup.addView(view, params); //将创建的子节点对应的view添加到root view中
}
}
}
总结:
1> ViewTree创建总结 — 即创建DecorView的过程
a. 在Activity的onCreate中调用setContentView,传入自定义布局文件对应的id
b. 调用createSubDecor根据R.styleable属性加载不同的布局文件创建ViewGroup — subDecor,并将subDecor设置为PhoneWindow的contentView,subDecor的contentView对应的id设置为android.R.id.content
c. 调用inflate首先找到自定义布局文件的根节点对应的View — temp,然后在rInflateChildren中的while循环递归创建子节点对应的View,将创建的子View添加到temp中,最终将temp添加到android.R.id.content对应的contentView中
2> inflate参数总结
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)
//parser对应R.layout.activity_main
//root对应android.R.id.content
//attachToRoot:true表示将R.layout.activity_main中的根节点对应的view添加到android.R.id.content对应的contentView中,false表示直接将parser中的根节点对应的view直接作为android.R.id.content对应的contentView,而不是添加进去
a. 如果root为null,attachToRoot将失去作用,设置任何值都没有意义
b. 如果root不为null,attachToRoot参数默认为true
b.1 attachToRoot设为true,则会给加载的布局文件的指定一个父布局,即id为android.R.id.content的root
b.2 attachToRoot设为false,则会将布局文件最外层的所有layout属性进行设置,当该view被添加到父view当中时,这些layout属性会自动生效
ViewTree的绘制是从onResume开始,在Activity启动过程中会调用到handleResumeActivity,进而调用到onResume,所以从handleResumeActivity开始分析ViewTree的绘制
frameworksbasecorejavaandroidappActivityThread.java:
public final class ActivityThread {
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
boolean isForward, String reason) {
if (!performResumeActivity(r, finalStateRequest, reason)) { //最终调用到onResume
return;
}
final Activity a = r.activity;
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow(); //获取当前Activity对应的PhoneWindow
View decor = r.window.getDecorView(); //获得当前Activity对应的DecorView
ViewManager wm = a.getWindowManager(); //获得当前Activity对应的WindowManagerImpl
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_base_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true; //标记根布局DecorView已经添加到窗口
wm.addView(decor, l); //将根布局DecorView添加到当前Activity的窗口上面
}
}
}
}
}
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
}
public final class WindowManagerGlobal {
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
ViewRootImpl root;
root = new ViewRootImpl(view.getContext(), display);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
root.setView(view, wparams, panelParentView);
}
}
ViewRootImpl.java (frameworksbasecorejavaandroidview) 329673 2017/8/25
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view; //将DecorView保存到ViewRootImpl.mView
mAttachInfo.mRootView = view;
mAdded = true; //标记已添加DecorView
requestLayout(); //请求布局
}
}
}
}
在ViewTree的创建过程中分析了DecorView的创建过程,在ViewRootImpl.setView中将创建的DecorView保存到ViewRootImpl.mView
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void scheduleTraversals() {
if (!mTraversalScheduled) {
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
performTraversals();
}
}
private void performTraversals() {
final View host = mView;
//DecorView所需要窗口的宽高
int desiredWindowWidth;
int desiredWindowHeight;
if (mFirst) { //如果是第一次绘制DecorView
mFullRedrawNeeded = true;
mLayoutRequested = true;
final Configuration config = mContext.getResources().getConfiguration();
if (shouldUseDisplaySize(lp)) { //如果窗口的类型是有状态栏的,那么顶层视图DecorView所需要窗口的宽度和高度就是除了状态栏
Point size = new Point();
mDisplay.getRealSize(size);
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
} else { //否则顶层视图DecorView所需要窗口的宽度和高度就是整个屏幕的宽高
desiredWindowWidth = dipToPx(config.screenWidthDp);
desiredWindowHeight = dipToPx(config.screenHeightDp);
}
}
//获得view宽高的测量规格,mWidth和mHeight表示窗口的宽高,lp.widthhe和lp.height表示DecorView根布局宽和高
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); //测量:测量View的大小
performLayout(lp, mWidth, mHeight); //布局:设置View的位置
performDraw(); //绘制:绘制View内容到Canvas画布上
}
}
在ViewRootImpl.performTraversals中分别调用performMeasure、performLayout、performDraw进行View的测量、布局、绘制
2.1 分析performMeasureViewRootImpl.java (frameworksbasecorejavaandroidview) 329673 2017/8/25
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
View.java (frameworksbasecorejavaandroidview) 1045455 2017/8/25
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
frameLayout.java (frameworksbasecorejavaandroidwidget) 19293 2017/8/25
public class frameLayout extends ViewGroup {
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
//测量frameLayout下每个子视图View的宽和高
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,
child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
childState = combineMeasuredStates(childState, child.getMeasuredState());
if (measureMatchParentChildren) {
if (lp.width == LayoutParams.MATCH_PARENT ||
lp.height == LayoutParams.MATCH_PARENT) {
mMatchParentChildren.add(child);
}
}
}
}
// Account for padding too
maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
// Check against our minimum height and width
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
// Check against our foreground's minimum height and width
final Drawable drawable = getForeground();
if (drawable != null) {
maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
}
//设置当前frameLayout测量结果,此方法的调用表示当前View测量的结束
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
}
}
performMeasure总结:
DecorView在测量过程中会使用for循环测量每个子View的宽高,然后调用setMeasuredDimension设置当前的测量结果
因此在自定义View的过程中如果重写onMeasure方法,在测量结束之前必须调用setMeasuredDimension方法测量才有效
ViewRootImpl.java (frameworksbasecorejavaandroidview) 329673 2017/8/25
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
mInLayout = true;
final View host = mView;
try {
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
mInLayout = false;
} finally {
}
mInLayout = false;
}
}
View.java (frameworksbasecorejavaandroidview) 1045455 2017/8/25
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
public void layout(int l, int t, int r, int b) {
//记录上一次布局的坐标
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
//调用setframe设置DecorView的坐标,DecoreView的子view不在这里设置
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalframe(l, t, r, b) : setframe(l, t, r, b);
//调用onLayout设置DecoreView子view的坐标
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
}
}
}
a. setframe设置DecorView父View的坐标
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
protected boolean setframe(int left, int top, int right, int bottom) {
boolean changed = false; //判断当前view的布局是否有变化
if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
changed = true;
//保存上一次布局的位置
int oldWidth = mRight - mLeft;
int oldHeight = mBottom - mTop;
int newWidth = right - left;
int newHeight = bottom - top;
boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
//清除上一次的布局
invalidate(sizeChanged);
//设置当前view的最新位置
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
if (sizeChanged) {
sizeChange(newWidth, newHeight, oldWidth, oldHeight);
}
}
return changed;
}
}
b. onLayout设置DecoreView子View的坐标
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
}
frameLayout.java (frameworksbasecorejavaandroidwidget) 19293 2017/8/25
public class frameLayout extends ViewGroup {
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutChildren(left, top, right, bottom, false );
}
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();
//遍历DecorView的所有子view
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
//只有View可见度不为GONE才会参与布局
if (child.getVisibility() != GONE) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
//计算子view的左上角坐标(childLeft, childTop),计算子view的宽高
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
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
}
performLayout总结:
DecorView在布局过程中首先会调用setframe布局DecorView父View,然后会调用onLayout使用for循环布局DecorView的每个子View
在调用onLayout布局子View时,只有可见度不为GONE的view才会参与布局
ViewRootImpl.java (frameworksbasecorejavaandroidview) 329673 2017/8/25
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
private void performDraw() {
final boolean fullRedrawNeeded = mFullRedrawNeeded;
mFullRedrawNeeded = false;
try {
draw(fullRedrawNeeded);
} finally {
}
}
private void draw(boolean fullRedrawNeeded) {
Surface surface = mSurface;
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) { //ViewRootImpl.setView将DecorView保存到ViewRootImpl.mView的同时,也保存到了mAttachInfo.mRootView,mAttachInfo.mRootView = DecorView
return;
}
}
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
final Canvas canvas;
try {
final int left = dirty.left;
final int top = dirty.top;
final int right = dirty.right;
final int bottom = dirty.bottom;
canvas = mSurface.lockCanvas(dirty); //从surface对象中获得canvas变量
}
try {
try {
canvas.translate(-xoff, -yoff); //调整画布的位置
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
attachInfo.mSetIgnoreDirtyState = false;
mView.draw(canvas); //开始绘制View视图
drawAccessibilityFocusedDrawableIfNeeded(canvas);
}
}
return true;
}
}
View.java (frameworksbasecorejavaandroidview) 1045455 2017/8/25
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
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;
// 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
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
drawAutofilledHighlight(canvas);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
// Step 7, draw the default focus highlight
drawDefaultFocusHighlight(canvas);
if (debugDraw()) {
debugDrawFocus(canvas);
}
// we're done...
return;
}
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);
drawAutofilledHighlight(canvas);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
if (debugDraw()) {
debugDrawFocus(canvas);
}
}
protected void onDraw(Canvas canvas) {
}
}
step1: 绘制视图View的背景 — drawBackground
step2: 保存画布canvas的边框参数
step3: 绘制视图View的内容 — onDraw
step4: 绘制当前视图View的子视图 — dispatchDraw
step5: 绘制滑动时边框的渐变效果
step6: 绘制滚动条 — onDrawForeground
performDraw总结:
View绘制的画布参数canvas是由surface对象获得,因此View视图绘制最终会绘制到Surface中
View绘制调用了onDraw方法,通过该方法把View内容的绘制逻辑留给子类去实现,因此自定义View需要重写父类的onDraw方法来实现View内容绘制
Activity启动流程会执行startSpecificActivityLocked
- 首先调用ActivityThread.performLaunchActivity,此函数最终调用onCreate -> onStart,在onCreate中调用SetContentView创建DecorView
- 然后调用ActivityThread.handleResumeActivity,此函数首先调用onResume,然后调用ViewRootImpl.setView将创建的DecorView保存到ViewRootImpl.mView,在ViewRootImpl.performTraversals中分别调用performMeasure、performLayout、performDraw进行View的测量、布局、绘制
好文章:
https://www.jianshu.com/p/83438249ae91
https://blog.csdn.net/fjz2014/article/details/46731893
https://blog.csdn.net/feiduclear_up/article/details/46772477



