Android View 的绘制流程


view作为构成android界面的基本元素,深入了解view的绘制流程对开发人员来说是很有必要的。

我们创建一个Activity都会在onCreate方法中写setContentView(layoutResId),把我们的布局传进去,那此时我们的布局就添加到屏幕上了吗?当然不是的。那我们的这个布局是在什么时候添加进屏幕的呢?

在ActivityThread.java 的handleResumeActivity中

java 复制代码
//ActivityThread.java
@Override
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,boolean
isForward, String reason) {
//...
wm.addView(decor, l);
//...
}

这里调用wm的addView,才把DecorView添加到窗口,wm其实就是WindowManagerImpl,接着我们跟进到WindowManagerImpl的addView

java 复制代码
//WindowManagerImpl.java
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyTokens(params);
    mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
    mContext.getUserId());
}

这里调用的是mGlobal的addView方法,mGlobal其实就是WindowManagerGlobal,是管理整个进程所有窗口信息的,接下来看看WindowManagerGlobal的addView方法

java 复制代码
public void addView(View view, ViewGroup.LayoutParams params,
    Display display, Window parentWindow, int userId) {
    //...
    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
    ViewRootImpl root;
    if (windowlessSession == null) {
        root = new ViewRootImpl(view.getContext(), display);
     } else {
         root = new ViewRootImpl(view.getContext(), display,windowlessSession);
     }
//为DecorView设置LayoutParams
    view.setLayoutParams(wparams);
    mViews.add(view);
    mRoots.add(root);
    mParams.add(wparams);

    root.setView(view, wparams, panelParentView, userId);
    //...  
}

接下来才到关键的地方了,ViewRootImpl的setView方法

java 复制代码
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
    //...
    //遍历view树逻辑
    requestLayout();
    //...
    //将窗口添加到WMS上面
    res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
            getHostVisibility(),mDisplay.getDisplayId(), userId,
                            mInsetsController.getRequestedVisibilities(), inputChannel,
                            mTempInsets,mTempControls);
    //...
    //将ViewRootImpl设置为DecorView的parent
    view.assignParent(this);
}

在这里的requestLayout,最终会走到performTraversals方法,这里才真正开始遍历view的measure、layout、draw等流程

java 复制代码
private void performTraversals() {
    //...
    //预测量,最多会执行3次onMeasure
    // Ask host how big it wants to be
    windowSizeMayChange |= measureHierarchy(host, lp, mView.getContext().getResources(),
    desiredWindowWidth, desiredWindowHeight);
    //...
    // Ask host how big it wants to be
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    //...
    performLayout(lp, mWidth, mHeight);
    //...
    if (!performDraw() && mSyncBufferCallback != null) {
        mSyncBufferCallback.onBufferReady(null);
    }
    //...
}

预测量

java 复制代码
private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,
    final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {
    //...
  
    if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
        final DisplayMetrics packageMetrics = res.getDisplayMetrics();
        //默认320dp
        res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);
        if (baseSize != 0 && desiredWindowWidth > baseSize) {
            childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width,lp.privateFlags);
            childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height,
                        lp.privateFlags);
            //第一次预测量
            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
            if((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0 {
                goodMeasure = true;
            } else {
                //宽度不够则再将剩余空间一般给child继续测量
                baseSize = (baseSize + desiredWindowWidth) / 2;
                childWidthMeasureSpec = getRootMeasureSpec(baseSize,
                    lp.width,lp.privateFlags);
                performMeasure(childWdithMeasureSpec,childHeightMeasureSpec);
                if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
                        if (DEBUG_DIALOG) Log.v(mTag, "Good!");
                        goodMeasure = true;
                 }
            }
        }
    }
           //如果宽度还不满足,则将全部宽度给child去测量
        if (!goodMeasure) {
            childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width,
                    lp.privateFlags);
            childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height,
                    lp.privateFlags);
            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
            if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight())
            {
                windowSizeMayChange = true;
            }
        }
}

测量

java 复制代码
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        if (mView == null) {
            return;
        }
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try {
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
}
java 复制代码
//View.java
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
    //...
    //最终执行到对应view的onMeasure中
    onMeasure(widthMeasureSpec, heightMeasureSpec);
    //...
}

布局

java 复制代码
//ViewRootImpl.java
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
            int desiredWindowHeight) {

    //...
    host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
    //...
}


//View.java
public void layout(int l, int t, int r, int b) {
    //...
    //在这里给左上右下去赋值,之后的getWidth和getHeight才能获取到值
    boolean changed = isLayoutModeOptical(mParent) ?
                setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
    //...
    //这里调用到对应view的onLayout方法
    onLayout(changed, l, t, r, b);
    //...
}

绘制

java 复制代码
//ViewRootImpl.java
private boolean performDraw() {
    //...
    boolean canUseAsync = draw(fullRedrawNeeded, usingAsyncReport && mSyncBuffer);
    //...
}

private boolean draw(boolean fullRedrawNeeded, boolean forceDraw) {
    //...
    if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
        scalingRequired, dirty, surfaceInsets)) {
        return false;
    }
}

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
    //...
    //执行到对应view的draw方法
    mView.draw(canvas);
    //...
}
相关推荐
轻口味44 分钟前
Android应用性能优化
android
全职计算机毕业设计1 小时前
基于 UniApp 平台的学生闲置物品售卖小程序设计与实现
android·uni-app
dgiij1 小时前
AutoX.js向后端传输二进制数据
android·javascript·websocket·node.js·自动化
SevenUUp2 小时前
Android Manifest权限清单
android
高林雨露2 小时前
Android 检测图片抓拍, 聚焦图片后自动完成拍照,未对准图片的提示请将摄像头对准要拍照的图片
android·拍照抓拍
INSBUG4 小时前
CVE-2024-21096:MySQLDump提权漏洞分析
android·adb
Mercury Random5 小时前
Qwen 个人笔记
android·笔记
苏苏码不动了5 小时前
Android 如何使用jdk命令给应用/APK重新签名。
android
aqi006 小时前
FFmpeg开发笔记(五十三)移动端的国产直播录制工具EasyPusher
android·ffmpeg·音视频·直播·流媒体