前段时间看到朋友发出一张效果图,发现android并没有这种类似的控件,于是便有了这么一个demo,供初学自定义控件的童鞋参考,废话不多说直接开干!
首先,分析下控件构成,我们需要画一个类似标尺的图案,然后在上面画一个圆:
新建工程-创建类TestView继承自View
public class TestView extends View {
private int width, height;//自定义View的宽高
private int verticalCenter, horizontalCenter; //圆的位置(x,y)
private int circleRadius; //圆的半径
private int circleColor; //圆的颜色
private float startX, startY, stopX, stopY;//标尺开始(x,y)结束(x,y)
private int padding;//标尺据两边的边距
private int copies; //等份数量
既然我们需要画,那么我们接下来就需要去实现onDraw()方法:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();//新建画笔
paint.setAntiAlias(false);//设置是否抗锯齿
paint.setColor(Color.BLACK);//设置颜色
//画出一条横线,我们把它画在居中位置
startX = padding;
startY = height / 2;
stopX = width - padding;
stopY = height / 2;
canvas.drawLine(startX, startY, stopX, stopY, paint); //画线,参数一起始点的x轴位置,参数二起始点的y轴位置,参数三终点的x轴水平位置,参数四y轴垂直位置,最后一个参数为Paint 画刷对象。
//循环画出竖线 根据我们指定的等份数量copies
for (int i = 0; i <= copies; i++) {
startX = padding + i * ((width - 2 * padding) / copies);
startY = height / 2 - 70;//70是竖线的高度 这个可以提出来单独弄个属性
stopX = padding + i * ((width - 2 * padding) / copies);
stopY = height / 2;
canvas.drawLine(startX, startY, stopX, stopY, paint);
}
//根据属性画圆
paint.setColor(circleColor);
canvas.drawCircle(horizontalCenter, verticalCenter, circleRadius, paint);
}
这样我们的图形基本上就算完成了,那么我们的参数从何而来呢,那么接下来我们就去定义参数,在res-values-新建attrs.xml文件
加入你需要的参数名和数据类型
<attr name="circleRadius" format="dimension" />
<attr name="padding" format="dimension"/>
<attr name="copies" format="integer" />
<attr name="circleColor" format="color" />
<declare-styleable name="TestView">
<attr name="circleRadius" />
<attr name="padding" />
<attr name="copies" />
<attr name="circleColor"/>
</declare-styleable>
参数定义了,那么我们需要如何去取这些参数呢?
public TestView(Context context, AttributeSet attrs) {
super(context, attrs);
verticalCenter = height / 2;
horizontalCenter = padding;
/**
* 获得我们所定义的自定义样式属性
*/
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TestView);
int n = a.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = a.getIndex(i);
switch (attr) {
case R.styleable.TestView_padding:
padding = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 30, getResources().getDisplayMetrics()));
break;
case R.styleable.TestView_circleColor:
// 默认颜色设置为黑色
circleColor = a.getColor(attr, Color.BLACK);
break;
case R.styleable.TestView_circleRadius:
circleRadius = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 30, getResources().getDisplayMetrics()));
break;
case R.styleable.TestView_copies:
copies = a.getInteger(attr, 5);
break;
}
}
a.recycle();
}
应该不用解释上面代码的意思了吧,相信你看得懂。
那么目前为止我们已经把图案 参数都搞定了,那么如何去实现事件监听呢,接下来我们就去看看如何实现的。
我们需要在TestView中重写onTouchEvent来实现触摸监听:
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE://手指移动
int y = (int) event.getY();
int x = (int) event.getX();
//我们判断手指触摸的位置是否在圆上(这里我的触摸范围是这个圆的外接矩形)
if ((this.horizontalCenter - circleRadius) < x && x < (this.horizontalCenter + circleRadius)) {
if ((this.verticalCenter - circleRadius) < y && y < (this.verticalCenter + circleRadius)) {
this.horizontalCenter = (int) event.getX();//如果是 我们就移动圆的圆心位置
invalidate();//重绘
} else {
return true;//如果不是 消费事件不做处理
}
} else {
return true;//如果不是 消费事件不做处理
}
break;
case MotionEvent.ACTION_UP:
//抬起手指的时候,我们需要判断离那个“刻度”更近 圆移动到最近的那个刻度上
int item = (this.horizontalCenter - padding) / ((width - 2 * padding) / copies);
int remainder = (this.horizontalCenter - padding) % ((width - 2 * padding) / copies);
if (remainder >= ((width - 2 * padding) / copies / 2)) {
this.horizontalCenter = (width - 2 * padding) / copies * (item + 1) + padding;
} else {
this.horizontalCenter = (width - 2 * padding) / copies * item + padding;
}
invalidate();
break;
}
return true;
}
在自定义控件中我们也经常重写onMeasure
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = getWidth();
height = getHeight();
}
用来获得宽高。
最后我们在布局文件中使用我们定义的控件即可:
<RelativeLayout
android:id="@+id/activity_main"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/back"
tools:context="com.example.administrator.testview.MainActivity">
<com.example.administrator.testview.TestView
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="@color/cc"
app:circleColor="@color/color"
app:circleRadius="10dp"
app:copies="4"
app:padding="20dp"/>
</RelativeLayout>