前言
作为一名有创新意思的开发人员,你迟早会发现内置的控件会满足不了你的想象力。
拥有扩展已存在的视图、组建复合的控件以及创建独特的新视图能力,可以创建出最适合自己应用程序工作流的有优美用户界面,让用户得到最优的体验。
创建新视图的最佳方法和希望达到的目标有关:
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)); } @Override protected 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) { // 如果没有指定界限,则默认返回大小200 result = 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);//添加可访问性支持,罗盘显示方向 } @Override protected 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(); } // 将当前方向用作可访问性事件使用的内容 @Override public 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 { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Compass compass=(Compass) this.findViewById(R.id.compass); compass.setBearing(0); } }
运行实例:
喜欢的朋友关注我和我的公众号!谢谢
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-10-10 21:21:54