自定义view之弧形进度条
2021-08-05 本文已影响0人
重新起步
车载需要做一个suv后门的开关演示器,用户可以通过在屏幕上触摸来控制车门的开关角度;所以我们需要做一个类似与扇形的进度条;
需要具备的基础知识如下:
画点:
画图片:
画线:canvas.drawLine(startX, startY, endX, endY, linePaint);
画扇形:https://blog.csdn.net/ls15256928597/article/details/70077825
填充色渐变:https://blog.csdn.net/harvic880925/
首先我们考虑最简单的场景:即车门最大只能打开90度;如下图:
p点是用户touch的区域;
q点是显示在进度刻度上的点,根据p点的变化来变化;
oq是需要展示的游标;
正方形是实际view的大小;
那么我们需要做的是,
view的绘制,大致思路如下
- 绘制蓝色扇形区域
- 绘制q点
- 绘制oq线段
步骤1.绘制蓝色扇形,
1.知道圆点,知道半经,先画一个正方形,正方形的中心就是圆点,正方形边长的一半,就是半径;
2.扇形是从,90度,画到180度的;
步骤2.绘制q点
1.需要知道q点对应的x,y
2.x和y可以根据半斤和角度a通过勾股定理计算出来
3.角度a根据用户点击p对应的x和y值,然后根据勾股定理计算出来
步骤3.绘制线段
步骤2中计算出q点的x和y,o点的x,y是已知的,连接起来即可;
所以ondraw方法,分为3步:
那么p点如何知道,那就要通过ontouch方法,事件的x,y对应的就是p点的x和y,代码如下:
然后通过勾股定理,计算出角度a;
最后调用 invalidate(),通知重绘,也就是每move一次,界面重绘一次;
package com.ford.sync.fordcalendar.ui.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.SweepGradient;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import com.ford.sync.fordcalendar.R;
/**
* author: lwang253 date: 2021/1/25 create CalendarApplication.
*/
public class ArcProgress extends View {
private int max = 90;// 支持的最大角度
private Paint arcPaint; // 弧形画笔
private Paint linePaint; // 直线画笔
private Paint pointPaint; // 点画笔
private int radio;
private int whsize; //
private double angle; // 当前的角度
private float touchX; // 触摸x
private float touchY; // 触摸y
private float pointX; // 刻度x
private float pointY; // 刻度y
public ArcProgress(Context context) {
super(context);
init();
}
public ArcProgress(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public ArcProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public ArcProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
arcPaint = new Paint();
arcPaint.setStyle(Paint.Style.FILL);
arcPaint.setStrokeWidth(1);
arcPaint.setTextSize(30);
linePaint = new Paint();
linePaint.setStyle(Paint.Style.FILL);
linePaint.setStrokeWidth(3);
linePaint.setColor(Color.BLACK);
pointPaint = new Paint();
pointPaint.setAntiAlias(true);
pointPaint.setTextSize(36);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// canvas.drawColor(Color.RED);// 实际可以去掉,用于观察view的实际大小
calculatePointByAngle(angle);
drawArc(canvas);
drawLine(canvas);
drawPoint(canvas);
}
/**
* 根据当前的角度,找到刻度q点的位置
*
* @param angle
*/
private void calculatePointByAngle(double angle) {
Log.d("ArcProgress", "sin30:" + Math.sin(30 * Math.PI / 180));
pointX = radio - (float) (radio * Math.sin(angle * Math.PI / 180));
pointY = radio + (float) (radio * Math.cos(angle * Math.PI / 180));
Log.d("ArcProgress", "calculatePointByAngle pointX:" + pointX + ";pointY:" + pointY);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
whsize = w > h ? h : w;
radio = whsize / 2;
super.onSizeChanged(w, h, oldw, oldh);
}
private void drawPoint(Canvas canvas) {
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.radio_sel);
Rect mSrcRect = new Rect(0, 0, bmp.getWidth(), bmp.getHeight());
int left = (int) (pointX - bmp.getWidth()/2);
int top = (int) (pointY - bmp.getHeight()/2);
Rect mDestRect = new Rect(left, top, left+bmp.getWidth(), top+bmp.getHeight());
canvas.drawBitmap(bmp, mSrcRect, mDestRect, pointPaint);
}
private void drawLine(Canvas canvas) {
canvas.drawLine(radio, radio, pointX, pointY, linePaint);
}
private void drawArc(Canvas canvas) {
// 矩形区域,这种方式画出来的扇形占view的1/4
RectF oval = new RectF(0, 0, whsize, whsize);
// 渐变的填充色
Shader gradient = new RadialGradient(radio, radio,
radio, Color.TRANSPARENT, Color.BLUE, Shader.TileMode.REPEAT);
arcPaint.setShader(gradient);
canvas.drawArc(oval, 90, 90, true, arcPaint);
}
public float Dp2Px(Context context, float dp) {
final float scale = context.getResources().getDisplayMetrics().density;
return (dp * scale + 0.5f);
}
/**
* 根据触摸点,计算角度
*
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
touchX = event.getX();
touchY = event.getY();
Log.d("ArcProgress", "x:" + touchX + "; y:" + touchY);
if (touchX > radio || touchY < radio) {
// 在另外三个区域不处理touch事件;
return true;
}
calculateAngle();
Log.d("ArcProgress", "angle:" + angle);
invalidate();// 计算出角度后,刷新界面
break;
}
return true;
}
/**
* 三角函数计算角度a Math.atan计算的是弧度
*/
private void calculateAngle() {
angle = Math.atan((radio - touchX) / (touchY - radio)) * 180 / Math.PI;
if (angle > 90) {
angle = 90;
}
if (angle < 0) {
angle = 0;
}
}
};