Android 自定义view 和 onMeasure方法介绍
http://blog.csdn.net/gf771115/article/details/9730439http://blog.csdn.net/gf771115/article/details/9730439
http://blog.csdn.net/gf771115/article/details/9730439
http://blog.csdn.net/gf771115/article/details/9730439
http://blog.csdn.net/gf771115/article/details/9730439
http://blog.csdn.net/gf771115/article/details/9730439
http://blog.csdn.net/gf771115/article/details/9730439
http://blog.csdn.net/gf771115/article/details/9730439
Android 自定义view 和 onMeasure方法介绍
分类:Android/OMS 2013-08-03 10:47 616人阅读 评论(0) 收藏 举报转自:http://blog.csdn.net/cyq1028/article/details/7390631
http://blog.csdn.net/sunny2come/article/details/7287298
哈哈,兄弟我终于自己写了一个view,不是网上那种简单的哦,还是有一定技术含量的,
我是通过学习ApiDemo(android自带的sample)里面LabelView实现的,
先谈谈学习过程,觉得一开始不应当盲目的动手做,应对想把原理搞明白,哪怕一个很小的View,也应当将各个细节弄清楚,
等这些搞定了,接下来的工作就是水道渠成了!
自定义一个View那必须继承View,
首先说说我的View是啥,恩,很简单,就是一个椭圆,其中可以设置4个参数,分别是top、left、right、bottom,应该很清楚吧,因为canvas.drawOval时用到了4个值,
首先得定义4个属性值,是我的View专有的,
建立attr.xml放到values下面
[java] view plain copy- <?xml version="1.0"encoding="UTF-8"?>
- <resources>
- <declare-styleable name="RocketView">
- <attr name="ovalLeft"format="dimension"/>
- <attr name="ovalTop"format="dimension"/>
- <attr name="ovalRight"format="dimension"/>
- <attr name="ovalBottom"format="dimension"/>
- </declare-styleable>
- </resources>
- <?xmlversion="1.0"encoding="UTF-8"?>
- <resources>
- <declare-styleablename="RocketView">
- <attrname="ovalLeft"format="dimension"/>
- <attrname="ovalTop"format="dimension"/>
- <attrname="ovalRight"format="dimension"/>
- <attrname="ovalBottom"format="dimension"/>
- </declare-styleable>
- </resources>
然后写个RocketView [java] view plain copy
- packagecom.myos;
- importandroid.content.Context;
- importandroid.content.res.TypedArray;
- importandroid.graphics.Canvas;
- importandroid.graphics.Color;
- importandroid.graphics.Paint;
- importandroid.graphics.RectF;
- importandroid.util.AttributeSet;
- importandroid.util.Log;
- importandroid.view.View;
- importandroid.view.View.MeasureSpec;
- publicclassRocketViewextendsView{
- privatePaint mOvalPaint;
- privateintmStrokeWidth =2;
- privateintpadding =3;
- //椭圆参数
- privateintmOval_l;
- privateintmOval_t;
- privateintmOval_r;
- privateintmOval_b;
- //构造
- publicRocketView(Context context, AttributeSet attrs) {
- super(context, attrs);
- initRocketView();
- TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.RocketView);
- mOval_l = a.getDimensionPixelOffset(R.styleable.RocketView_ovalLeft, padding);
- mOval_t = a.getDimensionPixelOffset(R.styleable.RocketView_ovalTop, padding);
- mOval_r = a.getDimensionPixelOffset(R.styleable.RocketView_ovalRight,100);
- mOval_b = a.getDimensionPixelOffset(R.styleable.RocketView_ovalBottom,100);
- a.recycle();
- }
- privatevoidinitRocketView() {
- mOvalPaint =newPaint();
- mOvalPaint.setAntiAlias(true);
- mOvalPaint.setColor(Color.BLUE);
- mOvalPaint.setStyle(Paint.Style.STROKE);
- mOvalPaint.setStrokeWidth(mStrokeWidth);
- setPadding(padding,padding,padding,padding);
- }
- publicvoidsetOvalRect(intl,intt,intr,intb){
- mOval_l = l + padding;
- mOval_t = t + padding;
- mOval_r = r;
- mOval_b = b;
- requestLayout();
- invalidate();
- }
- @Override
- protectedvoidonDraw(Canvas canvas) {
- // TODO Auto-generated method stub
- super.onDraw(canvas);
- canvas.drawColor(Color.WHITE);
- // 绘制椭圆
- RectF re11 =newRectF(mOval_l, mOval_t, mOval_r, mOval_b);
- canvas.drawOval(re11, mOvalPaint);
- // // 绘制圆形
- // canvas.drawCircle(mCircle_x, mCircle_y, mCircle_r, mPaint);
- }
- /**
- * @see android.view.View#measure(int, int)
- */
- @Override
- protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec) {
- setMeasuredDimension(measureWidth(widthMeasureSpec),
- measureHeight(heightMeasureSpec));
- }
- /**
- * Determines the width of this view
- * @param measureSpec A measureSpec packed into an int
- * @return The width of the view, honoring constraints from measureSpec
- */
- privateintmeasureWidth(intmeasureSpec) {
- intresult =0;
- intspecMode = MeasureSpec.getMode(measureSpec);
- intspecSize = MeasureSpec.getSize(measureSpec);
- if(specMode == MeasureSpec.EXACTLY) {
- // We were told how big to be
- result = specSize;
- }else{
- // Measure the text
- result = mOval_r + getPaddingLeft()
- + getPaddingRight();
- if(specMode == MeasureSpec.AT_MOST) {
- // Respect AT_MOST value if that was what is called for by measureSpec
- result = Math.min(result, specSize);
- }
- }
- returnresult;
- }
- /**
- * Determines the height of this view
- * @param measureSpec A measureSpec packed into an int
- * @return The height of the view, honoring constraints from measureSpec
- */
- privateintmeasureHeight(intmeasureSpec) {
- intresult =0;
- intspecMode = MeasureSpec.getMode(measureSpec);
- intspecSize = MeasureSpec.getSize(measureSpec);
- if(specMode == MeasureSpec.EXACTLY) {
- // We were told how big to be
- result = specSize;
- }else{
- // Measure the text (beware: ascent is a negative number)
- result = mOval_b + getPaddingTop()
- + getPaddingBottom();
- if(specMode == MeasureSpec.AT_MOST) {
- // Respect AT_MOST value if that was what is called for by measureSpec
- result = Math.min(result, specSize);
- }
- }
- returnresult;
- }
- }
- packagecom.myos;
- importandroid.content.Context;
- importandroid.content.res.TypedArray;
- importandroid.graphics.Canvas;
- importandroid.graphics.Color;
- importandroid.graphics.Paint;
- importandroid.graphics.RectF;
- importandroid.util.AttributeSet;
- importandroid.util.Log;
- importandroid.view.View;
- importandroid.view.View.MeasureSpec;
- publicclassRocketViewextendsView{
- privatePaintmOvalPaint;
- privateintmStrokeWidth=2;
- privateintpadding=3;
- //椭圆参数
- privateintmOval_l;
- privateintmOval_t;
- privateintmOval_r;
- privateintmOval_b;
- //构造
- publicRocketView(Contextcontext,AttributeSetattrs){
- super(context,attrs);
- initRocketView();
- TypedArraya=context.obtainStyledAttributes(attrs,
- R.styleable.RocketView);
- mOval_l=a.getDimensionPixelOffset(R.styleable.RocketView_ovalLeft,padding);
- mOval_t=a.getDimensionPixelOffset(R.styleable.RocketView_ovalTop,padding);
- mOval_r=a.getDimensionPixelOffset(R.styleable.RocketView_ovalRight,100);
- mOval_b=a.getDimensionPixelOffset(R.styleable.RocketView_ovalBottom,100);
- a.recycle();
- }
- privatevoidinitRocketView(){
- mOvalPaint=newPaint();
- mOvalPaint.setAntiAlias(true);
- mOvalPaint.setColor(Color.BLUE);
- mOvalPaint.setStyle(Paint.Style.STROKE);
- mOvalPaint.setStrokeWidth(mStrokeWidth);
- setPadding(padding,padding,padding,padding);
- }
- publicvoidsetOvalRect(intl,intt,intr,intb){
- mOval_l=l+padding;
- mOval_t=t+padding;
- mOval_r=r;
- mOval_b=b;
- requestLayout();
- invalidate();
- }
- @Override
- protectedvoidonDraw(Canvascanvas){
- //TODOAuto-generatedmethodstub
- super.onDraw(canvas);
- canvas.drawColor(Color.WHITE);
- //绘制椭圆
- RectFre11=newRectF(mOval_l,mOval_t,mOval_r,mOval_b);
- canvas.drawOval(re11,mOvalPaint);
- ////绘制圆形
- //canvas.drawCircle(mCircle_x,mCircle_y,mCircle_r,mPaint);
- }
- /**
- *@seeandroid.view.View#measure(int,int)
- */
- @Override
- protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){
- setMeasuredDimension(measureWidth(widthMeasureSpec),
- measureHeight(heightMeasureSpec));
- }
- /**
- *Determinesthewidthofthisview
- *@parammeasureSpecAmeasureSpecpackedintoanint
- *@returnThewidthoftheview,honoringconstraintsfrommeasureSpec
- */
- privateintmeasureWidth(intmeasureSpec){
- intresult=0;
- intspecMode=MeasureSpec.getMode(measureSpec);
- intspecSize=MeasureSpec.getSize(measureSpec);
- if(specMode==MeasureSpec.EXACTLY){
- //Weweretoldhowbigtobe
- result=specSize;
- }else{
- //Measurethetext
- result=mOval_r+getPaddingLeft()
- +getPaddingRight();
- if(specMode==MeasureSpec.AT_MOST){
- //RespectAT_MOSTvalueifthatwaswhatiscalledforbymeasureSpec
- result=Math.min(result,specSize);
- }
- }
- returnresult;
- }
- /**
- *Determinestheheightofthisview
- *@parammeasureSpecAmeasureSpecpackedintoanint
- *@returnTheheightoftheview,honoringconstraintsfrommeasureSpec
- */
- privateintmeasureHeight(intmeasureSpec){
- intresult=0;
- intspecMode=MeasureSpec.getMode(measureSpec);
- intspecSize=MeasureSpec.getSize(measureSpec);
- if(specMode==MeasureSpec.EXACTLY){
- //Weweretoldhowbigtobe
- result=specSize;
- }else{
- //Measurethetext(beware:ascentisanegativenumber)
- result=mOval_b+getPaddingTop()
- +getPaddingBottom();
- if(specMode==MeasureSpec.AT_MOST){
- //RespectAT_MOSTvalueifthatwaswhatiscalledforbymeasureSpec
- result=Math.min(result,specSize);
- }
- }
- returnresult;
- }
- }
在构造中getDimensionPixelOffset检索出一个属性值,没有的话就使用第2个参数做默认值,
可以在布局xml中初始化这个属性值,下面是我main.xml
[java] view plain copy- <?xml version="1.0"encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res/com.myos"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/hello"
- />
- <com.myos.RocketView
- app:ovalLeft="0dp"
- android:id="@+id/rv"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
- </LinearLayout>
- <?xmlversion="1.0"encoding="utf-8"?>
- <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res/com.myos"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/hello"
- />
- <com.myos.RocketView
- app:ovalLeft="0dp"
- android:id="@+id/rv"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
- </LinearLayout>
另外构造函数中onMeasure很重要的,onDraw很明显就是画椭圆了,android在画的时候呢,会去先测量,需要我们来提供View的宽和高,
大家都知道wrap_content、fill_parent,测量时会有3种模式,分别是UNSPECIFIED、EXACTLY、AT_MOST,
当使用
wrap_content时就是AT_MOST模式,fill_parent就是EXACTLY模式,可以看看官方文档:
http://developer.android.com/guide/topics/ui/how-android-draws.html
相信研读一下代码就明白了
RocketView定义好了,接下来就是使用了
[java] view plain copy- packagecom.myos;
- importandroid.app.Activity;
- importandroid.os.Bundle;
- publicclassMainActivityextendsActivity {
- RocketView mRocketView;
- /** Called when the activity is first created. */
- @Override
- publicvoidonCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- mRocketView = (RocketView)findViewById(R.id.rv);
- mRocketView.setOvalRect(0,0,500,100);
- }
- }
- packagecom.myos;
- importandroid.app.Activity;
- importandroid.os.Bundle;
- publicclassMainActivityextendsActivity{
- RocketViewmRocketView;
- /**Calledwhentheactivityisfirstcreated.*/
- @Override
- publicvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- mRocketView=(RocketView)findViewById(R.id.rv);
- mRocketView.setOvalRect(0,0,500,100);
- }
- }
可以通过setOvalRect函数来动态调整椭圆的大小!
下面是运行效果,ko啦!
onMeasure方法在控件的父元素正要放置它的子控件时调用.它会问一个问题,“你想要用多大地方啊?”,然后传入两个参数——widthMeasureSpec和heightMeasureSpec.
它们指明控件可获得的空间以及关于这个空间描述的元数据.
比返回一个结果要好的方法是你传递View的高度和宽度到setMeasuredDimension方法里.
(请发邮件到 [email protected] 获得翻强软件,能上youtube哟。)
接下来的代码片段给出了如何重写onMeasure.注意,调用的本地空方法是来计算高度和宽度的.它们会译解widthHeightSpec和heightMeasureSpec值,并计算出合适的高度和宽度值.
java代码:
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int measuredHeight = measureHeight(heightMeasureSpec);
- int measuredWidth = measureWidth(widthMeasureSpec);
- setMeasuredDimension(measuredHeight, measuredWidth);
- }
- private int measureHeight(int measureSpec) {
- // Return measured widget height.
- }
- private int measureWidth(int measureSpec) {
- // Return measured widget width.
- }
边界参数——widthMeasureSpec和heightMeasureSpec ,效率的原因以整数的方式传入。
MeasureSpec封装了父布局传递给子布局的布局要求,每个MeasureSpec代表了一组宽度和高度的要求。一个MeasureSpec由大小和模式组成。
它有三种模式:
UNSPECIFIED(未指定),父元素不对自元素施加任何束缚,子元素可以得到任意想要的大小;
EXACTLY(完全),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;
AT_MOST(至多),子元素至多达到指定大小的值。
它常用的三个函数:
1.static int getMode(int measureSpec):根据提供的测量值(格式)提取模式(上述三个模式之一)
2.static int getSize(int measureSpec):根据提供的测量值(格式)提取大小值(这个大小也就是我们通常所说的大小)
3.static int makeMeasureSpec(int size,int mode):根据提供的大小值和模式创建一个测量值(格式)
这个类的使用呢,通常在view组件的onMeasure方法里面调用但也有少数例外
在它们使用之前,首先要做的是使用MeasureSpec类的静态方法getMode和getSize来译解,如下面的片段所示:
java代码:
- int specMode = MeasureSpec.getMode(measureSpec);
- int specSize = MeasureSpec.getSize(measureSpec);
依据specMode的值,如果是AT_MOST,specSize 代表的是最大可获得的空间;如果是EXACTLY,specSize 代表的是精确的尺寸;如果是UNSPECIFIED,对于控件尺寸来说,没有任何参考意义。
当以EXACT方式标记测量尺寸,父元素会坚持在一个指定的精确尺寸区域放置View。在父元素问子元素要多大空间时,AT_MOST指示者会说给我最大的范围。在很多情况下,你得到的值都是相同的。
在两种情况下,你必须绝对的处理这些限制。在一些情况下,它可能会返回超出这些限制的尺寸,在这种情况下,你可以让父元素选择如何对待超出的View,使用裁剪还是滚动等技术。
接下来的框架代码给出了处理View测量的典型实现:
java代码:
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int measuredHeight = measureHeight(heightMeasureSpec);
- int measuredWidth = measureWidth(widthMeasureSpec);
- setMeasuredDimension(measuredHeight, measuredWidth);
- }
- private int measureHeight(int measureSpec) {
- int specMode = MeasureSpec.getMode(measureSpec);
- int specSize = MeasureSpec.getSize(measureSpec);
- // Default size if no limits are specified.
- int result = 500;
- if (specMode == MeasureSpec.AT_MOST){
- // Calculate the ideal size of your
- // control within this maximum size.
- // If your control fills the available
- // space return the outer bound.
- result = specSize;
- }
- else if (specMode == MeasureSpec.EXACTLY){
- // If your control can fit within these bounds return that value.
- result = specSize;
- }
- return result;
- }
- private int measureWidth(int measureSpec) {
- int specMode = MeasureSpec.getMode(measureSpec);
- int specSize = MeasureSpec.getSize(measureSpec);
- // Default size if no limits are specified.
- int result = 500;
- if (specMode == MeasureSpec.AT_MOST){
- // Calculate the ideal size of your control
- // within this maximum size.
- // If your control fills the available space
- // return the outer bound.
- result = specSize;
- }
- else if (specMode == MeasureSpec.EXACTLY){
- // If your control can fit within these bounds return that value.
- result = specSize;
- }
- return result;
- }
更多相关文章
- android 使用statfs获得文件路径可用空间大小的方法
- android 共享元素
- Android Launcher2 icon大小修改
- Android中的一个TextView中的字体设置不同大小
- Android自定义对话框的大小
- android app -- Picasso 二级缓存加载图片,可控制加载图片大小(附
- Android:自适应不同分辨率的屏幕大小、方向和不同分辨率的字体大