9.view 作图过程,讲讲draw/onDraw和drawChild

2023-11-05 12:18

本文主要是介绍9.view 作图过程,讲讲draw/onDraw和drawChild,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

9.view 绘制过程,讲讲draw/onDraw和drawChild
转载请标明出处: 

http://blog.csdn.net/yujun411522/article/details/46226135
本文出自:【yujun411522的博客】


 9.1 view的绘制过程
view的绘制过程在UI中还是非常重要的,view的绘制是从根节点,自上而下的一个过程,主要经历三个过程:
     
先看performTraversals函数代码,这段代码很长,主要工作可以归结为三个函数:   
 ViewRootImpl.java中performTraversals()private void perfromTraversals(){final View host = mView;....//1.measure过程host.measure(childWidthMeasureSpec,childHeightMeasureSpec);....//2.layout过程host.layout(0,0,host.getMeasureWidth(),host.getMeasureHeight);        .....//3 draw过程draw(fullRedrawNeeded);}
下面分别来介绍着三个过程:

9.1.1 measure
measure中主要涉及以下几个方法:
public final void measure(int widthMeasureSpec,int heightMeasureSpec)
protected voidonMeasure(int widthMeasureSpec,int heightMeasureSpec)
protected final void setMeasureDimension(int measuredWidht,int measureHeight)
这里先还要介绍一个类:MeasureSpec。引入这个类的主要目的是在android中子view的尺寸还要受到父view的限制
一个MeasureSpec是一个int 类型,共32b,有两部分组成:高两位是mode,剩下的30位是size
mode中有三种模式:
UNSPECIFIED:不受到父view的限制,可以设置为任意值。基本上不用到。
EXACTLY:父view中指定子view的大小,不管子view如果设置。比如:match_parent、或者具体的大小
AT_MOST:父view中指定子view最多只能这么多。比如:wrap_content
在ViewRootImpl中已经说明了对应关系    
  private int getRootMeasureSpec(int windowSize, int rootDimension) {int measureSpec;switch (rootDimension) {case ViewGroup.LayoutParams.MATCH_PARENT</strong>:// Window can't resize. Force root view to be windowSize.measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);break;case ViewGroup.LayoutParams.WRAP_CONTENT</strong>:// Window can resize. Set max size for root view.measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);break;default:// Window wants to be an exact size. Force root view to be that size.measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);break;}return measureSpec;}
可以看出match_parent具体尺寸对应的都是EXACTLYwrap_content对应的是AT_MOST
接下来看一下view.measure函数的处理过程    
 public final void measure (int widthMeasureSpec, int heightMeasureSpec) {....// measure ourselves, this should set the measured dimension flag backonMeasure(widthMeasureSpec, heightMeasureSpec);....    mOldWidthMeasureSpec = widthMeasureSpec;mOldHeightMeasureSpec = heightMeasureSpec;}
调用了onMeasure方法:   
  protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimensiongetDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));}
里面涉及了三个方法: getSuggestedMinimumWidth,getDefaultSize,setMeasuredDimension
先看view.getSuggestedMinimumWidth()   
  protected int getSuggestedMinimumWidth() {int suggestedMinWidth = mMinWidth ;if (mBGDrawable != null) {final int bgMinWidth = mBGDrawable.getMinimumWidth();if (suggestedMinWidth < bgMinWidth) {suggestedMinWidth = bgMinWidth;}}return suggestedMinWidth</strong>;//返回背景图最小值和view最小值中的较大者}
getSuggestedMinimumHeight()方法类似,不做介绍。
再看view.getDefaultSize()方法      
  public static int getDefaultSize(int size, int measureSpec) {int result = size;int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);switch (specMode) {case MeasureSpec.UNSPECIFIED:result = size;//UNSPECIFIED,返回值为size值break;case MeasureSpec.AT_MOST://AT_MOST,返回值为size值case MeasureSpec.EXACTLY:result = specSize;//EXACTLY,返回值为specSize值break;}return result;}
再看setMeasuredDimension方法:       
 protected final void setMeasuredDimension (int measuredWidth, int measuredHeight) {mMeasuredWidth = measuredWidth;mMeasuredHeight = measuredHeight;mPrivateFlags |= MEASURED_DIMENSION_SET ;}
我们在override完onMeasure方法之后一定要执行这个setMeasuredDimension方法,这个方法的目的就是保存measure之后的值。

以上介绍的是view的measure过程,如果是viewGroup时流程会有区别:viewGroup循环measure所有子view。先看一看ViewGroup中的measureChildren方法   
  /*** Ask all of the children of this view to measure themselves, taking into account both the MeasureSpec requirements for this view and its padding.* We skip children that are in the GONE state The heavy lifting is done in getChildMeasureSpec.     * @param widthMeasureSpec The width requirements for this view* @param heightMeasureSpec The height requirements for this view*/protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {final int size = mChildrenCount;final View[] children = mChildren;for (int i = 0; i < size; ++i) {final View child = children[i];if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {//过滤掉GONE的viewmeasureChild(child, widthMeasureSpec, heightMeasureSpec);//让所有的子view measure自己。调用measureChild方法。}}}
调用ViewGroup.measureChild(view,int,int)。类似measureChild函数还有measureChildWithMargins(view,int,int))
  /*** Ask one of the children of this view to measure itself, taking into account both the MeasureSpec requirements for this view and its padding.* The heavy lifting is done in getChildMeasureSpec.* @param child The child to measure* @param parentWidthMeasureSpec The width requirements for this view* @param parentHeightMeasureSpec The height requirements for this view*/protected void measureChild(View child, int parentWidthMeasureSpec,int parentHeightMeasureSpec) {final LayoutParams lp = child.getLayoutParams();//主要工作还是在getChildMeasureSpec函数中进行final int childWidthMeasureSpec = getChildMeasureSpec</strong>(parentWidthMeasureSpec,mPaddingLeft+mPaddingRight, lp.width);final int childHeightMeasureSpec = getChildMeasureSpec</strong>(parentHeightMeasureSpec, mPaddingTop+ mPaddingBottom, lp.height);child.measure</strong>(childWidthMeasureSpchildHeightMeasureSpec ec, childHeightMeasureSpec);//计算完childWidthMeasureSpec 、childHeightMeasureSpec 调用子view的measure方法}
其中调用了ViewGroup.getChildMeasureSpec方法,这个方法比较复杂: 此方法的目的是根据父view对子view的MeasureSpec和子view的layout参数产生一个最合适的MeasureSpec  
  / * Does the hard part of measureChildren: figuring out the MeasureSpec to pass to a particular child. This method figures out the right MeasureSpec for one dimension (height or width) of one child view. The goal is to combine information from ourMeasureSpec with the LayoutParams of the child to get the best possible results. For example,if the this view knows its size (because its MeasureSpec has a mode of EXACTLY), and the child has indicated in its LayoutParamsthat it wants to be the same size as the parent, the parent should ask the child to layout given an exact size.* @param spec The requirements for this view* @param padding The padding of this view for the current dimension and  margins, if applicable* @param childDimension How big the child wants to be in the current    dimension* @return a MeasureSpec integer for the child*/public static int getChildMeasureSpec(int spec, int padding, int childDimension) {int specMode = MeasureSpec.getMode(spec);int specSize = MeasureSpec.getSize(spec);int size = Math.max(0, specSize - padding);int resultSize = 0;int resultMode = 0;switch (specMode) {// Parent has imposed an exact size on uscase MeasureSpec.EXACTLY:if (childDimension >= 0) {resultSize = childDimension;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {// Child wants to be our size. So be it.resultSize = size;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.WRAP_CONTENT) {// Child wants to determine its own size. It can't be// bigger than us.resultSize = size;resultMode = MeasureSpec.AT_MOST;}break;// Parent has imposed a maximum size on uscase MeasureSpec.AT_MOST:if (childDimension >= 0) {// Child wants a specific size... so be itresultSize = childDimension;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {// Child wants to be our size, but our size is not fixed.// Constrain child to not be bigger than us.resultSize = size;resultMode = MeasureSpec.AT_MOST;} else if (childDimension == LayoutParams.WRAP_CONTENT) {// Child wants to determine its own size. It can't be// bigger than us.resultSize = size;resultMode = MeasureSpec.AT_MOST;}break;// Parent asked to see how big we want to becase MeasureSpec.UNSPECIFIED:if (childDimension >= 0) {// Child wants a specific size... let him have itresultSize = childDimension;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {// Child wants to be our size... find out how big it should// beresultSize = 0;resultMode = MeasureSpec.UNSPECIFIED;} else if (childDimension == LayoutParams.WRAP_CONTENT) {// Child wants to determine its own size.... find out how// big it should beresultSize = 0;resultMode = MeasureSpec.UNSPECIFIED;}break;}return MeasureSpec.makeMeasureSpec(resultSize, resultMode);}
可以看出上面的处理过程还是挺复杂的,该方法返回父view对子view的一个32b的measureSpec。
measureChild方法最后调用view的measure方法,再按照view的measure流程绘制。
具体的可以参看不同view控件,以及measure的实现。

9.1.2 layout
measure的过程就是确定view的大小而layout的过程则是确定view位置
看一下view.layout()方法:   
  public void layout(int l, int t, int r, int b) {int oldL = mLeft ;int oldT = mTop ;int oldB = mBottom ;int oldR = mRight ;boolean changed = setFrame(l, t, r, b);if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED ) {if (ViewDebug. TRACE_HIERARCHY) {ViewDebug. trace( this, ViewDebug.HierarchyTraceType.ON_LAYOUT );}onLayout(changed, l, t, r, b);//调用onLayout方法mPrivateFlags &= ~LAYOUT_REQUIRED ;if (mOnLayoutChangeListeners != null) {ArrayList<OnLayoutChangeListener> listenersCopy =(ArrayList<OnLayoutChangeListener>) mOnLayoutChangeListeners .clone();int numListeners = listenersCopy.size();for (int i = 0; i < numListeners; ++i) {listenersCopy.get(i).onLayoutChange(this , l, t, r, b, oldL, oldT, oldR, oldB);//通知对象的listener调用onLayoutChange}}}mPrivateFlags &= ~FORCE_LAYOUT ;}     
调用onLayout方法,再看onLayout方法是一个空实现:     
    protected void onLayout( boolean changed, int left, int top, int right, int bottom) {    }
也就是我们需要自己实现onLayout方法。
这是view的layout过程,再看viewGroup过程,先看viewGroup.layout方法:   
 @Overridepublic final void layout(int l, int t, int r, int b) {if (mTransition == null || !mTransition.isChangingLayout()) {super.layout</strong>(l, t, r, b);//调用父类view的layout方法</strong>} else {// record the fact that we noop'd it; request layout when transition finishesmLayoutSuppressed = true;}}
调用父类view的layout方法,再看onLayout方法   
protected abstract void onLayout(boolean changed,int l, int t, int r, int b);
viewGroup.onLayout是抽象函数。
ViewGroup对应的就是一个个具体的布局,看具体布局是如何实现的。
LinearLayout的layoutVertical方法 
  void layoutVertical() {// ....     for (int i = 0; i < count; i++) {final View child = getVirtualChildAt(i);if (child == null) {childTop += measureNullChild(i);} else if (child.getVisibility() != GONE) {childTop += lp. topMargin ;setChildFrame</strong>(child, childLeft, childTop + getLocationOffset(child),childWidth, childHeight);childTop += childHeight + lp. bottomMargin + getNextLocationOffset(child);i += getChildrenSkipCount(child, i);}}}
其中出现了LinearLayout.setChildFrame方法:     
    private void setChildFrame(View child, int left, int top, int width, int height) {       child.layout(left, top, left + width, top + height);}
再看FrameLayout的onLayout方法: 
   protected void onLayout( boolean changed, int left, int top, int right, int bottom) {final int count = getChildCount();        for (int i = 0; i < count; i++) {final View child = getChildAt(i);if (child.getVisibility() != GONE) {final LayoutParams lp = (LayoutParams) child.getLayoutParams();final int width = child.getMeasuredWidth();final int height = child.getMeasuredHeight();                    ..switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK ) {..}switch (verticalGravity) {..}child.layout</strong>(childLeft, childTop, childLeft + width, childTop + height);}}}   
可以看出都是调用子view中的layout方法。所以在自定义view的时候需要自己重写view的onLayout方法。

9.1.3 draw
得到位置之后,最后就是绘制view了。
view做的事情主要有以下几个部分:
1. Draw the background
2. If necessary, save the canvas' layers to prepare for fading
3. Draw view's content
4. Draw children
5. If necessary, draw the fading edges and restore layers
6. Draw decorations (scrollbars for instance)
比较重要的是step3和step4:       
   // Step 3, draw the contentif (!dirtyOpaque) onDraw(canvas);其中onDraw实现:空实现。因为view怎样展示是由具体的view自行实现。protected void onDraw(Canvas canvas) {}// Step 4, draw the childrendispatchDraw(canvas);
里面涉及了两个函数,onDraw和dispatchDraw函数,先看onDraw函数:
protected void onDraw(Canvas canvas) { }//空实现,因为view到底怎么展示是view自己决定的,所以要空实现
再看dispatchDraw方法:
protected void dispatchDraw(Canvas canvas) {  }也是空实现,因为view中没有子view,也就不需要向子view派发draw请求了,所以是空实现。
 
viewGroup中没有重写draw函数,所以是继承view的draw函数; 
viewGroup中也没有重写onDraw函数,所以是继承view的onDraw函数,也就是空实现
再看viewGroup中的viewGroup.dispatchDraw(Canvas)方法:      
   protected void dispatchDraw(Canvas canvas) {final int count = mChildrenCount;final View[] children = mChildren ;int flags = mGroupFlags ;       ..          if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {for (int i = 0; i < count; i++) {final View child = children[i];if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {more |= drawChild</strong>(canvas, child, drawingTime);}}} else {for (int i = 0; i < count; i++) {final View child = children[getChildDrawingOrder(count, i)];if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {more |=drawChild</strong>(canvas, child, drawingTime);}}}// Draw any disappearing views that have animationsif (mDisappearingChildren != null) {final ArrayList<View> disappearingChildren = mDisappearingChildren ;final int disappearingCount = disappearingChildren.size() - 1;// Go backwards -- we may delete as animations finishfor (int i = disappearingCount; i >= 0; i--) {final View child = disappearingChildren.get(i);more |= drawChild</strong>(canvas, child, drawingTime);}}..}
又调用了drawChild(Canvas,View,Long)方法,此方法主要作用是draw viewGroup中的某一个view,代码太长就不贴出现了。

这篇关于9.view 作图过程,讲讲draw/onDraw和drawChild的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/349801

相关文章

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

作业提交过程之HDFSMapReduce

作业提交全过程详解 (1)作业提交 第1步:Client调用job.waitForCompletion方法,向整个集群提交MapReduce作业。 第2步:Client向RM申请一个作业id。 第3步:RM给Client返回该job资源的提交路径和作业id。 第4步:Client提交jar包、切片信息和配置文件到指定的资源提交路径。 第5步:Client提交完资源后,向RM申请运行MrAp

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

Solr 使用Facet分组过程中与分词的矛盾解决办法

对于一般查询而言  ,  分词和存储都是必要的  .  比如  CPU  类型  ”Intel  酷睿  2  双核  P7570”,  拆分成  ”Intel”,”  酷睿  ”,”P7570”  这样一些关键字并分别索引  ,  可能提供更好的搜索体验  .  但是如果将  CPU  作为 Facet  字段  ,  最好不进行分词  .  这样就造成了矛盾  ,  解决方法

Python:豆瓣电影商业数据分析-爬取全数据【附带爬虫豆瓣,数据处理过程,数据分析,可视化,以及完整PPT报告】

**爬取豆瓣电影信息,分析近年电影行业的发展情况** 本文是完整的数据分析展现,代码有完整版,包含豆瓣电影爬取的具体方式【附带爬虫豆瓣,数据处理过程,数据分析,可视化,以及完整PPT报告】   最近MBA在学习《商业数据分析》,大实训作业给了数据要进行数据分析,所以先拿豆瓣电影练练手,网络上爬取豆瓣电影TOP250较多,但对于豆瓣电影全数据的爬取教程很少,所以我自己做一版。 目

MVC(Model-View-Controller)和MVVM(Model-View-ViewModel)

1、MVC MVC(Model-View-Controller) 是一种常用的架构模式,用于分离应用程序的逻辑、数据和展示。它通过三个核心组件(模型、视图和控制器)将应用程序的业务逻辑与用户界面隔离,促进代码的可维护性、可扩展性和模块化。在 MVC 模式中,各组件可以与多种设计模式结合使用,以增强灵活性和可维护性。以下是 MVC 各组件与常见设计模式的关系和作用: 1. Model(模型)

ORACLE语法-包(package)、存储过程(procedure)、游标(cursor)以及java对Result结果集的处理

陈科肇 示例: 包规范 CREATE OR REPLACE PACKAGE PACK_WMS_YX IS-- Author : CKZ-- Created : 2015/8/28 9:52:29-- Purpose : 同步数据-- Public type declarations,游标 退休订单TYPE retCursor IS REF CURSOR;-- RETURN vi_co_co

MFC中App,Doc,MainFrame,View各指针的互相获取

纸上得来终觉浅,为了熟悉获取方法,我建了个SDI。 首先说明这四个类的执行顺序是App->Doc->Main->View 另外添加CDialog类获得各个指针的方法。 多文档的获取有点小区别,有时间也总结一下。 //  App void CSDIApp::OnApp() {      //  App      //  Doc     CDocument *pD

OpenStack创建虚拟机过程

OpenStack创建虚拟机过程 一、在分析OpenStack创建虚拟机的过程之前,先来梳理一下需要用用到哪些组件。 二、每一步都需要去keystone去进行验证,下图有详细的流程。 登录界面或命令行通过RESTful API向keystone获取认证信息。keystone通过用户请求认证信息,并生成auth-token返回给对应的认证请求。界面或命令行通过RESTful API

Maven生命周期:深入理解构建过程

目录 1. Maven生命周期简介 2. 默认生命周期的阶段 3. 清理生命周期 4. 站点生命周期 5. Maven生命周期的灵活性 6. 结论         在Java开发中,Maven是一个不可或缺的工具,它通过自动化项目的构建、依赖管理和文档生成等任务,极大地提高了开发效率。Maven的核心之一是其构建生命周期,它定义了项目构建过程中的一系列阶段。在这篇文章中,我们将深