Android实战简易教程<四十六>(自定义控件体验之罗盘)

前言

作为一名有创新意思的开发人员,你迟早会发现内置的控件会满足不了你的想象力。

拥有扩展已存在的视图、组建复合的控件以及创建独特的新视图能力,可以创建出最适合自己应用程序工作流的有优美用户界面,让用户得到最优的体验。

创建新视图的最佳方法和希望达到的目标有关:

1.如果现有控件已经可以满足希望实现的基本功能,那么只需对现有控件的外观或行为进行修改或扩展即可。通过重写事件处理程序和onDraw()方法。

2.可以通过组合多个视图来创建不可分割的、可重用的控件,从而使它可以综合使用过个相关联的视图功能,比如一键清空TextView组合控件。

3.创建一个全新的控件。

下面我们通过一个小实例,创建一个罗盘界面来体验一下如何自定义控件。

一.创建自定义控件类Compass,继承View:

[java]  view plain copy
  1. package com.example.compass;  
  2.   
  3. import android.content.Context;  
  4. import android.content.res.Resources;  
  5. import android.graphics.Canvas;  
  6. import android.graphics.Paint;  
  7. import android.util.AttributeSet;  
  8. import android.view.View;  
  9. import android.view.accessibility.AccessibilityEvent;  
  10.   
  11. public class Compass extends View {  
  12.     private Paint makerPaint;  
  13.     private Paint textPaint;  
  14.     private Paint circlePaint;  
  15.     private String north, south, east, west;  
  16.     private int textHeight;  
  17.   
  18.     public Compass(Context context) {  
  19.         super(context);  
  20.         initCompassView();  
  21.     }  
  22.   
  23.     public Compass(Context context, AttributeSet attrs) {  
  24.         super(context, attrs);  
  25.         initCompassView();  
  26.     }  
  27.   
  28.     public Compass(Context context, AttributeSet attrs, int defStyleAttr) {  
  29.         super(context, attrs, defStyleAttr);  
  30.         initCompassView();  
  31.     }  
  32.   
  33.     private void initCompassView() {  
  34.         setFocusable(true);  
  35.         Resources r = this.getResources();  
  36.         // 画圆  
  37.         circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  38.         circlePaint.setColor(r.getColor(R.color.background_color));  
  39.         circlePaint.setStrokeWidth(1);  
  40.         circlePaint.setStyle(Paint.Style.FILL_AND_STROKE);  
  41.   
  42.         north = r.getString(R.string.cardinal_north);  
  43.         south = r.getString(R.string.cardinal_south);  
  44.         east = r.getString(R.string.cardinal_east);  
  45.         west = r.getString(R.string.cardinal_west);  
  46.   
  47.         textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  48.         textPaint.setColor(r.getColor(R.color.text_color));  
  49.   
  50.         textHeight = (int) textPaint.measureText("yY");  
  51.   
  52.         makerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//抗锯齿  
  53.         makerPaint.setColor(r.getColor(R.color.maker_color));  
  54.   
  55.     }  
  56.   
  57.     @Override  
  58.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  59.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  60.         int measureWidth = measure(widthMeasureSpec);  
  61.         int measureHeight = measure(heightMeasureSpec);  
  62.         int d = Math.min(measureHeight, measureWidth);  
  63.         setMeasuredDimension(d, d);  
  64.     }  
  65.   
  66.     private int measure(int measureSpec) {  
  67.         int result = 0;  
  68.         // 对测量说明进行解码  
  69.         int speMode = MeasureSpec.getMode(measureSpec);  
  70.         int speSize = MeasureSpec.getSize(measureSpec);  
  71.   
  72.         if (speMode == MeasureSpec.UNSPECIFIED) {  
  73.             // 如果没有指定界限,则默认返回大小200  
  74.             result = 200;  
  75.         } else {  
  76.             // 由于你希望填充可以的空间,所有总是返回整个可用的的边界  
  77.             result = speSize;  
  78.         }  
  79.         return result;  
  80.     }  
  81.   
  82.     //添加属性  
  83.     private float bearing;  
  84.   
  85.     public float getBearing() {  
  86.         return bearing;  
  87.     }  
  88.   
  89.     public void setBearing(float _bearing) {  
  90.         bearing = _bearing;  
  91.         sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);//添加可访问性支持,罗盘显示方向  
  92.     }  
  93.   
  94.     @Override  
  95.     protected void onDraw(Canvas canvas) {  
  96.         super.onDraw(canvas);  
  97.         int mMeasureWidth = getMeasuredWidth();  
  98.         int mMeasureHeight = getMeasuredHeight();  
  99.         int px = mMeasureWidth / 2;  
  100.         int py = mMeasureHeight / 2;  
  101.         int radius = Math.min(px, py);//去最小值作为半径;  
  102.   
  103.         // 绘制背景  
  104.         canvas.drawCircle(px, py, radius, circlePaint);  
  105.   
  106.         canvas.save();  
  107.         canvas.rotate(-bearing, px, py);// 旋转-bearing度角度;  
  108.   
  109.         // 绘制标记  
  110.   
  111.         int textWidth = (int) textPaint.measureText("W");  
  112.         int cardinalX = px - textWidth / 2;  
  113.         int cardinalY = py - radius + textHeight;  
  114.   
  115.         // 每15度绘制一个标记,每45度绘制一个文本  
  116.   
  117.         for (int i = 0; i < 24; i++) {  
  118.             canvas.drawLine(px, py - radius, px, py - radius + 10, makerPaint);  
  119.             canvas.save();  
  120.             canvas.translate(0, textHeight);  
  121.   
  122.             // 绘制基本方位  
  123.             if (i % 6 == 0) {  
  124.                 String dirString = "";  
  125.                 switch (i) {  
  126.                 case 0:  
  127.                     dirString = north;  
  128.                     int arrowY = 2 * textHeight;  
  129.                     canvas.drawLine(px, arrowY, px - 53 * textHeight, makerPaint);  
  130.                     canvas.drawLine(px, arrowY, px + 53 * textHeight, makerPaint);  
  131.                     break;  
  132.                 case 6:  
  133.                     dirString = east;  
  134.                     break;  
  135.                 case 12:  
  136.                     dirString = south;  
  137.                     break;  
  138.                 case 18:  
  139.                     dirString = west;  
  140.                     break;  
  141.   
  142.                 default:  
  143.                     break;  
  144.                 }  
  145.                 canvas.drawText(dirString, cardinalX, cardinalY, textPaint);  
  146.                 // 每45度绘制文本  
  147.             } else if (i % 3 == 0) {  
  148.                 String angle = String.valueOf(i * 15);  
  149.                 float angleTextWidth = textPaint.measureText(angle);  
  150.                 int angleTextX = (int) (px - angleTextWidth / 2);  
  151.                 int angleTextY = py - radius + textHeight;  
  152.                 canvas.drawText(angle, angleTextX, angleTextY, textPaint);  
  153.             }  
  154.             canvas.restore();  
  155.             canvas.rotate(15, px, py);  
  156.         }  
  157.         canvas.restore();  
  158.   
  159.     }  
  160.   
  161.     // 将当前方向用作可访问性事件使用的内容  
  162.     @Override  
  163.     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {  
  164.         super.dispatchPopulateAccessibilityEvent(event);  
  165.         if (isShown()) {  
  166.             String bearingStr = String.valueOf(bearing);  
  167.             if (bearingStr.length() > AccessibilityEvent.MAX_TEXT_LENGTH)  
  168.                 bearingStr = bearingStr.substring(0, AccessibilityEvent.MAX_TEXT_LENGTH);  
  169.             event.getText().add(bearingStr);  
  170.             return true;  
  171.         } else {  
  172.             return false;  
  173.         }  
  174.   
  175.     }  
  176.   
  177. }  

二、配置属性

[html]  view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.   
  4.     <string name="app_name">Compass</string>  
  5.     <string name="hello_world">Hello world!</string>  
  6.     <string name="action_settings">Settings</string>  
  7.     <string name="cardinal_north" >N</string>  
  8.     <string name="cardinal_east" >E</string>  
  9.     <string name="cardinal_south" >S</string>  
  10.     <string name="cardinal_west" >W</string>  
  11.       
  12.   
  13. </resources>  

[html]  view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.     <color name="background_color">#F555</color>  
  4.     <color name="maker_color">#AFFF</color>  
  5.     <color name="text_color">#AFFF</color>      
  6. </resources>  


三、引入自定义控件

[html]  view plain copy
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent" >  
  5.   
  6.     <com.example.compass.Compass  
  7.         android:id="@+id/compass"  
  8.         android:layout_width="match_parent"  
  9.         android:layout_height="match_parent" />  
  10.   
  11. </RelativeLayout>  

[java]  view plain copy
  1. package com.example.compass;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5.   
  6. public class MainActivity extends Activity {  
  7.   
  8.     @Override  
  9.     protected void onCreate(Bundle savedInstanceState) {  
  10.         super.onCreate(savedInstanceState);  
  11.         setContentView(R.layout.activity_main);  
  12.         Compass compass=(Compass) this.findViewById(R.id.compass);  
  13.         compass.setBearing(0);  
  14.     }  
  15.   
  16.       
  17. }  

运行实例:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值