新的任务又来了,这次需要实现一个仪表盘的自定义控件,自定义控件一不常写就手生,这次又巩固下,并且学了一些新知识。
https://developer.android.com/training/custom-views/index.html
Android官方文档中关于自定义控件的教程,在大致了解自定义控件相关内容后,看官方的文档,收获更多,能发现许多其他人文章里并没有写的地方。所以有能力的都看看。
美工设计如下:
主要需要实现中间表盘和下方小表盘两个控件(背景色赞)
在开始前,先整理下自定义控件步骤,
首先需要想好要自定义的控件应该需要哪几个属性,比如对于大表盘,背景什么的美工都帮我设计好了,所以我只需要一个属性 speed,表示当前速度。对于小表盘的话,需要较多,我需要圆的半径,当前数值,数值最大值,数值名称,圆环颜色这几个属性。
想好了所需要的属性后,我们需要去定义这些属性,在res/values/attrs.xml里定义,定义好类名和属性名。
创建对应之前定义的类名,生成构造函数,声明所需要的几个属性与paint,并从xml里提取对应的属性值。
重写onMeasure方法
重写onDraw方法,完成控件所需的各种绘制
提供接口,使得可以在代码中更改属性,改变自定义控件状态。
对于自定义view来说,按以上步骤基本就完成了(viewgroup将onDraw换为OnLayout)。
接下来开始第一个控件:
MyWatch
1.首先自定义属性,如上所说我只需要一个speed属性,于是声明
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyWatch">
<attr name="speed" format="integer" />
</declare-styleable>
</resources>
2.创建对应类,构造函数如下
public MyWatch(Context context) {
this(context, null, 0);
}
public MyWatch(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyWatch(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
/**
* 获得我们所定义的自定义样式属性
*/
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MyWatch, defStyleAttr, 0);
speed = a.getInt(R.styleable.MyWatch_speed, 0);
paint = new Paint();
back = BitmapFactory.decodeResource(getResources(), R.drawable.back_watch);
center = BitmapFactory.decodeResource(getResources(), R.drawable.icon_watch_center);
arrow = BitmapFactory.decodeResource(getResources(), R.drawable.icon_watch_arrow);
a.recycle();
}
把paint的创建,和几个使用到的bitmap的构造都放到构造函数里,而不放在onDraw里,提高每次界面重绘的效率。
3.重写OnMeasure方法
我这没有做太多的计算,由于表盘使用的是一张图片(源码中已经改成了自己绘制),不是手动画的,所以控件大小基本就固定了。
这里具体做法可以看下一个控件。
在这里不做操作的后果是,如果使用wrap_content属性设置长宽,会和使用match_parent一样的效果。参见http://blog.csdn.net/lmj623565791/article/details/24252901/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else {
float needWidth = back.getWidth();
int desired = (int) (getPaddingLeft() + needWidth