自定义View_onDraw,onMeasure方法
2016-08-28 12:34
218 查看
package xena.view; import xena.act.R; import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import android.view.View; public class MyView extends View { private Context context; private int r = 10; private String str = "小"; private final String NS = "http://www.hqyj.com";// 名称空间 private boolean flag; public float getR() { return r; } public void setR(int r) { this.r = r; this.invalidate();//触发onDraw方法的执行 } public String getStr() { return str; } public void setStr(String str) { this.str = str; this.invalidate();//触发onDraw方法的执行 } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; this.invalidate();//触发onDraw方法的执行 } // 用于自定义View当作标签时用的 public MyView(Context context, AttributeSet attrs) { super(context, attrs); this.context = context; // 参数1:名称空间, 参数2:自定义属性名, 取失败时当作默认值返回 this.r = attrs.getAttributeIntValue(NS, "r", 20); // this.flag = attrs.getAttributeBooleanValue(NS, "flag", false); // 参数1:我称空间, 参数2: 自定义属性名 // this.str = attrs.getAttributeValue(NS, "text"); // 如果属性值是资源文件id // int getAttributeResourceValue(String namespace, String attributeName, // int defaultValue); int id = attrs.getAttributeResourceValue(NS, "text", -1); if (id != -1) { // int id = R.string.hello_world; Resources resources = this.context.getResources(); this.str = resources.getString(id); }else { this.str = attrs.getAttributeValue(NS, "text"); } } // 用于new MyView(...)用的 public MyView(Context context) { super(context); } private int getwidth(int widthMeasureSpec) { int width = 10; int width_mode = MeasureSpec.getMode(widthMeasureSpec); if(width_mode==MeasureSpec.EXACTLY) { width = MeasureSpec.getSize(widthMeasureSpec); }else if(width_mode==MeasureSpec.AT_MOST) { width = (int) (2*r); }else if(width_mode==MeasureSpec.UNSPECIFIED) { width = 100; } return width; } // 00 11 10 11 private int getheight(int heightMeasureSpec) { int hight = 10; int hight_mode = MeasureSpec.getMode(heightMeasureSpec); if(hight_mode==MeasureSpec.EXACTLY) { hight = MeasureSpec.getSize(heightMeasureSpec); }else if(hight_mode==MeasureSpec.AT_MOST) { hight= (int) (2*r); }else if(hight_mode==MeasureSpec.UNSPECIFIED) { hight = 100; } return hight; } //在onDraw前调用 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width=0, hight=0; width = this.getwidth(widthMeasureSpec); hight = this.getheight(heightMeasureSpec); this.setMeasuredDimension(width, hight); } // // 用于绘制界面上的内容,当界面显示时调用 @Override protected void onDraw(Canvas canvas) {// canvas画布对象 super.onDraw(canvas); // 创建笔 画 Paint paint = new Paint(); paint.setColor(Color.BLUE); // 画圆 canvas.drawCircle(r, r, r, paint); paint.setColor(Color.RED); if (flag) { canvas.drawCircle(r, r, 5, paint); } // 画字 // 参数1:被绘制的字符串, 参数2,3:指字符串每一个字符的左下角坐标 paint.setTextSize(30);// 设置文字大小 canvas.drawText(this.str, r, r, paint); } }
(1)一般情况重写onMeasure()方法作用是为了自定义View尺寸的规则,如果你的自定义View的尺寸是根据父控件行为一致,就不需要重写onMeasure()方法
(2)如果不重写onMeasure方法,那么自定义view的尺寸默认就和父控件一样大小,当然也可以在布局文件里面写死宽高,而重写该方法可以根据自己的需求设置自定义view大小
(3)widthMeasureSpec和heightMeasureSpec这两个值通常情况下都是由父视图经过计算后传递给子视图的,说明父视图会在一定程度上决定子视图的大小。
1.
wrap--AT_MOST
fill--EXACTLY
固定值--EXACTLY
上边是大家都知道的,但是一直很疑惑UNSPECIFIED什么情况发生,今天测了下,发现在使用weigt时会调用,因为此时定义的值为0dp,这不就是UNSPECIFIED嘛!
父 match_parent match_parent match_parent wrap_content wrap_content wrap_content 200px
200px 200px
子 match_parent 100px wrap_content match_parent 100px
wrap_content 100px match_parent wrap_content
结果 exactly(fill) exactly(100px) at_most(fill) at_most (fill) exactly(100xp) at_most(fill)
exactly(100px) exactly(200px) at_most(200xp)
1.有at_most 适合自定义一个大小,(子的大小超不过父大小)
2.有exactly 则适合测出的大小.
3,有unspecified, 适合任意指定大小,(子的大小可超过父大小)
可以说重载onMeasure(),onLayout(),onDraw()三个函数构建了自定义View的外观形象。再加上onTouchEvent()等重载视图的行为,可以构建任何我们需要的可感知到的自定义View。
不管是自定义View还是系统提供的TextView这些,它们都必须放置在LinearLayout等一些ViewGroup中,因此理论上我们可以很好的理解onMeasure(),onLayout(),onDraw()这三个函数:1.View本身大小多少,这由onMeasure()决定;2.View在ViewGroup中的位置如何,这由onLayout()决定;3.绘制View,onDraw()定义了如何绘制这个View。
widthMeasureSpec, heightMeasureSpec这两个参数是从哪里来的?onMeasure()函数由包含这个View的具体的ViewGroup调用,因此值也是从这个ViewGroup中传入的。这里我直接给出答案:子类View的这两个参数,由ViewGroup中的layout_width,layout_height和padding以及View自身的layout_margin共同决定。权值weight也是尤其需要考虑的因素,有它的存在情况可能会稍微复杂点。
这个值由高32位组成,最高的两位保存的值叫specMode,可以通过如代码中所示的MeasureSpec.getMode()获取;低30位为specSize,同样可以由MeasureSpec.getSize()获取
所有的View的onMeasure()的最后一行都会调用setMeasureDimension()函数的作用——这个函数调用中传进去的值是View最终的视图大小。也就是说onMeasure()中之前所作的所有工作都是为了最后这一句话服务的。
specMode一共有三种可能:
MeasureSpec.EXACTLY:父视图希望子视图的大小应该是specSize中指定的。
MeasureSpec.AT_MOST:子视图的大小最多是specSize中指定的值,也就是说不建议子视图的大小超过specSize中给定的值。
MeasureSpec.UNSPECIFIED:我们可以随意指定视图的大小。
MATCH_PARENT对应于EXACTLY,WRAP_CONTENT对应于AT_MOST,其他情况也对应于EXACTLY,它和MATCH_PARENT的区别在于size值不一样。
相关文章推荐
- 关于自定义View初始化时不执行onDraw方法的笔记
- 自定义的VIEW没有执行onDraw方法
- android 自定义view 里onMeasure方法里使用getWidth()=0
- 安卓自定义View基础06-View的onMeasure(),onDraw()方法详解以及Padding的处理
- 自定义TextView,不执行onDraw方法,求解脱!
- 自定义View-onMeasure()方法
- android 自定义View之View的测量(onMeasure()方法)
- 自定义view之继承View重写onDraw方法
- 关于自定义View中onMesure和onDraw方法
- 自定义view中onMeasure、onLayout、onDraw、onFinishInflate、onSizeChanged方法调用时机
- 为什么自定义ViewGroup ondraw方法不会被调用
- 纯粹自定义view即继承view(重写onMeasure() onDraw())
- Android 自定义 view(三)—— onDraw 方法理解
- Android 自定义 view(三)—— onDraw 方法理解
- 自定义view的onDraw()方法不被调用
- Android图形graphics--自定义TextView,onMeasure和onDraw
- 为什么自定义ViewGroup ondraw方法不会被调用
- 自定义ViewGroup ondraw方法不被调用解决办法
- 自定义viewGroup 为什么不走 onDraw方法?
- Android 自定义ViewGroup中onDraw方法不执行的解决方法