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 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
  1. <?xml version="1.0"encoding="UTF-8"?>
  2. <resources>
  3. <declare-styleable name="RocketView">
  4. <attr name="ovalLeft"format="dimension"/>
  5. <attr name="ovalTop"format="dimension"/>
  6. <attr name="ovalRight"format="dimension"/>
  7. <attr name="ovalBottom"format="dimension"/>
  8. </declare-styleable>
  9. </resources>
[java] view plain copy
  1. <?xmlversion="1.0"encoding="UTF-8"?>
  2. <resources>
  3. <declare-styleablename="RocketView">
  4. <attrname="ovalLeft"format="dimension"/>
  5. <attrname="ovalTop"format="dimension"/>
  6. <attrname="ovalRight"format="dimension"/>
  7. <attrname="ovalBottom"format="dimension"/>
  8. </declare-styleable>
  9. </resources>

然后写个RocketView

[java] view plain copy
  1. packagecom.myos;
  2. importandroid.content.Context;
  3. importandroid.content.res.TypedArray;
  4. importandroid.graphics.Canvas;
  5. importandroid.graphics.Color;
  6. importandroid.graphics.Paint;
  7. importandroid.graphics.RectF;
  8. importandroid.util.AttributeSet;
  9. importandroid.util.Log;
  10. importandroid.view.View;
  11. importandroid.view.View.MeasureSpec;
  12. publicclassRocketViewextendsView{
  13. privatePaint mOvalPaint;
  14. privateintmStrokeWidth =2;
  15. privateintpadding =3;
  16. //椭圆参数
  17. privateintmOval_l;
  18. privateintmOval_t;
  19. privateintmOval_r;
  20. privateintmOval_b;
  21. //构造
  22. publicRocketView(Context context, AttributeSet attrs) {
  23. super(context, attrs);
  24. initRocketView();
  25. TypedArray a = context.obtainStyledAttributes(attrs,
  26. R.styleable.RocketView);
  27. mOval_l = a.getDimensionPixelOffset(R.styleable.RocketView_ovalLeft, padding);
  28. mOval_t = a.getDimensionPixelOffset(R.styleable.RocketView_ovalTop, padding);
  29. mOval_r = a.getDimensionPixelOffset(R.styleable.RocketView_ovalRight,100);
  30. mOval_b = a.getDimensionPixelOffset(R.styleable.RocketView_ovalBottom,100);
  31. a.recycle();
  32. }
  33. privatevoidinitRocketView() {
  34. mOvalPaint =newPaint();
  35. mOvalPaint.setAntiAlias(true);
  36. mOvalPaint.setColor(Color.BLUE);
  37. mOvalPaint.setStyle(Paint.Style.STROKE);
  38. mOvalPaint.setStrokeWidth(mStrokeWidth);
  39. setPadding(padding,padding,padding,padding);
  40. }
  41. publicvoidsetOvalRect(intl,intt,intr,intb){
  42. mOval_l = l + padding;
  43. mOval_t = t + padding;
  44. mOval_r = r;
  45. mOval_b = b;
  46. requestLayout();
  47. invalidate();
  48. }
  49. @Override
  50. protectedvoidonDraw(Canvas canvas) {
  51. // TODO Auto-generated method stub
  52. super.onDraw(canvas);
  53. canvas.drawColor(Color.WHITE);
  54. // 绘制椭圆
  55. RectF re11 =newRectF(mOval_l, mOval_t, mOval_r, mOval_b);
  56. canvas.drawOval(re11, mOvalPaint);
  57. // // 绘制圆形
  58. // canvas.drawCircle(mCircle_x, mCircle_y, mCircle_r, mPaint);
  59. }
  60. /**
  61. * @see android.view.View#measure(int, int)
  62. */
  63. @Override
  64. protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec) {
  65. setMeasuredDimension(measureWidth(widthMeasureSpec),
  66. measureHeight(heightMeasureSpec));
  67. }
  68. /**
  69. * Determines the width of this view
  70. * @param measureSpec A measureSpec packed into an int
  71. * @return The width of the view, honoring constraints from measureSpec
  72. */
  73. privateintmeasureWidth(intmeasureSpec) {
  74. intresult =0;
  75. intspecMode = MeasureSpec.getMode(measureSpec);
  76. intspecSize = MeasureSpec.getSize(measureSpec);
  77. if(specMode == MeasureSpec.EXACTLY) {
  78. // We were told how big to be
  79. result = specSize;
  80. }else{
  81. // Measure the text
  82. result = mOval_r + getPaddingLeft()
  83. + getPaddingRight();
  84. if(specMode == MeasureSpec.AT_MOST) {
  85. // Respect AT_MOST value if that was what is called for by measureSpec
  86. result = Math.min(result, specSize);
  87. }
  88. }
  89. returnresult;
  90. }
  91. /**
  92. * Determines the height of this view
  93. * @param measureSpec A measureSpec packed into an int
  94. * @return The height of the view, honoring constraints from measureSpec
  95. */
  96. privateintmeasureHeight(intmeasureSpec) {
  97. intresult =0;
  98. intspecMode = MeasureSpec.getMode(measureSpec);
  99. intspecSize = MeasureSpec.getSize(measureSpec);
  100. if(specMode == MeasureSpec.EXACTLY) {
  101. // We were told how big to be
  102. result = specSize;
  103. }else{
  104. // Measure the text (beware: ascent is a negative number)
  105. result = mOval_b + getPaddingTop()
  106. + getPaddingBottom();
  107. if(specMode == MeasureSpec.AT_MOST) {
  108. // Respect AT_MOST value if that was what is called for by measureSpec
  109. result = Math.min(result, specSize);
  110. }
  111. }
  112. returnresult;
  113. }
  114. }
[java] view plain copy
  1. packagecom.myos;
  2. importandroid.content.Context;
  3. importandroid.content.res.TypedArray;
  4. importandroid.graphics.Canvas;
  5. importandroid.graphics.Color;
  6. importandroid.graphics.Paint;
  7. importandroid.graphics.RectF;
  8. importandroid.util.AttributeSet;
  9. importandroid.util.Log;
  10. importandroid.view.View;
  11. importandroid.view.View.MeasureSpec;
  12. publicclassRocketViewextendsView{
  13. privatePaintmOvalPaint;
  14. privateintmStrokeWidth=2;
  15. privateintpadding=3;
  16. //椭圆参数
  17. privateintmOval_l;
  18. privateintmOval_t;
  19. privateintmOval_r;
  20. privateintmOval_b;
  21. //构造
  22. publicRocketView(Contextcontext,AttributeSetattrs){
  23. super(context,attrs);
  24. initRocketView();
  25. TypedArraya=context.obtainStyledAttributes(attrs,
  26. R.styleable.RocketView);
  27. mOval_l=a.getDimensionPixelOffset(R.styleable.RocketView_ovalLeft,padding);
  28. mOval_t=a.getDimensionPixelOffset(R.styleable.RocketView_ovalTop,padding);
  29. mOval_r=a.getDimensionPixelOffset(R.styleable.RocketView_ovalRight,100);
  30. mOval_b=a.getDimensionPixelOffset(R.styleable.RocketView_ovalBottom,100);
  31. a.recycle();
  32. }
  33. privatevoidinitRocketView(){
  34. mOvalPaint=newPaint();
  35. mOvalPaint.setAntiAlias(true);
  36. mOvalPaint.setColor(Color.BLUE);
  37. mOvalPaint.setStyle(Paint.Style.STROKE);
  38. mOvalPaint.setStrokeWidth(mStrokeWidth);
  39. setPadding(padding,padding,padding,padding);
  40. }
  41. publicvoidsetOvalRect(intl,intt,intr,intb){
  42. mOval_l=l+padding;
  43. mOval_t=t+padding;
  44. mOval_r=r;
  45. mOval_b=b;
  46. requestLayout();
  47. invalidate();
  48. }
  49. @Override
  50. protectedvoidonDraw(Canvascanvas){
  51. //TODOAuto-generatedmethodstub
  52. super.onDraw(canvas);
  53. canvas.drawColor(Color.WHITE);
  54. //绘制椭圆
  55. RectFre11=newRectF(mOval_l,mOval_t,mOval_r,mOval_b);
  56. canvas.drawOval(re11,mOvalPaint);
  57. ////绘制圆形
  58. //canvas.drawCircle(mCircle_x,mCircle_y,mCircle_r,mPaint);
  59. }
  60. /**
  61. *@seeandroid.view.View#measure(int,int)
  62. */
  63. @Override
  64. protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){
  65. setMeasuredDimension(measureWidth(widthMeasureSpec),
  66. measureHeight(heightMeasureSpec));
  67. }
  68. /**
  69. *Determinesthewidthofthisview
  70. *@parammeasureSpecAmeasureSpecpackedintoanint
  71. *@returnThewidthoftheview,honoringconstraintsfrommeasureSpec
  72. */
  73. privateintmeasureWidth(intmeasureSpec){
  74. intresult=0;
  75. intspecMode=MeasureSpec.getMode(measureSpec);
  76. intspecSize=MeasureSpec.getSize(measureSpec);
  77. if(specMode==MeasureSpec.EXACTLY){
  78. //Weweretoldhowbigtobe
  79. result=specSize;
  80. }else{
  81. //Measurethetext
  82. result=mOval_r+getPaddingLeft()
  83. +getPaddingRight();
  84. if(specMode==MeasureSpec.AT_MOST){
  85. //RespectAT_MOSTvalueifthatwaswhatiscalledforbymeasureSpec
  86. result=Math.min(result,specSize);
  87. }
  88. }
  89. returnresult;
  90. }
  91. /**
  92. *Determinestheheightofthisview
  93. *@parammeasureSpecAmeasureSpecpackedintoanint
  94. *@returnTheheightoftheview,honoringconstraintsfrommeasureSpec
  95. */
  96. privateintmeasureHeight(intmeasureSpec){
  97. intresult=0;
  98. intspecMode=MeasureSpec.getMode(measureSpec);
  99. intspecSize=MeasureSpec.getSize(measureSpec);
  100. if(specMode==MeasureSpec.EXACTLY){
  101. //Weweretoldhowbigtobe
  102. result=specSize;
  103. }else{
  104. //Measurethetext(beware:ascentisanegativenumber)
  105. result=mOval_b+getPaddingTop()
  106. +getPaddingBottom();
  107. if(specMode==MeasureSpec.AT_MOST){
  108. //RespectAT_MOSTvalueifthatwaswhatiscalledforbymeasureSpec
  109. result=Math.min(result,specSize);
  110. }
  111. }
  112. returnresult;
  113. }
  114. }

在构造中getDimensionPixelOffset检索出一个属性值,没有的话就使用第2个参数做默认值,

可以在布局xml中初始化这个属性值,下面是我main.xml

[java] view plain copy
  1. <?xml version="1.0"encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res/com.myos"
  4. android:orientation="vertical"
  5. android:layout_width="fill_parent"
  6. android:layout_height="fill_parent"
  7. >
  8. <TextView
  9. android:layout_width="wrap_content"
  10. android:layout_height="wrap_content"
  11. android:text="@string/hello"
  12. />
  13. <com.myos.RocketView
  14. app:ovalLeft="0dp"
  15. android:id="@+id/rv"
  16. android:layout_width="wrap_content"
  17. android:layout_height="wrap_content"/>
  18. </LinearLayout>
[java] view plain copy
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res/com.myos"
  4. android:orientation="vertical"
  5. android:layout_width="fill_parent"
  6. android:layout_height="fill_parent"
  7. >
  8. <TextView
  9. android:layout_width="wrap_content"
  10. android:layout_height="wrap_content"
  11. android:text="@string/hello"
  12. />
  13. <com.myos.RocketView
  14. app:ovalLeft="0dp"
  15. android:id="@+id/rv"
  16. android:layout_width="wrap_content"
  17. android:layout_height="wrap_content"/>
  18. </LinearLayout>
我这里没啥用处,仅仅试一下,待扩展属性时再说,说明一下xmlns:app中app可以随便命名的,这个android的规矩还真多

另外构造函数中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
  1. packagecom.myos;
  2. importandroid.app.Activity;
  3. importandroid.os.Bundle;
  4. publicclassMainActivityextendsActivity {
  5. RocketView mRocketView;
  6. /** Called when the activity is first created. */
  7. @Override
  8. publicvoidonCreate(Bundle savedInstanceState) {
  9. super.onCreate(savedInstanceState);
  10. setContentView(R.layout.main);
  11. mRocketView = (RocketView)findViewById(R.id.rv);
  12. mRocketView.setOvalRect(0,0,500,100);
  13. }
  14. }
[java] view plain copy
  1. packagecom.myos;
  2. importandroid.app.Activity;
  3. importandroid.os.Bundle;
  4. publicclassMainActivityextendsActivity{
  5. RocketViewmRocketView;
  6. /**Calledwhentheactivityisfirstcreated.*/
  7. @Override
  8. publicvoidonCreate(BundlesavedInstanceState){
  9. super.onCreate(savedInstanceState);
  10. setContentView(R.layout.main);
  11. mRocketView=(RocketView)findViewById(R.id.rv);
  12. mRocketView.setOvalRect(0,0,500,100);
  13. }
  14. }

可以通过setOvalRect函数来动态调整椭圆的大小!

下面是运行效果,ko啦!


Android 自定义view 和 onMeasure方法介绍_第1张图片




onMeasure方法在控件的父元素正要放置它的子控件时调用.它会问一个问题,“你想要用多大地方啊?”,然后传入两个参数——widthMeasureSpec和heightMeasureSpec.

  它们指明控件可获得的空间以及关于这个空间描述的元数据.
  比返回一个结果要好的方法是你传递View的高度和宽度到setMeasuredDimension方法里.
(请发邮件到 [email protected] 获得翻强软件,能上youtube哟。)
  接下来的代码片段给出了如何重写onMeasure.注意,调用的本地空方法是来计算高度和宽度的.它们会译解widthHeightSpec和heightMeasureSpec值,并计算出合适的高度和宽度值.

java代码:

  1. @Override
  2. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

  3. int measuredHeight = measureHeight(heightMeasureSpec);
  4. int measuredWidth = measureWidth(widthMeasureSpec);
  5. setMeasuredDimension(measuredHeight, measuredWidth);
  6. }

  7. private int measureHeight(int measureSpec) {


  8. // Return measured widget height.
  9. }

  10. private int measureWidth(int measureSpec) {

  11. // Return measured widget width.
  12. }
复制代码


边界参数——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代码:

  1. int specMode = MeasureSpec.getMode(measureSpec);
  2. int specSize = MeasureSpec.getSize(measureSpec);
复制代码


依据specMode的值,如果是AT_MOST,specSize 代表的是最大可获得的空间;如果是EXACTLY,specSize 代表的是精确的尺寸;如果是UNSPECIFIED,对于控件尺寸来说,没有任何参考意义。
  当以EXACT方式标记测量尺寸,父元素会坚持在一个指定的精确尺寸区域放置View。在父元素问子元素要多大空间时,AT_MOST指示者会说给我最大的范围。在很多情况下,你得到的值都是相同的。
  在两种情况下,你必须绝对的处理这些限制。在一些情况下,它可能会返回超出这些限制的尺寸,在这种情况下,你可以让父元素选择如何对待超出的View,使用裁剪还是滚动等技术。

  接下来的框架代码给出了处理View测量的典型实现:

java代码:

  1. @Override

  2. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

  3. int measuredHeight = measureHeight(heightMeasureSpec);

  4. int measuredWidth = measureWidth(widthMeasureSpec);

  5. setMeasuredDimension(measuredHeight, measuredWidth);

  6. }

  7. private int measureHeight(int measureSpec) {

  8. int specMode = MeasureSpec.getMode(measureSpec);
  9. int specSize = MeasureSpec.getSize(measureSpec);

  10. // Default size if no limits are specified.

  11. int result = 500;
  12. if (specMode == MeasureSpec.AT_MOST){

  13. // Calculate the ideal size of your
  14. // control within this maximum size.
  15. // If your control fills the available
  16. // space return the outer bound.

  17. result = specSize;
  18. }
  19. else if (specMode == MeasureSpec.EXACTLY){

  20. // If your control can fit within these bounds return that value.
  21. result = specSize;
  22. }

  23. return result;
  24. }

  25. private int measureWidth(int measureSpec) {
  26. int specMode = MeasureSpec.getMode(measureSpec);
  27. int specSize = MeasureSpec.getSize(measureSpec);

  28. // Default size if no limits are specified.
  29. int result = 500;
  30. if (specMode == MeasureSpec.AT_MOST){
  31. // Calculate the ideal size of your control
  32. // within this maximum size.
  33. // If your control fills the available space
  34. // return the outer bound.
  35. result = specSize;
  36. }

  37. else if (specMode == MeasureSpec.EXACTLY){
  38. // If your control can fit within these bounds return that value.

  39. result = specSize;
  40. }

  41. return result;
  42. }

更多相关文章

  1. android 使用statfs获得文件路径可用空间大小的方法
  2. android 共享元素
  3. Android Launcher2 icon大小修改
  4. Android中的一个TextView中的字体设置不同大小
  5. Android自定义对话框的大小
  6. android app -- Picasso 二级缓存加载图片,可控制加载图片大小(附
  7. Android:自适应不同分辨率的屏幕大小、方向和不同分辨率的字体大

随机推荐

  1. Android三种播放视频的方式
  2. Android探索之旅 | AIDL原理和实例讲解
  3. Mac AndroidStudio真机调试
  4. Android(安卓)Service
  5. 再谈 android 设备SN的获取 续 android
  6. Android引路蜂地图开发示例:第一个地图应
  7. android手机图片查看
  8. android download 下载管理
  9. Android之android:theme设置在Applicatio
  10. Android开发:还是Menu