【Android 车载 App】实现座椅调节控制十字指针的效果
效果展示
实现方法
思路
- 第一步,画两条十字虚线
- 第二步,在每个虚线的末端画出圆角三角形
- 第三步,在十字虚线的中间位置画出两个同心圆,一个填充内容,一个描边
- 第四步,复写onTouchEvent方法,实现同心圆的移动逻辑
代码
第一步,画两条十字虚线
初始化画笔
PathEffect pathEffect = new DashPathEffect(new float[]{10f,10f}, 1);
linePaint.setStyle(Paint.Style.STROKE);
linePaint.setStrokeWidth(5f);
linePaint.setColor(Color.GRAY);
linePaint.setPathEffect(pathEffect);
在onDraw方法中使用画笔,画出两条十字虚线
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawLine(30,getHeight()/2, getWidth()-30,getHeight()/2, linePaint);
canvas.drawLine(getWidth()/2, 30, getWidth()/2, getHeight()-30, linePaint);
}
第二步,在每个虚线的末端画出圆角三角形
画三角形需要用到Path
的相关知识(这部分内容请同学们自行百度),声明及Path
的初始化如下:
Path upTrianglePath = new Path();
Path downTrianglePath = new Path();
Path leftTrianglePath = new Path();
Path rightTrianglePath = new Path();
private Path getTrianglePath(int direction) {
initTrianglePath();
switch (direction) {
case 0:// 上
return upTrianglePath;
case 1: // 下
return downTrianglePath;
case 2: // 左
return leftTrianglePath;
case 3: // 右
return rightTrianglePath;
default:
break;
}
return upTrianglePath;
}
private void initTrianglePath() {
upTrianglePath.moveTo(getWidth()/2, 0);
upTrianglePath.lineTo(getWidth()/2 - 30, 52);
upTrianglePath.lineTo(getWidth()/2 + 30, 52);
upTrianglePath.close();
downTrianglePath.moveTo(getWidth()/2, getHeight());
downTrianglePath.lineTo(getWidth()/2 - 30, getHeight() - 52);
downTrianglePath.lineTo(getWidth()/2 + 30, getHeight() - 52);
downTrianglePath.close();
leftTrianglePath.moveTo(0, getHeight()/2);
leftTrianglePath.lineTo(52, getHeight()/2 + 30);
leftTrianglePath.lineTo(52, getHeight()/2 - 30);
leftTrianglePath.close();
rightTrianglePath.moveTo(getWidth(), getHeight()/2);
rightTrianglePath.lineTo(getWidth() - 52, getHeight()/2 + 30);
rightTrianglePath.lineTo(getWidth() - 52, getHeight()/2 - 30);
rightTrianglePath.close();
}
除了使用 Path
之外,因为我们需要画出圆角三角形,所以还需要对 Paint
对象的 PathEffect
做一下设置:
private void initPaint() {
//...
trianglePaint.setStyle(Paint.Style.FILL);
trianglePaint.setColor(Color.GRAY);
float radius = 10f;
CornerPathEffect corEffect = new CornerPathEffect(radius);
trianglePaint.setPathEffect(corEffect);
//...
}
然后在 onDraw
方法中画出四个顶点位置的三角形:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//....
canvas.drawPath(getTrianglePath(0), trianglePaint);
canvas.drawPath(getTrianglePath(1), trianglePaint);
canvas.drawPath(getTrianglePath(2), trianglePaint);
canvas.drawPath(getTrianglePath(3), trianglePaint);
//....
}
第三步,在十字虚线的中间位置画出两个同心圆,一个填充内容,一个描边
画圆只需要使用canvas.drawCircle
方法即可,但是由于两个圆一个需要填充内容,一个需要描边,所以我们使用两个Paint
对象来分别实现对应的效果。
声明Paint
对象:
Paint circlePaint1 = new Paint();
Paint circlePaint2 = new Paint();
private void initPaint() {
// ...
circlePaint1.setStyle(Paint.Style.FILL);// 填充内容
circlePaint1.setColor(Color.GRAY);
circlePaint2.setStyle(Paint.Style.STROKE); // 描边
circlePaint2.setStrokeWidth(14);
circlePaint2.setColor(Color.GRAY);
}
然后在 onDraw
方法中画出中心的两个同心圆:
@Override
protected void onDraw(Canvas canvas) {
// ...
canvas.drawCircle(getCircleX(), getCircleY(), 20, circlePaint1);
canvas.drawCircle(getCircleX(), getCircleY(), 48, circlePaint2);
}
第四步,复写onTouchEvent方法,实现同心圆的移动逻辑
复写onTouchEvent
方法前,需要先设置clickable
为true
,否则我们的View
只能接收到 ACTION_DOWN
事件,无法接收到其他事件。
public SeatAdaptionView(Context context, @Nullable AttributeSet attrs) {
//...
setClickable(true);
}
然后复写 onTouchEvent
方法,实现同心圆的移动逻辑,实现的思路是:
ACTION_DOWN
时记录初始触摸点- 在
ACTION_MOVE
时计算X轴与Y轴的偏移值deltaX
与deltaY
,比较二者绝对值大小,根据比较结果设定滑动方向。deltaX
大,则延X轴滑动,deltaY
大则延Y轴滑动。 - 根据
deltaX
(或deltaY
)的值,改变圆心位置,并执行重绘。 - 在
ACTION_UP
时,恢复到初始状态。
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch(action) {
case MotionEvent.ACTION_DOWN:
// 1.记录初始触摸点
touchX = event.getX();
touchY = event.getY();
lastX = event.getX();
lastY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
// 2.计算X轴与Y轴的偏移值 `deltaX` 与 `deltaY`
touchX = event.getX();
touchY = event.getY();
float deltaX = lastX - touchX;
float deltaY = lastY - touchY;
if (moveDirection == -1) {
// 比较二者绝对值大小,根据比较结果设定滑动方向。`deltaX`大,则延X轴滑动,`deltaY`大则延Y轴滑动。
if (Math.abs(deltaX) >= Math.abs(deltaY)) {
moveDirection = 0;
} else {
moveDirection = 1;
}
} else if (moveDirection == 0) {
// 水平方向移动
mDeltaX = deltaX;
// 3.根据 `deltaX`(或`deltaY`)的值,改变圆心位置,并执行重绘。
invalidate();
} else {
// 垂直方向移动
mDeltaY = deltaY;
// 根据 `deltaX`(或`deltaY`)的值,改变圆心位置,并执行重绘。
invalidate();
}
break;
case MotionEvent.ACTION_UP:
// 4.恢复到初始状态。
touchX = -1;
touchY = -1;
moveDirection = -1;
mDeltaX = 0;
mDeltaY = 0;
invalidate();
break;
default:
break;
}
Log.d("SeatAdaptionView", "onTouchEvent: Action = " + event.getAction() + " touchX = " + touchX + " touchY = " + touchY);
return super.onTouchEvent(event);
}