Android——View绘制机制小结

(本文图片有借鉴,内容为读《Android艺术与探索》有感,如有错误欢迎指出)

View绘制机制分为measure()测量大小,layout()确定位置,draw()绘制三个过程。

Measure过程:

MeasureSpec:

        是一个大小和模式的组合值,32位整型。前两位为mode,后30位为size。

MeasureSpec的模式:

  • UPSPECIFIED : 父容器对于子容器没有任何限制,子容器想要多大就多大
  • EXACTLY: 父容器已经为子容器设置了尺寸,子容器应当服从这些边界,不论子容器想要多大的空间。
  • AT_MOST:子容器可以是声明大小内的任意大小

测量过程是先从子view开始测量,结果出来后在测量自己的逐层向上测量,但测量时计算大小的过程则是拿着父view的MeasureSpec,再结合子view的LayoutParams计算子view的MeasureSpec,再逐层向下计算。

所以相应的排列组合如下:

父view的MeasureSpec

子view的Layout_XXX

子view的MeasureSpec

EXACTLY大小确定

match_parent:充满整个父view,所以大小也是确定的

size = 父view的大小,mode = EXACTLY

wrap_content:根据自己大小进行调整,但因为父view是固定的,因此最大不能超过父View

size = 暂定父view的大小,mode = AT_MOST

后在实现对自己大小的测量

确定的值(如20dp):不管父view是怎样的,都固定大小

size = 固定大小,mode = EXACTLY

AT_MOST大小不能超过size的值

match_parent:充满整个父view,父view大小不确定,因此也不确定,最大是父view的大小

size = 暂定父view的大小,mode = AT_MOST

虽然写的是match_parent但因为父view不确定所以大小也是不确定的

wrap_content:根据自己大小进行调整,因为父view子view大小都是不固定的,但能确定的是不会超过父容器

size = 暂定父view的大小,mode = AT_MOST

后在实现对自己大小的测量

确定的值(如20dp):不管父view是怎样的,都固定大小

size = 固定大小,mode = EXACTLY

UNSPECIFID未制定大小,没有任何束缚和约束

match_parent:充满整个父view,父view大小没有要求,所以子view也没有任何约束,想多大就多大,size值失去意义,一般直接设成0

size = 0,mode = UNSPECIFIED

wrap_content:根据自己大小进行调整,同上

size = 0,mode = UNSPECIFIED

确定的值(如20dp):不管父view是怎样的,都固定大小

size = 固定大小,mode = EXACTLY

由此判断子mode的流程可以总结为如下:


p.s. 当view采用固定宽高时,不管父容器MeasureSpec是什么,view的MeasureSpec都是精准模式且大小遵需layoutParams中的大小

对于View的默认值测量时比较容易。根据父容器的状态设定默认值的大小。

但对于其他View的派生类(如TextView、Button、ImageView等)都对onMeasure方法进行重写。会先去测量文字和图片的的高度等拿到contant这个高度,然后再根据mode设定view真正的大小。然后在所有的子view测量完成后,调用setMeasuredDimension设置自己的宽高。(不同view设定方式不同,frameLayout可能是最大子view大小,linearLayout可能是高度的叠加等。)

整个view的结构。其中包含view和textView的linearLayout是用户所添加的布局。

代码:

<LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"    
   android:id="@+id/linear"
   android:layout_width="match_parent"    
   android:layout_height="wrap_content"    
   android:layout_marginTop="50dp"    
   android:background="@android:color/holo_blue_dark"    
   android:paddingBottom="70dp"    
   android:orientation="vertical">    
   <TextView        
    android:id="@+id/text"       
    android:layout_width="match_parent"     
    android:layout_height="wrap_content"  
    android:background="@color/material_blue_grey_800"       
    android:text="TextView"        
    android:textColor="@android:color/white"        
    android:textSize="20sp" />    
   <View       
      android:id="@+id/view"       
     android:layout_width="match_parent" 
     android:layout_height="150dp"    
     android:background="@android:color/holo_green_dark" />
</LinearLayout>

总结构:

 

那么View的绘制是从哪里开始的呢?

我们知道每个Activity 均会创建一个 PhoneWindow对象,是Activity和整个View系统交互的接口,每个Window都对应着一个View和一个ViewRootImpl,

Window和View通过ViewRootImpl来建立联系,对于Activity来说,ViewRootImpl是连接WindowManager和DecorView的纽带,

绘制的入口是由ViewRootImpl的performTraversals方法来发起Measure,Layout,Draw等流程的。

Layout过程

performTraversals在DecorView.measure计算出大小后,执行Layout来确定具体放在哪个位置。 传入参数为left,top,right,bottom四个位置(因此可不measure就确定view的大小。如right-left)

draw过程

draw的递归流程:

 

draw 过程中一共分成7步,

第一步:drawBackground(canvas): 作用就是绘制 View 的背景。

第二步:如果必要,保存canvas的layers

第三步:onDraw(canvas) :绘制 View 的内容。View 的内容是根据自己需求自己绘制的,所以方法是一个空方法,View的继承类自己复写实现绘制内容。

第四步:dispatchDraw(canvas):遍历子View进行绘制内容。在 View 里面是一个空实现,ViewGroup 里面才会有实现。在自定义 ViewGroup 一般不用复写这个方法,因为它在里面的实现帮我们实现了子 View 的绘制过程,基本满足需求。

第五步:如果必要,绘制渐变效果重启layers

第六步:onDrawForeground(canvas):对前景色跟滚动条进行绘制。

第七步:drawDefaultFocusHighlight(canvas):绘制默认焦点高亮

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android的Widget是指可以被放置在桌面或者其他应用中的小部件,比如常见的时钟、天气、日历等等。在Android中,Widget的布局可以使用XML文件来进行定义,与普通的布局定义类似,可以使用各种属性来设置Widget的样式和行为。 在Widget的XML布局文件中,可以使用View类中定义的许多属性来控制Widget的样式和行为,比如: 1. android:id:设置Widget的ID,可以在代码中通过findViewById()方法来获取对应的View对象。 2. android:layout_width、android:layout_height:设置Widget的宽度和高度,可以使用具体数值或者match_parent、wrap_content等特殊值。 3. android:layout_gravity:设置Widget在父布局中的对齐方式,比如center、left、right等等。 4. android:padding、android:paddingLeft、android:paddingRight等:设置Widget的内边距,用于控制Widget内部内容的显示位置。 5. android:background:设置Widget的背景颜色或者背景图片。 6. android:clickable、android:longClickable:设置Widget是否可以被点击或者长按。 7. android:focusable、android:focusableInTouchMode:设置Widget是否可以获取焦点,用于控制Widget是否可以响应键盘事件等。 8. android:visibility:设置Widget是否可见,可以使用值为visible、invisible、gone。 除了以上列出的属性之外,还有许多其他的属性可以用于控制Widget的样式和行为,具体可以查看Android官方文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值