当前位置: 首页 > news >正文

Android View 的绘制流程


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

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

在ActivityThread.java 的handleResumeActivity中

//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

//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方法

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设置LayoutParamsview.setLayoutParams(wparams);mViews.add(view);mRoots.add(root);mParams.add(wparams);root.setView(view, wparams, panelParentView, userId);//...  
}

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

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的parentview.assignParent(this);
}

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

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

预测量

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();//默认320dpres.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;}}
}

测量

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);}
}
//View.java
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {//...//最终执行到对应view的onMeasure中onMeasure(widthMeasureSpec, heightMeasureSpec);//...
}

布局

//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);//...
}

绘制

//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);//...
}


http://www.mrgr.cn/news/31166.html

相关文章:

  • 复制这个盒子内容的时候,会触发这个盒子的点击事件
  • C#自定义曲线绘图面板
  • Xinstall助力App推广,下载自动绑定提升转化率
  • 15.多线程概述一(下篇)
  • 如何限制任何爬虫爬取网站的图片
  • 无人机之4G模块的主要功能和优势
  • 【时时三省】(C语言基础)指针笔试题1
  • 专利管理系统如何确保专利资产持续有效?
  • 技术老总眼中的品宣与促销:挑战与对策
  • 【算法竞赛】栈
  • QT的dropEvent函数进入不了
  • ASPICE认证、咨询和培训的价值是什么?
  • 零基础玩转实在Agent -- 基础篇|实在Agent研究
  • 【北京迅为】《STM32MP157开发板使用手册》- 第四十一章 计数信号量实验
  • 二级C语言2024-3易错题
  • 【ppt2svg svg2png/jpg】ppt转图片解决方案
  • Pandas中df常用方法介绍
  • C++日期类详解 第二级支线任务
  • FB FC里调用全局变量注意事项
  • 用 JS 实现一个发布订阅模式