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

来源:互联网 发布:伤感的网络歌曲有哪些 编辑:程序博客网 时间:2024/05/17 04:34


前言

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

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

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

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

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

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

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

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

package com.example.compass;import android.content.Context;import android.content.res.Resources;import android.graphics.Canvas;import android.graphics.Paint;import android.util.AttributeSet;import android.view.View;import android.view.accessibility.AccessibilityEvent;public class Compass extends View {private Paint makerPaint;private Paint textPaint;private Paint circlePaint;private String north, south, east, west;private int textHeight;public Compass(Context context) {super(context);initCompassView();}public Compass(Context context, AttributeSet attrs) {super(context, attrs);initCompassView();}public Compass(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);initCompassView();}private void initCompassView() {setFocusable(true);Resources r = this.getResources();// 画圆circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);circlePaint.setColor(r.getColor(R.color.background_color));circlePaint.setStrokeWidth(1);circlePaint.setStyle(Paint.Style.FILL_AND_STROKE);north = r.getString(R.string.cardinal_north);south = r.getString(R.string.cardinal_south);east = r.getString(R.string.cardinal_east);west = r.getString(R.string.cardinal_west);textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);textPaint.setColor(r.getColor(R.color.text_color));textHeight = (int) textPaint.measureText("yY");makerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//抗锯齿makerPaint.setColor(r.getColor(R.color.maker_color));}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int measureWidth = measure(widthMeasureSpec);int measureHeight = measure(heightMeasureSpec);int d = Math.min(measureHeight, measureWidth);setMeasuredDimension(d, d);}private int measure(int measureSpec) {int result = 0;// 对测量说明进行解码int speMode = MeasureSpec.getMode(measureSpec);int speSize = MeasureSpec.getSize(measureSpec);if (speMode == MeasureSpec.UNSPECIFIED) {// 如果没有指定界限,则默认返回大小200result = 200;} else {// 由于你希望填充可以的空间,所有总是返回整个可用的的边界result = speSize;}return result;}//添加属性private float bearing;public float getBearing() {return bearing;}public void setBearing(float _bearing) {bearing = _bearing;sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);//添加可访问性支持,罗盘显示方向}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);int mMeasureWidth = getMeasuredWidth();int mMeasureHeight = getMeasuredHeight();int px = mMeasureWidth / 2;int py = mMeasureHeight / 2;int radius = Math.min(px, py);//去最小值作为半径;// 绘制背景canvas.drawCircle(px, py, radius, circlePaint);canvas.save();canvas.rotate(-bearing, px, py);// 旋转-bearing度角度;// 绘制标记int textWidth = (int) textPaint.measureText("W");int cardinalX = px - textWidth / 2;int cardinalY = py - radius + textHeight;// 每15度绘制一个标记,每45度绘制一个文本for (int i = 0; i < 24; i++) {canvas.drawLine(px, py - radius, px, py - radius + 10, makerPaint);canvas.save();canvas.translate(0, textHeight);// 绘制基本方位if (i % 6 == 0) {String dirString = "";switch (i) {case 0:dirString = north;int arrowY = 2 * textHeight;canvas.drawLine(px, arrowY, px - 5, 3 * textHeight, makerPaint);canvas.drawLine(px, arrowY, px + 5, 3 * textHeight, makerPaint);break;case 6:dirString = east;break;case 12:dirString = south;break;case 18:dirString = west;break;default:break;}canvas.drawText(dirString, cardinalX, cardinalY, textPaint);// 每45度绘制文本} else if (i % 3 == 0) {String angle = String.valueOf(i * 15);float angleTextWidth = textPaint.measureText(angle);int angleTextX = (int) (px - angleTextWidth / 2);int angleTextY = py - radius + textHeight;canvas.drawText(angle, angleTextX, angleTextY, textPaint);}canvas.restore();canvas.rotate(15, px, py);}canvas.restore();}// 将当前方向用作可访问性事件使用的内容@Overridepublic boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {super.dispatchPopulateAccessibilityEvent(event);if (isShown()) {String bearingStr = String.valueOf(bearing);if (bearingStr.length() > AccessibilityEvent.MAX_TEXT_LENGTH)bearingStr = bearingStr.substring(0, AccessibilityEvent.MAX_TEXT_LENGTH);event.getText().add(bearingStr);return true;} else {return false;}}}

二、配置属性

<?xml version="1.0" encoding="utf-8"?><resources>    <string name="app_name">Compass</string>    <string name="hello_world">Hello world!</string>    <string name="action_settings">Settings</string>    <string name="cardinal_north" >N</string>    <string name="cardinal_east" >E</string>    <string name="cardinal_south" >S</string>    <string name="cardinal_west" >W</string>    </resources>

<?xml version="1.0" encoding="utf-8"?><resources>    <color name="background_color">#F555</color>    <color name="maker_color">#AFFF</color>    <color name="text_color">#AFFF</color>    </resources>


三、引入自定义控件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent" >    <com.example.compass.Compass        android:id="@+id/compass"        android:layout_width="match_parent"        android:layout_height="match_parent" /></RelativeLayout>

package com.example.compass;import android.app.Activity;import android.os.Bundle;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Compass compass=(Compass) this.findViewById(R.id.compass);compass.setBearing(0);}}

运行实例:


喜欢的朋友关注我和我的公众号!谢谢
1 0