1.android 自定义控件初步
以之前一个项目内使用的自定义圆环为例,当用户滑动屏幕时,可以选择不同的刻度,开设置目标值。效果如下:
2.实现过程:
2.1.定义一个自定义View类,继承自View类,同时注意必须使用构造方法,在构造方法中首先调用超类的构造方法
2.2.values文件夹下创建一个attrs.xml 文件定义这个控件的一些基本属性
<!-- 滑动选择目标步数的控件属性设置 -->
<declare-styleable name="SetGoalStepProgressView">
<attr name="color_start" format="color" />
<attr name="color_end" format="color" />
<attr name="circleDimensionRate" format="dimension" />
<attr name="triangleDimensionRate" format="dimension" />
</declare-styleable>
2.3.一般需要重写onMesure() 、OnDraw()方法,如果要在屏幕变换时显示不同,也要重写onSizeChanged()方法
onMesure()可以调用系统的
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} 也可以自己重写
系统帮我们测量的高度和宽度都是MATCH_PARNET,当我们设置明确的宽度和高度时,系统帮我们测量的结果就是我们设置的结果,当我们设置为WRAP_CONTENT,或者MATCH_PARENT系统帮我们测量的结果就是MATCH_PARENT的长度。
所以,当设置了WRAP_CONTENT时,我们需要自己进行测量,即重写onMesure方法”:重写之前先了解MeasureSpec的specMode,一共三种类型:
EXACTLY:一般是设置了明确的值或者是MATCH_PARENTAT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT UNSPECIFIED:表示子布局想要多大就多大,很少使用
onDraw()方法的重写非常重要,它接受了一个canvas(画布)作为参数,
这个控件需要跟用户交互,所以要重写view 的onTouchEvent()方法,这个自定义控件也是在onTouchEvent()方法内完成滑动角度的计算的
直接看代码吧:
package com.example.myautoviewdemo;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class SetGoalStepProgressView extends View {
private int color_start;
private int color_end;
public static int flag;
private float circleDimensionRate = 0.1f;// 圆环比例
private float triangleDimensionRate = 0.1f;// 三角形比例
private float s;// 三角形边长
private float strokeWidth;// 圆环宽度
private float angle = getContext().getSharedPreferences("goalstepfiles",
Context.MODE_WORLD_READABLE)
.getFloat("setgoalangle", (float) 135.0);// 角度0-360
// 第一次设置默认为72度
private RectF oval = new RectF();
private Paint paint = new Paint();
private Path path = new Path();// 绘制三角形的路径
private Path path_matrix = new Path();// 经过matrix变换的三角形路径
private Matrix matrix = new Matrix();// 三角形旋转矩阵
Rect rect = new Rect();// 测量文字所占的高度宽度
private String TAG = "SetGoalStepProgressView";
private int goalstepcount;
private String text;
private SharedPreferences goalPreferences;
int stepcount;
private final float sqrt3 = (float) Math.sqrt(3);
public SetGoalStepProgressView(Context context) {
super(context);
init(null, 0);
}
public SetGoalStepProgressView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0);
}
public SetGoalStepProgressView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
init(attrs, defStyle);
}
private void init(AttributeSet attrs, int defStyle) {
// Load attributes
final TypedArray a = getContext().obtainStyledAttributes(attrs,
R.styleable.SetGoalStepProgressView, defStyle, 0);
color_start = a.getColor(
R.styleable.SetGoalStepProgressView_color_start,
Color.parseColor("#79CDCD"));// 默认绿色
color_end = a.getColor(R.styleable.SetGoalStepProgressView_color_end,
Color.LTGRAY);// 默认灰色
circleDimensionRate = a.getFloat(
R.styleable.SetGoalStepProgressView_circleDimensionRate, 0.1f);
triangleDimensionRate = a
.getFloat(
R.styleable.SetGoalStepProgressView_triangleDimensionRate,
0.1f);
a.recycle();
}
private void initDrawVar(float width) {
if (s != triangleDimensionRate * width) {
s = triangleDimensionRate * width;// 三角形边长
strokeWidth = circleDimensionRate * width;// 圆环宽度
// 绘制圆环的矩形区域
oval.set(s + strokeWidth / 2f, s + strokeWidth / 2f, width - s
- strokeWidth / 2f, width - s - strokeWidth / 2f);
// 绘制三角形的路径
path.reset();
path.moveTo(0, -s / sqrt3);
path.lineTo(s / 2f, sqrt3 / 6 * s);
path.lineTo(-s / 2f, sqrt3 / 6 * s);
path.close();
}
}
@Override
protected void onDraw(Canvas canvas) {
// Log.e(TAG, "run here! onDraw() angle ="+angle);
super.onDraw(canvas);
int width = this.getWidth();
initDrawVar(width);
// 画绿色环形
paint.setAntiAlias(true);
paint.setColor(color_start);
paint.setStrokeWidth(strokeWidth);
paint.setStyle(Paint.Style.STROKE); // 设置空心
// 正右方为0度,顺时针旋转,正上方为270度
canvas.drawArc(oval, 270 - angle, angle, false, paint);
// 画灰色环形
paint.setColor(color_end);
canvas.drawArc(oval, 270, 360 - angle, false, paint);
// 画三角形
paint.setStrokeWidth(1);
paint.setColor(color_start);
paint.setStyle(Paint.Style.FILL);
matrix.reset();
matrix.setRotate(180 - angle);// 旋转
float angle_rad = (float) (angle / 180f * Math.PI);// 化成弧度制
matrix.postTranslate(// 平移
(float) (width / 2f - (width / 2f - s + s / sqrt3)
* Math.sin(angle_rad)), (float) (width / 2f - (width
/ 2f - s + s / sqrt3)
* Math.cos(angle_rad)));
path_matrix.set(path);
path_matrix.transform(matrix);
canvas.drawPath(path_matrix, paint);
// 显示百分比
paint.setTextSize(width * 0.1f);
if (angle == 360.0) {
goalPreferences = getContext().getSharedPreferences(
"goalstepfiles", Context.MODE_WORLD_READABLE);
goalstepcount = goalPreferences.getInt("setgoalstepcount", 10000); // 默认步数为10000步
text = goalstepcount + "";
// paint.getFontMetrics()方法测量不准确,无法保证居中显示;paint.getTextBounds方法测量较准确
paint.getTextBounds(text, 0, text.length(), rect);// 测量text所占宽度和高度
canvas.drawText(text, width / 2f - rect.width() / 2, width / 2f
+ rect.height() / 2, paint);
} else {
// 角度小于72度(目标为4000)时自动设置为最小4000
// text = String.valueOf((Math.round((angle / 360 * 24000)+4000)));
int goaltext_int = (int) ((400 * angle / 9) + 4000);
text = String.valueOf(goaltext_int);
// Log.e("SetGoalStepView", "text_4000 ="+text);
int text_int = Integer.parseInt(text);
if (text_int >= 100) {
int ittext_int = (text_int / 100) * 100;
text = ittext_int + "";
if (text_int > 19950) {
text = 20000 + "";
}
}
// 通过广播的形式给activity发送消息
Intent intent = new Intent("broadcast_action"); // action是broadcast_action;
intent.putExtra("stepcount", text);
intent.putExtra("stepangle", angle);
getContext().sendBroadcast(intent);
paint.getTextBounds(text, 0, text.length(), rect);// 测量text所占宽度和高度
canvas.drawText(text, width / 2f - rect.width() / 2, width / 2f
+ rect.height() / 2, paint);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
float x = event.getX();
float y = event.getY();
float r = this.getWidth() / 2 - s - strokeWidth;// 圆环半径
// 判断在圆环外 则处理触摸事件
if (Math.pow(x - this.getWidth() / 2, 2)
+ Math.pow(y - this.getWidth() / 2, 2) > Math.pow(r, 2)) {
double angle = Math.atan((this.getWidth() / 2 - x)
/ (this.getWidth() / 2 - y));
angle = angle / Math.PI * 180;
if (x > this.getWidth() / 2 && y <= this.getWidth() / 2) {// 第一象限
angle += 360;
} else if (y > this.getWidth() / 2) {// 第三四象限
angle += 180;
}
if (Math.abs(this.angle - angle) > 1) {
this.angle = (float) angle;
this.invalidate();
}
// Log.e(TAG, "run here! onTouchEvent()--angle ="+angle);
}
return true;
}
return super.onTouchEvent(event);
}
// 获取当前选择的比例
public float getRate() {
return angle / 360f;
}
public int getColor_start() {
return color_start;
}
public void setColor_start(int color_start) {
this.color_start = color_start;
}
public int getColor_end() {
return color_end;
}
public void setColor_end(int color_end) {
this.color_end = color_end;
}
public float getCircleDimensionRate() {
return circleDimensionRate;
}
public void setCircleDimensionRate(float circleDimensionRate) {
this.circleDimensionRate = circleDimensionRate;
}
public float getTriangleDimensionRate() {
return triangleDimensionRate;
}
public void setTriangleDimensionRate(float triangleDimensionRate) {
this.triangleDimensionRate = triangleDimensionRate;
}
}