Android基础学习(二十四)——View绘制-创新互联

1、Activity.setContentView
Activity.setContentView(layoutResID:int)
	PhoneWindow.setContentView(layoutResID:int)
    	PhoneWindow.installDecor
    	//mContentParent为DecorView
		LayoutInflater.inflate(layoutResID:int, mContentParent:ViewGroup)
    		//attachToRoot为 root != null
    		LayoutInflater.inflate(layoutResID:int, root:ViewGroup, attachToRoot:boolean) 
    			Resource res = getContext().getResources()
				XmlResourceParser parser = res.getLayout(layoutResID)
				LayoutInflater.inflate(parser:XmlResourceParser, root:ViewGroup, attachToRoot:boolean)
    				//获取layoutResID对应布局中的属性信息
					AttributeSet attrs = Xml.asAttributeSet(parser)
					String name = parser.getName()
    				//createViewFromTag方法根据tag标签属性来创建对应的View
					View temp = createViewFromTag(root, name, inflaterContext, attrs)
    				ViewGroup.LayoutParams params = root.generateLayoutParams(attrs)
    				//temp为xml布局文件的根View
    				//rInflateChildren,递归加载根View的所有子View
					LayoutInflater.rInflateChildren(parser, temp, attrs, true)
    					LayoutInflater.rInflate
    						View view = LayoutInflater.createViewFromTag
    						//这里的parent为temp
  							ViewGroup viewGroup = (ViewGroup)parent 
    						ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs)
    						//view会作为rInflateChildren方法的其中一个参数
    						LayoutInflater.rInflateChildren 
    							... ...
    						ViewGroup.addView
					root.addView(temp, params)	

(1)DecorView的创建时机:

成都创新互联公司服务紧随时代发展步伐,进行技术革新和技术进步,经过10年的发展和积累,已经汇集了一批资深网站策划师、设计师、专业的网站实施团队以及高素质售后服务人员,并且完全形成了一套成熟的业务流程,能够完全依照客户要求对网站进行网站设计、成都做网站、建设、维护、更新和改版,实现客户网站对外宣传展示的首要目的,并为客户企业品牌互联网化提供全面的解决方案。

Activity.setContentView->PhoneWindow.setContentView->PhoneWindow.installDecor ->PhoneWindow.generateDecor

(2)SetContentView主要做的事情:

①创建DecorView;②根据layoutResId创建View并添加到DecorView中

(3)LayoutParams

​ 在View绘制中,MeasureSpec封装了从父布局传递给子布局的布局要求。对于DecorView来说,其MeasureSpec由它自身的LayoutParams决定;对于除DecorView之外的普通View,其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams共同决定。

​ 每个View和ViewGroup都需要通过其父容器ViewGroup的generateLayoutParams方法来生成LayoutParams对象,且每个ViewGroup子类返回的LayoutParams一般来说都需要继承于MarginLayoutParams,这样才能具备解析layout_margin的能力,且还需要再根据自身ViewGroup提供的标签属性来进一步扩展MarginLayoutParams的功能。

2、View的绘制工作

ViewRoot负责执行View绘制的整个流程,每个应用程序窗口的decorView都有一个与之关联的ViewRoot对象,这种关联关系是由WindowManager来维护的,关联关系是在Activity启动时建立的:

ActivityThread.handleResumeActivity
	WindowManagerImpl.addView
		WindowManagerGlobal.addView  
			... ...   //将DecorView添加到Window中
			new ViewRootImpl(view.getContext(), display)  //创建ViewRootImpl对象
			ViewRootImpl.setView   //将ViewRootImpl对象与DecorView相互关联起来
				ViewRootImpl.requestLayout  //完成应用程序用户界面的初次布局
					 ViewRootImpl.scheduleTraversals
					 	ViewRootImpl.doTraversal
					 		ViewRootImpl.performTraversals
					 			ViewRootImpl.measureHierarchy
					 			ViewRootImpl.performLayout 
					 			ViewRootImpl.performDraw
				IWindowSession.addToDisplay  //跨进程调用,最终在系统进程使用WindowManagerService.addWindow()来实现更新window的逻辑

(1)measure 测量

//Ask host how big it wants to be
ViewRootImpl.measureHierarchy
    //获取根MeasureSpec,该值代表了对decorView的宽高的约束信息
	ViewRootImpl.getRootMeasureSpec  
	ViewRootImpl.performMeasure
		DecorView.measure
    		//只有满足forceLayout或needsLayout为true这两种情况才会进行实际的测量工作
			View.measure 
				DecorView.onMeasure
					FrameLayout.onMeasure
						//遍历DecorView的子View,对每个子View执行measureChildWithMargins(child) 
    					//目的是找到maxHeight和maxWidth,表示当前容器View用这个尺寸就能正常显示所有子View(同时考虑了padding和margin) 
						ViewGroup.measureChildWithMargins 
							ViewGroup.getChildMeasureSpec
							child.measure   //child:View  若此时的子View为ViewGroup的子类,便会调用相应容器类的onMeasure()方法,其他容器View的onMeasure()方法与FrameLayout的onMeasure()方法执行过程相似
								...   //递归执行所有子View的测量工作
						View.resolveSizeAndState  //根据之前的测量结果确定最终对FrameLayout的测量结果并存储起来
						ViewGroup.setMeasuredDimension

① ViewGroup没有像View一样对onMeasure方法做统一实现,ViewGroup是一个抽象类,其测量过程的onMeasure方法需要各个子类去具体实现,比如LinearLayout、RelativeLayout等。

② MeasureSpec并不是指View的测量宽高,而是根据MeasureSpec测出测量宽高。MeasureSpec是一个32位整数,由SpecMode和SpecSize两部分组成,其中,高2位为SpecMode,低30位为SpecSize。SpecMode为测量模式,SpecSize为相应测量模式下的测量尺寸。View(包括普通View和ViewGroup)的SpecMode由本View的LayoutParams结合父View的MeasureSpec生成。

SpecMode的取值可为以下三种:

  • EXACTLY: 对子View提出了一个确切的建议尺寸(SpecSize);

  • AT_MOST: 子View的大小不得超过SpecSize;

  • UNSPECIFIED: 对子View的尺寸不作限制,通常用于系统内部。

    //ViewRootImpl.getRootMeasureSpec
    private static int getRootMeasureSpec(int windowSize, int rootDimension) {int measureSpec;
    	switch (rootDimension) {case ViewGroup.LayoutParams.MATCH_PARENT:
    			// Window can't resize. Force root view to be windowSize.
    			measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
    			break;
    		case ViewGroup.LayoutParams.WRAP_CONTENT:
    			// 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;
    }
//ViewGroup.measureChildWithMargins
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
	final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width);
    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height);
	
    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);  //参数是父View用于约束其测量的
}
//ViewGroup.getChildMeasureSpec
//展现了根据父View的MeasureSpec和子View的LayoutParams生成子View的MeasureSpec的过程
  //参数:
  // spec为父View的MeasureSpec
  // padding为父View在相应方向的已用尺寸加上父View的padding和子View的margin
  // childDimension为子View的LayoutParams的值
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {int specMode = MeasureSpec.getMode(spec);
  int specSize = MeasureSpec.getSize(spec);

  // 现在size的值为父View相应方向上的可用大小
  int size = Math.max(0, specSize - padding);

  int resultSize = 0;
  int resultMode = 0;

  switch (specMode) {// Parent has imposed an exact size on us
    case MeasureSpec.EXACTLY:
      if (childDimension >= 0) {// 表示子View的LayoutParams指定了具体大小值(xx dp)
        resultSize = childDimension;
        resultMode = MeasureSpec.EXACTLY;
      } else if (childDimension == LayoutParams.MATCH_PARENT) {// 子View想和父View一样大
        resultSize = size;
        resultMode = MeasureSpec.EXACTLY;
      } else if (childDimension == LayoutParams.WRAP_CONTENT) {// 子View想自己决定其尺寸,但不能比父View大 
        resultSize = size;
        resultMode = MeasureSpec.AT_MOST;
      }
      break;

    // Parent has imposed a maximum size on us
    case MeasureSpec.AT_MOST:
      if (childDimension >= 0) {// 子View指定了具体大小
        resultSize = childDimension;
        resultMode = MeasureSpec.EXACTLY;
      } else if (childDimension == LayoutParams.MATCH_PARENT) {// 子View想跟父View一样大,但是父View的大小未固定下来
        // 所以指定约束子View不能比父View大
        resultSize = size;
        resultMode = MeasureSpec.AT_MOST;
      } else if (childDimension == LayoutParams.WRAP_CONTENT) {// 子View想要自己决定尺寸,但不能比父View大
        resultSize = size;
        resultMode = MeasureSpec.AT_MOST;
      }
      break;

      . . .
  }

  //noinspection ResourceType
  return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}

(2)layout 布局

ViewRootImpl.performLayout
	DecorView.layout
		View.layout    //layout方法确定View本身的位置
			View.setFrame  //设定View的四个顶点的位置,即初始化l、r、t、b四个值
			View.onLayout	//父容器确定子元素的位置   View和ViewGroup都没有真正实现此方法
			FrameLayout.onLayout
            	FrameLayout.layoutChildren
					child.layout  //child:View
						View.setFrame
    					View.onLayout
					    LinearLayout.onLayout  //以LinearLayout为例
					    	LinearLayout.layoutVertical
					    		LinearLayout.setChildFrame //调用子元素的layout方法
					    			child.layout
    									... //重复上述调用

layout方法又会调用自身的onLayout方法。onLayout方法在View类中是空实现,大部分情况下View都无需重写该方法;onLayout方法在ViewGroup中为抽象方法,即每个ViewGroup子类都需要通过实现该方法来管理自己的所有childView的摆放位置。

(3)draw 绘制

ViewRootImpl.performDraw
	ViewRootImpl.draw(boolean fullRedrawNeeded)  //视图重绘
		ViewRootImpl.drawSoftware
			DecorView.draw(Canvas canvas)
				View.draw  //ViewGroup没有重写draw
    				//绘制包含7步,2/5可略
					View.drawBackground //Step 1, draw the background, if needed
					View.onDraw  //Step 3, draw the content 不同的View有不同的实现
					View.dispatchDraw  //Step 4, draw the children  ViewGroup重写了此方法
					View.onDrawForeground  //Step 6, draw decorations (foreground, scrollbars)
					View.drawDefaultFocusHighlight  // Step 7, draw the default focus highlight

draw是绘制视图的过程,在这个过程中View需要通过操作Canvas来实现自己的UI效果。

参考文章:
深入理解Android之View的绘制流程
一文读懂 View 的 Measure、Layout、Draw 流程
探索 Android View 绘制流程

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


标题名称:Android基础学习(二十四)——View绘制-创新互联
本文路径:http://ybzwz.com/article/djppgs.html