• android 自定义控件---圆形方向盘


    在做Android平台开发的时候,经常会遇到安卓原生控件无法满足需求的情况,安卓允许开发者去继承已经存在的控件或者实现你自己的控件。

    先来看一下效果图

    circl

    采用直接集成View类,重写onDrow方法绘制。

    下面附上主要代码。


    1 新建一个类CircleView 继承自View

    image

      1 package com.lennon.view;
      2 
      3 import android.content.Context;
      4 import android.graphics.Canvas;
      5 import android.graphics.Color;
      6 import android.graphics.Paint;
      7 import android.graphics.Path;
      8 import android.graphics.RectF;
      9 import android.util.AttributeSet;
     10 import android.view.MotionEvent;
     11 import android.view.View;
     12 /**
     13  * 自定义圆形的方向布局
     14  * 
     15  * @author 樊列龙
     16  * @since 2014-06-07
     17  */
     18 public class CircleView extends View {
     19 
     20     private int circleWidth = 100; // 圆环直径
     21     private int circleColor = Color.argb(150, 255, 0, 0);
     22     private int innerCircleColor = Color.rgb(0, 150, 0);
     23     private int backgroundColor = Color.rgb(255, 255, 255);
     24     private Paint paint = new Paint();
     25     int center = 0;
     26     int innerRadius = 0;
     27     private float innerCircleRadius = 0;
     28     private float smallCircle = 10;
     29     public Dir dir = Dir.UP;
     30 
     31     public CircleView(Context context, AttributeSet attrs) {
     32         super(context, attrs);
     33     }
     34 
     35     public CircleView(Context context) {
     36         super(context);
     37 
     38         // paint = new Paint();
     39     }
     40 
     41     public CircleView(Context context, AttributeSet attrs, int defStyle) {
     42         super(context, attrs, defStyle);
     43 
     44     }
     45 
     46     @Override
     47     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     48         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     49 
     50         int measuredHeight = measureHeight(heightMeasureSpec);
     51         int measuredWidth = measureWidth(widthMeasureSpec);
     52 
     53         setMeasuredDimension(measuredWidth, measuredHeight);
     54 
     55         center = getWidth() / 2;
     56         innerRadius = (center - circleWidth / 2 - 10);// 圆环
     57         innerCircleRadius = center / 3;
     58         this.setOnTouchListener(onTouchListener);
     59     }
     60 
     61     /**
     62      * 测量宽度
     63      * 
     64      * @param measureSpec
     65      * @return
     66      */
     67     private int measureWidth(int measureSpec) {
     68         int specMode = MeasureSpec.getMode(measureSpec);
     69         int specSize = MeasureSpec.getSize(measureSpec);
     70 
     71         int result = 0;
     72 
     73         if (specMode == MeasureSpec.AT_MOST) {
     74             result = getWidth();
     75         } else if (specMode == MeasureSpec.EXACTLY) {
     76             result = specSize;
     77         }
     78         return result;
     79     }
     80 
     81     /**
     82      * 测量高度
     83      * 
     84      * @param measureSpec
     85      * @return
     86      */
     87     private int measureHeight(int measureSpec) {
     88 
     89         int specMode = MeasureSpec.getMode(measureSpec);
     90         int specSize = MeasureSpec.getSize(measureSpec);
     91 
     92         int result = 0;
     93 
     94         if (specMode == MeasureSpec.AT_MOST) {
     95 
     96             result = specSize;
     97         } else if (specMode == MeasureSpec.EXACTLY) {
     98             result = specSize;
     99         }
    100         return result;
    101     }
    102 
    103     /**
    104      * 开始绘制
    105      */
    106     @Override
    107     protected void onDraw(Canvas canvas) {
    108         super.onDraw(canvas);
    109 
    110         initBackGround(canvas);
    111         drawDirTriangle(canvas, dir);
    112 
    113     }
    114 
    115     /**
    116      * 绘制方向小箭头
    117      * 
    118      * @param canvas
    119      */
    120     private void drawDirTriangle(Canvas canvas, Dir dir) {
    121         paint.setColor(innerCircleColor);
    122         paint.setStrokeWidth(1);
    123         paint.setStyle(Paint.Style.FILL);
    124 
    125         switch (dir) {
    126         case UP:
    127             drawUpTriangle(canvas);
    128             break;
    129         case DOWN:
    130             drawDownTriangle(canvas);
    131             break;
    132         case LEFT:
    133             drawLeftTriangle(canvas);
    134             break;
    135         case RIGHT:
    136             drawRightTriangle(canvas);
    137             break;
    138         case CENTER:
    139             invalidate();
    140             break;
    141         default:
    142             break;
    143         }
    144 
    145         paint.setColor(backgroundColor);
    146 
    147         canvas.drawCircle(center, center, smallCircle, paint);
    148         // canvas.drawText(text, center, center+40, paint);
    149 
    150     }
    151 
    152     /**
    153      * 绘制向右的小箭头
    154      * 
    155      * @param canvas
    156      */
    157     private void drawRightTriangle(Canvas canvas) {
    158         Path path = new Path();
    159         path.moveTo(center, center);
    160         double sqrt2 = innerCircleRadius / Math.sqrt(2);
    161         double pow05 = innerCircleRadius * Math.sqrt(2);
    162         path.lineTo((float) (center + sqrt2), (float) (center - sqrt2));
    163         path.lineTo((float) (center + pow05), center);
    164         path.lineTo((float) (center + sqrt2), (float) (center + sqrt2));
    165         canvas.drawPath(path, paint);
    166         paint.setColor(backgroundColor);
    167         canvas.drawLine(center, center, center + innerCircleRadius, center, paint);
    168 
    169         drawOnclikColor(canvas, Dir.RIGHT);
    170     }
    171 
    172     /**
    173      * 绘制想左的小箭头
    174      * 
    175      * @param canvas
    176      */
    177     private void drawLeftTriangle(Canvas canvas) {
    178         Path path = new Path();
    179         path.moveTo(center, center);
    180         double sqrt2 = innerCircleRadius / Math.sqrt(2);
    181         double pow05 = innerCircleRadius * Math.sqrt(2);
    182         path.lineTo((float) (center - sqrt2), (float) (center - sqrt2));
    183         path.lineTo((float) (center - pow05), center);
    184         path.lineTo((float) (center - sqrt2), (float) (center + sqrt2));
    185         canvas.drawPath(path, paint);
    186 
    187         paint.setColor(backgroundColor);
    188         canvas.drawLine(center, center, center - innerCircleRadius, center, paint);
    189 
    190         drawOnclikColor(canvas, Dir.LEFT);
    191 
    192     }
    193 
    194     /**
    195      * 绘制向下的小箭头
    196      * 
    197      * @param canvas
    198      */
    199     private void drawDownTriangle(Canvas canvas) {
    200         Path path = new Path();
    201         path.moveTo(center, center);
    202         double sqrt2 = innerCircleRadius / Math.sqrt(2);
    203         double pow05 = innerCircleRadius * Math.sqrt(2);
    204         path.lineTo((float) (center - sqrt2), (float) (center + sqrt2));
    205         path.lineTo(center, (float) (center + pow05));
    206         path.lineTo((float) (center + sqrt2), (float) (center + sqrt2));
    207         canvas.drawPath(path, paint);
    208 
    209         paint.setColor(backgroundColor);
    210         canvas.drawLine(center, center, center, center + innerCircleRadius, paint);
    211 
    212         drawOnclikColor(canvas, Dir.DOWN);
    213     }
    214 
    215     /**
    216      * 点击的时候绘制黑色的扇形
    217      * 
    218      * @param canvas
    219      * @param dir
    220      */
    221     private void drawOnclikColor(Canvas canvas, Dir dir) {
    222         paint.setColor(Color.BLACK);
    223         paint.setStyle(Paint.Style.STROKE);
    224         paint.setStrokeWidth(100);
    225         switch (dir) {
    226         case UP:
    227             canvas.drawArc(new RectF(center - innerRadius, center - innerRadius, center + innerRadius, center
    228                     + innerRadius), 225, 90, false, paint);
    229             break;
    230         case DOWN:
    231             canvas.drawArc(new RectF(center - innerRadius, center - innerRadius, center + innerRadius, center
    232                     + innerRadius), 45, 90, false, paint);
    233             break;
    234         case LEFT:
    235             canvas.drawArc(new RectF(center - innerRadius, center - innerRadius, center + innerRadius, center
    236                     + innerRadius), 135, 90, false, paint);
    237             break;
    238         case RIGHT:
    239             canvas.drawArc(new RectF(center - innerRadius, center - innerRadius, center + innerRadius, center
    240                     + innerRadius), -45, 90, false, paint);
    241             break;
    242 
    243         default:
    244             break;
    245         }
    246 
    247         paint.setStyle(Paint.Style.FILL);
    248     }
    249 
    250     /**
    251      * 绘制像向上的箭头
    252      * 
    253      * @param canvas
    254      */
    255     private void drawUpTriangle(Canvas canvas) {
    256         Path path = new Path();
    257         path.moveTo(center, center);
    258         double sqrt2 = innerCircleRadius / Math.sqrt(2);
    259         double pow05 = innerCircleRadius * Math.sqrt(2);
    260 
    261         path.lineTo((float) (center - sqrt2), (float) (center - sqrt2));
    262         path.lineTo(center, (float) (center - pow05));
    263         path.lineTo((float) (center + sqrt2), (float) (center - sqrt2));
    264         canvas.drawPath(path, paint);
    265 
    266         paint.setColor(backgroundColor);
    267         canvas.drawLine(center, center, center, center - innerCircleRadius, paint);
    268 
    269         drawOnclikColor(canvas, Dir.UP);
    270     }
    271 
    272     /**
    273      * 绘制基本的背景, 这包括了三个步骤:1.清空画布 2.绘制外圈的圆 3.绘制内圈的圆
    274      * 
    275      * @param canvas
    276      */
    277     private void initBackGround(Canvas canvas) {
    278         clearCanvas(canvas);
    279         drawBackCircle(canvas);
    280         drawInnerCircle(canvas);
    281 
    282     }
    283 
    284     /**
    285      * 绘制中心白色小圆
    286      * 
    287      * @param canvas
    288      */
    289     private void drawInnerCircle(Canvas canvas) {
    290         paint.setColor(innerCircleColor);
    291         paint.setStyle(Paint.Style.FILL);
    292         paint.setStrokeWidth(1);
    293         canvas.drawCircle(center, center, innerCircleRadius, paint);
    294     }
    295 
    296     /**
    297      * 绘制背景的圆圈和隔线
    298      * 
    299      * @param canvas
    300      */
    301     private void drawBackCircle(Canvas canvas) {
    302         paint.setColor(circleColor);
    303         paint.setStrokeWidth(circleWidth);
    304         paint.setAntiAlias(true);
    305         paint.setStyle(Paint.Style.STROKE);
    306         canvas.drawCircle(center, center, innerRadius, paint); // 绘制圆圈
    307 
    308         paint.setColor(backgroundColor);
    309         paint.setStyle(Paint.Style.FILL);
    310         paint.setStrokeWidth(4);
    311         canvas.drawLine(center, center, 0, 0, paint);
    312         canvas.drawLine(center, center, center * 2, 0, paint);
    313         canvas.drawLine(center, center, 0, center * 2, paint);
    314         canvas.drawLine(center, center, center * 2, center * 2, paint);
    315 
    316     }
    317 
    318     /**
    319      * 清空画布
    320      * 
    321      * @param canvas
    322      */
    323     private void clearCanvas(Canvas canvas) {
    324         canvas.drawColor(backgroundColor);
    325     }
    326 
    327     OnTouchListener onTouchListener = new OnTouchListener() {
    328 
    329         @Override
    330         public boolean onTouch(View view, MotionEvent event) {
    331             Dir tmp = Dir.UNDEFINE;
    332             if ((tmp = checkDir(event.getX(), event.getY())) != Dir.UNDEFINE) {
    333                 dir = tmp;
    334                 invalidate();
    335             }
    336             return true;
    337         }
    338 
    339         /**
    340          * 检测方向
    341          * 
    342          * @param x
    343          * @param y
    344          * @return
    345          */
    346         private Dir checkDir(float x, float y) {
    347             Dir dir = Dir.UNDEFINE;
    348 
    349             if (Math.sqrt(Math.pow(y - center, 2) + Math.pow(x - center, 2)) < innerCircleRadius) {// 判断在中心圆圈内
    350                 dir = Dir.CENTER;
    351                 System.out.println("----中央");
    352             } else if (y < x && y + x < 2 * center) {
    353                 dir = Dir.UP;
    354                 System.out.println("----向上");
    355             } else if (y < x && y + x > 2 * center) {
    356                 dir = Dir.RIGHT;
    357                 System.out.println("----向右");
    358             } else if (y > x && y + x < 2 * center) {
    359                 dir = Dir.LEFT;
    360                 System.out.println("----向左");
    361             } else if (y > x && y + x > 2 * center) {
    362                 dir = Dir.DOWN;
    363                 System.out.println("----向下");
    364             }
    365 
    366             return dir;
    367         }
    368 
    369     };
    370 
    371     /**
    372      * 关于方向的枚举
    373      * 
    374      * @author Administrator
    375      * 
    376      */
    377     public enum Dir {
    378         UP, DOWN, LEFT, RIGHT, CENTER, UNDEFINE
    379     }
    380 
    381 }
    View Code

    2 在activity_main.xml中引用CircleView类

     1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" > 
     2 
     3 <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical"> 
     4 
     5 <com.lennon.view.CircleView android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/cv" /> 
     6 
     7 </LinearLayout> 
     8 
     9 </RelativeLayout>
    10 
    11  
    View Code

    好了 可以直接运行处结果了!

    下面对上述代码做一些说明:

    主要的自定义控件的方法有:

    1.有些基本功能原生控件都能提供,所以这个时候你只需要继承并对控件进行扩展。通过重写它的事件,onDraw,但是始终都保持都父类方法的调用。
    
    2.组合控件 就是通过合并几个控件的功能来生成一个控件。
    
    3.完完整整创建一个新的控件。

    我们这里实现的是一个完全自定义的控件,通常是继承View或者SurfaceView ,View类提供一个Canvas(画布)和一系列的画的方法,还有Paint(画笔)。使用它们去创建一个自定义的UI。你可以重写事件,包括屏幕接触或者按键按下等等,用来提供与用户交互。

    1.如果你不需要快速重画和3D图像的效果,那么让View作为父类提供一个轻量级的解决方案。

    2.如若不然,就需要使用SurfaceView作为父类,这样你就可以提供一个后台线程去画和使用OPENGL去实现你的图像。这个就相对重量级了,如果你的视图需要经常更新,然后由需要显示比较复杂的图像信息(尤其是在游戏和3D可视化),SurfaceView将是更好的选择。

    使用这这方式一般你需要重写2个方法:
    1.onMeasure

    什么是onMeasure?

    下面转载一段文章:

    View在屏幕上显示出来要先经过measure(计算)和layout(布局).
    1、什么时候调用onMeasure方法? 
    当控件的父元素正要放置该控件时调用.父元素会问子控件一个问题,“你想要用多大地方啊?”,然后传入两个参数——widthMeasureSpec和heightMeasureSpec.
    这两个参数指明控件可获得的空间以及关于这个空间描述的元数据.

    更好的方法是你传递View的高度和宽度到setMeasuredDimension方法里,这样可以直接告诉父控件,需要多大地方放置子控件.

        widthMeasureSpec和heightMeasureSpec这2个参数都是整形是出于效率的考虑,所以经常要做的就是对其解码=>

    1. int specMode = MeasureSpec.getMode(measureSpec);
    2. int specSize = MeasureSpec.getSize(measureSpec);
    1. 依据specMode的值,(MeasureSpec有3种模式分别是UNSPECIFIED, EXACTLY和AT_MOST)
    2. 如果是AT_MOST,specSize 代表的是最大可获得的空间;
      如果是EXACTLY,specSize 代表的是精确的尺寸;
      如果是UNSPECIFIED,对于控件尺寸来说,没有任何参考意义。
      2、那么这些模式和我们平时设置的layout参数fill_parent, wrap_content有什么关系呢?
      经过代码测试就知道,当我们设置width或height为fill_parent时,容器在布局时调用子 view的measure方法传入的模式是EXACTLY,因为子view会占据剩余容器的空间,所以它大小是确定的。
      而当设置为 wrap_content时,容器传进去的是AT_MOST, 表示子view的大小最多是多少,这样子view会根据这个上限来设置自己的尺寸。当子view的大小设置为精确值时,容器传入的是EXACTLY, 而MeasureSpec的UNSPECIFIED模式表示你没有指定大小。
    3. View的onMeasure方法默认行为是当模式为UNSPECIFIED时,设置尺寸为mMinWidth(通常为0)或者背景drawable的最小尺寸,当模式为EXACTLY或者AT_MOST时,尺寸设置为传入的MeasureSpec的大小。 
      有个观念需要纠正的是,fill_parent应该是子view会占据剩下容器的空间,而不会覆盖前面已布局好的其他view空间,当然后面布局子 view就没有空间给分配了,所以fill_parent属性对布局顺序很重要。以前所想的是把所有容器的空间都占满了,难怪google在2.2版本里 把fill_parent的名字改为match_parent.
      在两种情况下,你必须绝对的处理这些限制。在一些情况下,它可能会返回超出这些限制的尺寸,在这种情况下,你可以让父元素选择如何对待超出的View,使用裁剪还是滚动等技术。
    4. @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) {  // 代码基本类似measureHeight  }

    2 onDraw

    使用Canvas进行图形的绘制

    本文参考了:

    1.http://my.oschina.net/wangjunhe/blog/99764

    2.http://blog.csdn.net/ethan_xue/article/details/7313575

    代码下载地址

    http://download.csdn.net/detail/csulennon/7462169

  • 相关阅读:
    HTTP客户端
    获取IP地址和域名
    SQL语句、PL/SQL块和SQL*Plus命令之间的区别
    oracle中的游标
    oracle表问题
    精简版web浏览器
    oracle的存储过程
    数据库中的视图
    第一次作业
    折半查找
  • 原文地址:https://www.cnblogs.com/csulennon/p/3774898.html
Copyright © 2020-2023  润新知