讲真,自我感觉,我的水平真的是渣的一匹,好多东西都只停留在知道和会用的阶段,也想去研究原理和底层的实现,可是一看到代码就懵逼了,然后就看不下去了,
说自己不着急都是骗人的,我自己都不信,前两天买了本《Android 群英传》,江湖上都说这是一本初级过渡到中级不错的进阶书,所以准备看一下,才看了两天,今天
看到了 view 的测量及绘制,还有自定义 view(还没看完),学到什么就写篇博客吧,算是对自己所学的一个总结和记录吧,也可以督促自己,如果有讲的不对的地方或者
有歧义的地方,欢迎大家吐槽批评我!
正文开始(手动鼓掌)
view 的测量:
首先需要知道的是 view 的三种测量模式:
1、EXACTLY:精确值模式,当我们对 view 的 layout_width 和 layout_height 属性指定具体的数值的时候,比如 layout_width="100dp" 或者指定为 match_parent 时,系统
进行测量的时候,使用的是这种模式。
2、AT_MOST:最大值模式,当我们对 view 的 layout_width 和 layout_height 属性指定为 wrap_content 时,即 view 随着内容的大小变化而变化,或 viewgroup 随着 view 的
大小变化而变化,这个时候系统进行测量的时候,使用的是这种模式。
3、UNSPECIFIED:这个属性下不用指定其大小,一般在自定义 view 时才会使用(这种模式不是很理解,求指教)
在对 view 进行测量的时候,需要重写 onMeasure() 方法,view 默认的 onMeasure() 方法只支持 EXACTLY 模式,即指定具体的数值,所以在自定义 view 的时候必须重写
onMeasure(),这里留一个疑问:什么时候才会调用 Measure 方法进行测量?
- @Override
- protected voidonMeasure(intwidthMeasureSpec,int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
重写后点 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 进去看一下源码,发现系统最终会调用这个方法:
- setMeasuredDimension(intwidthMeasureSpec,intheightMeasureSpec);
这个方法的作用是将我们对 view 设置的宽和高设置进去,所以我们最终重写的 onMeasure() 方法就是这个样子的,方法内的两个入参 widthMeasureSpec 和 heightMeasureSpec
就是我们在 xml 里引用这个 view 时设置的 width 和 height,后面我们需要根据这两个值进行判断,判断系统要根据什么测量模式进行测量。
- @Override
- protected voidonMeasure(intwidthMeasureSpec,int heightMeasureSpec) {
- setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));
- }
前文一直在说系统的测量模式,那么我们要怎么样才能获取到系统的测量模式呢?获取测量模式后还需要获取具体的测量大小
- intspecMode = MeasureSpec.getMode(widthMeasureSpec);
- intspecSize = MeasureSpec.getSize(widthMeasureSpec);
单独对 measureWidth(widthMeasureSpec) 讲解一下,因为 height 和 width 是一样的。
- private intmeasureWidth(int widthMeasureSpec) {
- intwidthResult =0;
- intspecMode = MeasureSpec.getMode(widthMeasureSpec);
- intspecSize = MeasureSpec.getSize(widthMeasureSpec);
- if(specMode == MeasureSpec.EXACTLY){
- widthResult = specSize;
- }else {
- widthResult =400;
- if(specMode == MeasureSpec.AT_MOST){
- widthResult = Math.min(widthResult,specSize);
- }
- }
- return widthResult;
- }
这部分的理解要联系到前面说的 3 种测量模式,如果是在 EXACTLY if (specMode == MeasureSpec.EXACTLY) 这种模式下,我们已经在 xml 里面设置好了具体的数值,所以最后返回的值就是 specSize
如果是 AT_MOST 和 UNSPECIFIED 这两种测量模式下,我们就需要给 view 一个默认的大小,因为如果没有给默认的大小的话,系统不知道 view 的大小,所以 view 或默认充满父布局。这里默认的大小是 400,
大家会发现 else 里面还有一个 if,对 AT_MOST 这种模式又进行了判断,这是因为在这种模式下,view 不需要默认的大小,view 的是根据内容的大小变换而变化的。
最后就是 xml 里面进行引用和效果展示了:
- "1.0"encoding="utf-8">"http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/activity_main"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context="com.funny.myapplication.MainActivity">
- <com.funny.myapplication.StudyView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="View"
- android:textSize="20sp"
- android:background="#ccf"
- android:gravity="center"/>
(1)android:layout_width="wrap_content"
(2)android:layout_width="match_parent"
(3)android:layout_width="400px"
view 的绘制:
对 view 进行绘制需要重写 onDraw() 方法,onDraw() 方法中会有一个 canvas,可以把这个参数理解成画板,我们最终会借用这个画板进行绘制。
有了画板,要想绘画的话当然还需要一支画笔 paint。
- @Override
- protected void onDraw(Canvas canvas) {
- Paint paint1 =new Paint();
- paint1.setColor(getResources().getColor(R.color.colorAccent));
- paint1.setStyle(Paint.Style.FILL);
- Paint paint2 =newPaint();
- paint2.setColor(getResources().getColor(R.color.colorPrimary));
- paint2.setStyle(Paint.Style.FILL);//开始绘制canvas.drawOval(0,0,getMeasuredWidth(),getMeasuredHeight(),paint1);
- canvas.drawOval(0,0,getMeasuredWidth()-10,getMeasuredHeight()-10,paint2);//平移30个像素canvas.translate(30,0 );
- super.onDraw(canvas);
- canvas.restore();
- }
接下讲解一下这段代码的功能,首先实例了两只画笔,并且对画笔设置了颜色和风格,接下来开始绘制,这里我们画的是两个相互嵌套的椭圆,
canvas.drawOval(0,0,getMeasuredWidth(),getMeasuredHeight(),paint1); 这个方法需要 5 个入参,分别是 view 相对于父布局的左、上、右、下的坐标,最后一个是进行绘制的画笔。
系统给我们提供的 drawXXX 方法有很多:
- canvas绘制的常用方法有:
- drawColor() 填充颜色
- drawLine() 绘制线
- drawLines() 绘制线条
- drawOval() 绘制圆
- drawPath() 绘制路径
- drawPicture() 绘制图片
- drawPoint() 绘制点
- drawPoints() 绘制点
- drawRGB() 填充颜色
- drawRect() 绘制矩形
- drawText() 绘制文本
- drawTextOnPath() 在路径上绘制文本
效果展示:
viewGroup 的测量:
我们知道 viewGroup 管理子 view,那么 viewGroup 的大小,除设置指定大小外,是根据子 view 来决定的,viewGroup 在测量时会遍历所有的子 view,调用
子 view 的 Measure 方法来获得每一个子 view 的测量结果,这样文章开始留下的疑问就解决了,意不意外~ 关于 viewGroup 的绘制,一般情况下如果不是指定
了 viewGroup 的背景颜色,viewGroup 的 onDraw() 方法不会被调用,但是 viewGroup 会使用 dispatchDraw() 方法来绘制其子 view,过程同样是遍历所有的子 view
,并调用子 view 的绘制方法来完成绘制。
以上就是我对 view 的测量及绘制的全部理解,可能在很多人看来还是处于初级阶段,没有怎么涉及到源码,但是还是希望能帮助到一些人吧,对我自己也是一点点进步嘛,如果大家有什么
好的理解,欢迎留言哈~~~ 酱!拜啦!
来源: http://www.cnblogs.com/upwgh/p/7082034.html