【转】Android绘制View的过程研究——计算View的大小
简单描述可以解释为:计算大小(measure),布局坐标计算(layout),绘制到屏幕(draw);
下面看看每一步的动作到底是什么,
第一步:当activity启动的时候,触发初始化view过程的是由Window对象的DecorView调用View(具体怎样从xml中读取是用LayoutInflater.from(context).inflate)对象的 public final void measure(int widthMeasureSpec, int heightMeasureSpec)方法开始的,这个方法是final类型的,也就是所有的子类都不能继承该方法,保证android初始化view的原理不变。具体参数类值,后面会介绍。
第二步:View的measure方法 onMeasure(widthMeasureSpec, heightMeasureSpec),该方法进行实质性的view大小计算。注意:view的大小是有父view和自己的大小决定的,而不是单一决定的。这也就是为什么ViewGroup的子类会重新该方法,比如LinearLayout等。因为他们要计算自己和子view的大小。View基类有自己的实现,只是设置大小。其实根据源码来看,measure的过程本质上就是把Match_parent和wrap_content转换为实际大小
第三步:当measure结束时,回到DecorView,计算大小计算好了,那么就开始布局了,开始调用view的 public final void layout(int l, int t, int r, int b),该还是也是final类型的,目的和measure方法一样。layout方法内部会调用onlayout(int l, int t, int r, int b )方法,二ViewGroup将此方法abstract的了,所以我们继承ViewGroup的时候,需要重新该方法。该方法的本质是通过measure计算好的大小,计算出view在屏幕上的坐标点
第四步:measure过了,layout过了,那么就要开始绘制到屏幕上了,所以开始调用view的 public void draw(Canvas canvas)方法,此时方法不是final了,原因是程序员可以自己画,内部会调用ondraw,我们经常需要重写的方法。
这个两个参数都是有父view传递过来的,也就是代表了父view的大小。其实说大小不太对,应该说是建议“规格”。他有两部分组成,第一部分:高16位表示MODE,定义在MeasureSpec类中,有三种类型,MeasureSpec.EXACTLY:表示确定大小, MeasureSpec.AT_MOST:表示最大大小, MeasureSpec.UNSPECIFIED:不确定。第二部分:低16位表示size,既父view的大小,这就是为什么,我们在重写onmeasure方法是需要:int specMode = MeasureSpec.getMode(spec); int specSize = MeasureSpec.getSize(spec);这样调用,因为MeasureSpec知道怎么读取。对于跟view(并不是我们在xml中声明的第一个元素),而是系统的Window对象的decorVIew对象。Mode一般都为MeasureSpec.EXACTLY ,而size分别对应屏幕宽,高。也就是Window第一次掉用的view(这个view才是Xml中声明的第一个元素),一般都是这个值,而对于子view来说,这连个值就是你在xml定义的属性 android:layout_width和android:layout_height这个,当然了上面说过view的大小是有父view和子view共同决定的,所以这样有点不对,但是来源于这两个值。意思明白了,我们看看measure里边做什么了
2.measure方法内部操作过程
- 调用 onMeasure(widthMeasureSpec, heightMeasureSpec),将父view的建议大小和规格传入,view类自己的onmeasure方法,只是根据xml中配置的大小初始化大小 ,这个就不分析了,重要的我们看看viewGroup中的onMeasure方法,ViewGroup类中没有处理该方法,一般在他的子类中处理,比如LinearLayout中,我们以Linearlayout作为分析类。
- Linearlayout类的onMeasure方法分两种情况处理,1:重置排序,2:水平排序,这个大家都知道,我们分析重置排序,
- 获取所有的子view数量,对每个子view开始处理,如果子view是GONE的,则直接跳过
- 在LinearLaout.measureVertical方法中,首先:获取该子view的LayoutParams,在xml中定义的参数,将通过layout_weight定义的值累加到变量totalWeight中,所有的权重,然后判断如果view的height设置为零,但weight设置的大于0,则将height的值设置为LayoutParams.WRAP_CONTENT这个值,其他的不处理
- 然后调用measureChildWithMargins方法,这个做的处理包括:计算子view的measureSpec,即MODE和SIZE,调用方法为:getChildMeasureSpec,调用两次,分别 计算宽和高,getChildMeasureSpec内部根据父view的Measure和子view的layout_width和layout_height属性计算子view的measure,查看图片:getChildMeasureSpec计算子view的measure,总结如下:1.如果在xml中指定了子view的具体大小,那么计算结果不管父的measure是什么,结果都是EXACITY+child_size,2.如果子view的height指定
的值为FILL_PARENT,则返回的结果为:EXACITY+size,原因很简单:因为FILL_PARENT的意思是充满这个父view,所以返回的子view的measure就是view的大小,白!3.如果子vide的大小为wrap_content,那么返回的结果都为At_most+size,原因是:最大不能超过父view的大小。
- 子view的measure确定好以后,然后调用子view的measure方法,如果子view是View对象,则该view的大小测量结束,开始下一个子view的循环,如果子view是ViewGroup那么,又开始一个新的递归,处理逻辑和上面一样,值得所有的view对象测量结束。
- LinearLayout会在每个他的直接子view测量结束后,将该子view的高度累加到变量mTotalLength,其其实应该叫mTotalHeight更合适,但是为了和wight同一,所以命名为这个(这个过程不处理:父view的大小指定为具体值和fill_parent,且子view的高度指定为0和子viewweight值大于的)。
- 所有的子view测量结束后,才开始对layout_weight计算,这样我们可能想到,如果父view已经被占满了,那么有可能layout_weight大于0的view对象是不会显示的,而计算layout_weight的方法也很简单,就是用总高度减去上面分析完mTotalLength的值,就是剩下,然后去平分给view对象,注意计算权重时优先去android:android:weightSum(LinearLayout的xml属性)的值,如果不设置该值会计算和,所以该值既然设置了,就一定要子view的weight的总和相等,否则平分可能不能得到预期效果,分析一个例子吧:
<LinearLayout android:layout_width="fill_parent"
android:layout_height="200dp"
>
<TextView android:layout_width="fill_parent"
android:layout_height="100dp"
android:layout_weight="1"
/>
<TextView android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
/>
</LinearLayout>
上面这个例子再计算第一个TextView的时候,根据android:layout_height="100dp" 值,确定高度为100dp,计算第二个TextView的时候,由于android:layout_height="0"为0,所以不计算其高度,等到计算weight的时候才计算,当计算weight的时候,千万别认为第一个TextView已经计算过了,就不计算了,还是计算的,计算过程如下:第一个分配了100dp,还剩100dp,所以两个textview各分50dp,所以第一个TextVIew的 150dp,第二个就为50dp
至此,view的measure就结束了,所有的view值都结束了,大家可能发现,这个过程只用了几个属性: android:layout_width,android:layout_height,android:layout_weight还有marger和pading,其他的多数属性都在ondraw时候使用。
Androd开发View是一个基本的视图界面,但是如何做一个自定义的View,那View的大小是多少呢?这小节就研究下View的大小。通过LogCat来研究View的大小是怎样确定的。好了,直接切入正题吧. 1、 在Activity中直接new HelloView 时View的大小。 View的大小获取可以用其中的两种方法获取: this.getHeight():获取View的高 this.getWidth():获取View的宽 我们可以做一个猜想,View的大小是在什么时候确定的,是在new一个View的时候还是在onDraw()的时候?还是在其他时候?为了研究这个,我们分别在构造函数和onDraw中打上Log补丁。 --- >HelloVew.java public HelloView(Context context){ super(context); Log.v("HelloView(Context context)","" + this.getHeight()+ " " + this.getWidth()); } /** * 这个是我们要在XML中初始化用的 * */ public HelloView(Context context,AttributeSet attrs){ super(context, attrs); Log.v("HelloView(Context context,AttributeSet attrs)","" + this.getHeight()+ " " + this.getWidth()); } /** * 绘制View * */ protected void onDraw(Canvas canvas){ Log.v("onDraw(Canvas canvas)","" + this.getHeight()+ " " + this.getWidth()); canvas.drawColor(Color.WHITE); myUseBitmapFactory(canvas); myUseBitmapDrawable(canvas); myUseInputStreamandBitmapDrawable(canvas); } 运行: 我们观察可以发现,new View 的时候并没有确定了View的大小,并且系统就没有调用(Context context)这个构造函数。 也就是说View大小是在new View之后OnDraw之前确定的,那onDraw之前的又有那些方法了,呵呵,我们试着override这个方法试试: 我们观察发现:onMeasure方法运行了两次:第一次宽和高都是0,但是第二次就变了,是不是可以说是在这个方法中确定的,但是实际上不一定会是这么回事,这个我们放在以后研究。这里我们只需要知道不是在new View时确定的就好了。 2、在XML中定义时View大小 这个我们直接上代码: main.xml文件修改: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> <view class="com.fxhy.stady.HelloView" android:layout_width="50dip" android:layout_height="120dip" /> </LinearLayout> mainActivity : * 使用自定义的View * */ public class MainActivity extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main);// 使用自定义的View } } 运行: 我们发现,和我们Xml中定义的大小一样,哈哈,有兴趣的可以自己测试测试。 |
【转】Android绘制View的过程研究——计算View的大小的更多相关文章
- android绘制view的过程
1 android绘制view的过程简单描述 简单描述可以解释为:计算大小(measure),布局坐标计算(layout),绘制到屏幕(draw): 下面看看每一步的动作到底是 ...
- View学习(四)-View的绘制(draw)过程
View的draw过程相比之于measrue过程,也是比较简单的.并且在我们自定义View时,也经常需要重写onDraw方法,来绘制出我们要实现的效果. 如之前的文章所说,绘制的流程也是起始于View ...
- Android 绘制view的小知识点
[onMeasure] 直接继承view或ViewGroup的自定义控件需要重写onMeasure方法并设置wrap_content时的自身大小,否则在布局中使用wrap_content就相当于mat ...
- [原创]Android从xml加载到View对象过程解析
我们从Activity的setContentView()入手,开始源码解析, //Activity.setContentView public void setContentView(int layo ...
- Android绘图机制(二)——自定义View绘制形, 圆形, 三角形, 扇形, 椭圆, 曲线,文字和图片的坐标讲解
Android绘图机制(二)--自定义View绘制形, 圆形, 三角形, 扇形, 椭圆, 曲线,文字和图片的坐标讲解 我们要想画好一些炫酷的View,首先我们得知道怎么去画一些基础的图案,比如矩形,圆 ...
- Android通过xml生成创建View的过程解析
Android的布局方式有两种,一种是通过xml布局,一种是通过java代码布局,两种布局方式各有各的好处,当然也可以相互混合使用.很多人都习惯用xml布局,那xml布局是如何转换成view的呢?本文 ...
- 【转】(转)【Android】Paint的效果研究
转自:http://wpf814533631.iteye.com/blog/1847661 (转)[Android]Paint的效果研究 博客分类: android 在Paint中有很多的属性可以 ...
- Android绘制流程
一.前言 1.1.C++界面库 MFC.WTL.DuiLib.QT.Skia.OpenGL.Android里面的画图分为2D和3D两种: 2D是由Skia 来实现的,3D部分是由OpenGL实现的. ...
- Android艺术开发探索第四章——View的工作原理(下)
Android艺术开发探索第四章--View的工作原理(下) 我们上篇BB了这么多,这篇就多多少少要来点实战了,上篇主席叫我多点自己的理解,那我就多点真诚,少点套路了,老司机,开车吧! 我们这一篇就扯 ...
随机推荐
- 【BZOJ】3053: The Closest M Points(kdtree)
http://www.lydsy.com/JudgeOnline/problem.php?id=3053 本来是1a的QAQ.... 没看到有多组数据啊.....斯巴达!!!!!!!!!!!!!!!! ...
- X.509证书_生成X.509协议的证书
用法:1. 用NOTE打开,修改按实际情况脚本中的(1)~ (6)处参数2. 找一台含JVM环境的WIN机器3. 双击执行后,会生成一对密钥4. 请确保当前使用的JDK版本为6.0!!! @echo ...
- RN组件之ScrollView
一.ScrollView 该组件封装了Android平台的ScrollView(滚动组件),并且提供触摸事件"responder"系统功能.使用ScrollView的时候 确保有一 ...
- php 配置正确的时间
关于php时区时间错误问题 date 当前时间 时差 当地 本地date_default_timezone_set 之前有一个遗留问题,就是echo date("y-m-d h:i:s&qu ...
- php页面之间传值
echo("<script>window.open('2.php?head=".$head."');<script>");
- ArcEngine开发 尝试读取或写入受保护的内存。这通常指示其他内存已损坏。
if(pFeature!=null) { IPoint pnt = pFeature.Shape as IPoint; pntArray.Add(pnt); } 调试是pntArray.Add(pnt ...
- Ubuntu 14.04 AMD 64位 下 Android Studio 的安装
Ubuntu 14.04 AMD 64位 下 Android Studio 的安装 作者:yoyoyosiyu 邮箱:yoyoyosiyu@163.com 时间:2015年8月25日 Android ...
- lastLogon和lastLogonTimestamp的区别
如何得到用户最近一次登陆域的时间?在Windows2003域中有2个属性:lastLogon和lastLogonTimestamp,那么这2个属性到底有什么作用呢? lastLogon属性实时更新用户 ...
- 漫谈Java虚拟机(JVM)
Java 虚拟机(JVM)是可运行 Java 代码的假想计算机. 只要根据 JVM 规范描述将解释器移植到特定的计算机上,就能保证经过编译的任何 Java 代码能够在该系统上运行. 从上图中不难明白J ...
- [ZZ] Maxwell 架构
http://digi.163.com/14/0218/23/9LDCTFON00162DSP.html [IT168 评测]随着一句“娘娘,封神啦(宝鸡口音)”,中国的观众迅速认识到了两个极其出彩的 ...