栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Android系统之ViewTree的创建与绘制

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Android系统之ViewTree的创建与绘制

1. ViewTree的创建

在分析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

1.2 分析inflate
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属性会自动生效

2. ViewTree的绘制

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 分析performMeasure
ViewRootImpl.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方法测量才有效

2.2 分析performLayout
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才会参与布局

2.3 分析performDraw
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内容绘制

3. ViewTree的创建与绘制总结

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/583564.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号