转自: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 print ?
  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>

然后写个RocketView

[java] view plain copy print ?
  1. package com.myos;
  2. import android.content.Context;
  3. import android.content.res.TypedArray;
  4. import android.graphics.Canvas;
  5. import android.graphics.Color;
  6. import android.graphics.Paint;
  7. import android.graphics.RectF;
  8. import android.util.AttributeSet;
  9. import android.util.Log;
  10. import android.view.View;
  11. import android.view.View.MeasureSpec;
  12. public class RocketView extends View{
  13. private Paint mOvalPaint;
  14. private int mStrokeWidth = 2;
  15. private int padding = 3;
  16. //椭圆参数
  17. private int mOval_l;
  18. private int mOval_t;
  19. private int mOval_r;
  20. private int mOval_b;
  21. //构造
  22. public RocketView(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. private void initRocketView() {
  34. mOvalPaint = new Paint();
  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. public void setOvalRect(int l, int t, int r, int b){
  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. protected void onDraw(Canvas canvas) {
  51. // TODO Auto-generated method stub
  52. super.onDraw(canvas);
  53. canvas.drawColor(Color.WHITE);
  54. // 绘制椭圆
  55. RectF re11 = new RectF(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. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  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. private int measureWidth(int measureSpec) {
  74. int result = 0;
  75. int specMode = MeasureSpec.getMode(measureSpec);
  76. int specSize = 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. return result;
  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. private int measureHeight(int measureSpec) {
  97. int result = 0;
  98. int specMode = MeasureSpec.getMode(measureSpec);
  99. int specSize = 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. return result;
  113. }
  114. }

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

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

[java] view plain copy print ?
  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>
我这里没啥用处,仅仅试一下,待扩展属性时再说,说明一下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 print ?
  1. package com.myos;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. public class MainActivity extends Activity {
  5. RocketView mRocketView;
  6. /** Called when the activity is first created. */
  7. @Override
  8. public void onCreate(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. }

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

下面是运行效果,ko啦!






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(安卓)购物车UI及逻辑实现
  2. Android笔记:Android中的使用
  3. Android:使用canvas绘制饼状统计图(自动适应条目数量/大小)
  4. AudioTrack学习
  5. RN(Flexbox)与Android对比
  6. android ProgressBar被拉伸或压缩变形,显示不全
  7. Android手势滑动实现ImageView缩放图片大小
  8. Android(安卓)WebView通过代码修改网页的字体大小
  9. Android(安卓)使用aab 发布

随机推荐

  1. Android的UI两大基石
  2. android ImageView src属性不支持.9图
  3. android中的线程池
  4. Android应用程序获取系统权限
  5. Android调用WIFI设置
  6. Android(安卓)对话框(Dialog)大全 建立你
  7. Android中常用的五种布局
  8. android > Service
  9. Android(安卓)MMS,SMS之常用Uri
  10. Android(安卓)SDK 下载 链接