博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
View的绘制流程
阅读量:4330 次
发布时间:2019-06-06

本文共 7393 字,大约阅读时间需要 24 分钟。

View的绘制流程分析

1、首先从Activty 的生命周期开始setContentVie()开始。在activity的setContentView 方法里面如:

public void setContentView(@LayoutRes int layoutResID) {        getWindow().setContentView(layoutResID);        initWindowDecorActionBar();    }

调用的是phoneWindow.setContentView(@LayoutRes int layoutResID) 方法:

@Overridepublic void setContentView(int layoutResID) {     // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window    // decor, when theme attributes and the like are crystalized. Do not check the feature        // before this happens.        //一开始mContactParent 是为null的,因此会先初始化DecorVIew        if (mContentParent == null) {            installDecor();        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {            mContentParent.removeAllViews();        }        ....      if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,                    getContext());            transitionTo(newScene);        } else {            //将layout 解析并add 到mContentParent中            mLayoutInflater.inflate(layoutResID, mContentParent);        }ontentParent.addView(view, params);        }        ...

主要关注installDecor(),

private void installDecor() {     if (mDecor == null) {            // new 一个Decorview 的对象            mDecor = generateDecor(-1); //1            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);            mDecor.setIsRootNamespace(true);            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);            }            .....            if (mContentParent == null) {            mContentParent = generateLayout(mDecor); //②

首先会先执行会上面的方法1, new 了一个Decorview的对象,而DecorView则是PhoneWindow类的一个内部类,继承于FrameLayout,由此可知它也是一个ViewGroup,那么DecorView起到什么作用呢?

其实DecorView 是viewTree里的顶层View,是继承FrameLayout 的,有标题view和内容view这两个子元素,而内容view则是上面提到的mContentParent。我们接着看②号代码获取对应的mConentParent对应的View .

protected ViewGroup generateLayout(DecorView decor) {    ...    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);    ...

其中ID_ANDROID_CONTENT = com.android.internal.R.id.content; 对应我们的内容view 中,这个view 就是用来填充我们之前setContantView 方法传过来的layout 对应的view.

现在会回到之前的phoneWindow.setContentView() 在installDecor 后会通过LayoutInflate.infalte 将传过来的layout解析成view 并添加到mContentParent 中。

2、接下来到Activity 的onResume()阶段。

final void handleResumeActivity(IBinder token,            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {        ActivityClientRecord r = mActivities.get(token);        ...        if (a.mVisibleFromClient) {                    if (!a.mWindowAdded) {                        a.mWindowAdded = true;                        wm.addView(decor, l);        ...

在这个阶段会通过windowManager 将onCreate 创建的DecorView add 进去 。WindouwManager 的实现类是WindowManagerImpl如:

@Override    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {        applyDefaultToken(params);        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);    }

接下来调用到WindowManagerGlobal.addView 方法:

public void addView(View view, ViewGroup.LayoutParams params,            Display display, Window parentWindow) {        if (view == null) {            throw new IllegalArgumentException("view must not be null");                    ....                root = new ViewRootImpl(view.getContext(), display);            view.setLayoutParams(wparams);            mViews.add(view);            mRoots.add(root);            mParams.add(wparams);                    ...         try {                root.setView(view, wparams, panelParentView);            }         ...

在这里构造了ViewRootImpl类 .继续看root.setView 方法。

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {        synchronized (this) {            if (mView == null) {                mView = view;                mAttachInfo.mDisplayState = mDisplay.getState();                mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);                ....         // Schedule the first layout -before- adding to the window                // manager, to make sure we do the relayout before receiving                // any other events from the system.                requestLayout();                if ((mWindowAttributes.inputFeatures                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {                    mInputChannel = new InputChannel();                }        ....

接下来来调用了requestLaout 方法

@Override    public void requestLayout() {        if (!mHandlingLayoutInLayoutRequest) {            if (ViewDebugManager.DEBUG_REQUESTLAYOUT) {                Log.d(mTag, "requestLayout: mView = " + mView + ", this = " + this,                        new Throwable("requestLayout"));            }            checkThread();            mLayoutRequested = true;            scheduleTraversals();        }    }

在scheduleTraversals 里面将CALLBACK_TRAVERSAL 消息push (mChoreographer.postCallback) 到一个Choreographer的一个队列里面。

void scheduleTraversals() {        if (!mTraversalScheduled) {            mTraversalScheduled = true;            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();            if (ViewDebugManager.DEBUG_SCHEDULETRAVERSALS) {                Log.v(mTag, "scheduleTraversals: mTraversalBarrier = " + mTraversalBarrier                        + ",this = " + this, new Throwable("scheduleTraversals"));            }            mChoreographer.postCallback(                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);            if (!mUnbufferedInputDispatch) {                scheduleConsumeBatchedInput();            }            notifyRendererOfFramePending();            pokeDrawLockIfNeeded();        }    }

在Choreographer 在将CALLBACK_TRAVERSAL 这种类型的数据放到队列的同时会通知Choreographer 的FrameDisplayEventReceiver 接受surfaceflinger 上传的Vsync 信号,收到后执行doFrame方法,最后会执行到TraversalRunnable 的run()方法,执行doTraversal 操作。

final class TraversalRunnable implements Runnable {        @Override        public void run() {            doTraversal();        }    }
void doTraversal() {        if (ViewDebugManager.DEBUG_LIFECYCLE || ViewDebugManager.DEBUG_ENG) {            Log.v(mTag, "doTraversal: mTraversalScheduled = " + mTraversalScheduled + " mFisrt = "                  + mFirst + ",mTraversalBarrier = " + mTraversalBarrier + ",this = " + this);        }        if (mTraversalScheduled) {            mTraversalScheduled = false;            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);        if (mProfile) {                Debug.startMethodTracing("ViewAncestor");            }            performTraversals();            if (mProfile) {                Debug.stopMethodTracing();                mProfile = false;            }        }    }

doTraversal 主要调用了performTraversals()方法,在该方法里面分布调用performMeasure,performLayout,performDraw 分布完成view 的测量,布局,和绘制操作。

private void performTraversals() {        // cache mView since it is used so much below...        final View host = mView;        ...        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);        ...        performLayout(lp, mWidth, mHeight);        ...        performDraw();        ...

到了上面的流程后,就是对于的view 的测量,布局,和绘制操作流程分析了。 下一节分析ViewGroup的绘制流程

转载于:https://www.cnblogs.com/scoftlin/p/10553359.html

你可能感兴趣的文章
kibana的query string syntax 笔记
查看>>
旋转变换(一)旋转矩阵
查看>>
thinkphp3.2.3 bug集锦
查看>>
[BZOJ 4010] 菜肴制作
查看>>
C# 创建 读取 更新 XML文件
查看>>
KD树
查看>>
VsVim - Shortcut Key (快捷键)
查看>>
C++练习 | 模板与泛式编程练习(1)
查看>>
HDU5447 Good Numbers
查看>>
08.CXF发布WebService(Java项目)
查看>>
java-集合框架
查看>>
RTMP
查看>>
求一个数的整数次方
查看>>
点云PCL中小细节
查看>>
铁路信号基础
查看>>
RobotFramework自动化2-自定义关键字
查看>>
[置顶] 【cocos2d-x入门实战】微信飞机大战之三:飞机要起飞了
查看>>
BABOK - 需求分析(Requirements Analysis)概述
查看>>
第43条:掌握GCD及操作队列的使用时机
查看>>
Windows autoKeras的下载与安装连接
查看>>