效果图:
实现思路:
1.定义一个画外弧的画笔,使用画笔画出圆弧UI
2.定义一个画内圆(灰色)的画笔,使用画笔画出内圆UI
3.定义一个画圆点的画笔,使用画笔画出圆点UI
4.定义一个画文本的画笔,使用画笔画出文本的值
5.监听触摸事件,通过触摸事件计算和判断是否在圆弧上。
实现CustomVolumeProgressView自定义View,如下:
import android.content.Context;import android.graphics.Canvas;import android.graphics.Paint;import android.graphics.RectF;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.View;import androidx.annotation.NonNull;import androidx.annotation.Nullable;public class CustomVolumeProgressView extends View { /** * 进度条所占用的角度 */ private static final int ARC_FULL_DEGREE = 270; /** * 绘制进度条画笔 */ private Paint progressBarPaint; /** * 绘制内心园画笔 */ private Paint innerCirclePaint; /** * 绘制小圆的画笔 */ private Paint smallCirclePaint; /** * 绘制文字的画笔 */ private Paint textPaint; /** * 绘制文字大小 */ private int textSize = 60; /** * 圆弧进度条圆心位置 */ private int centerX, centerY; /** * 小圆的半径 */ private int smallCircleRadius ; /** * 小圆的宽度 */ private int smallCircleWidth = 7; /** * 圆弧进度条绘制区域 */ private RectF progressBarRectF ; /** * 圆弧进度值(0-100) */ private int mProgress; /** * 圆弧进度最大值 */ private int mProgressMax = 100; /** * 圆弧进度条的宽度 */ private int progressBarWidth = 8; /** * 内心园的宽度 */ private int innerCircleWidth = 6; /** * 小圆圆心坐标 */ float smallX ,smallY; /** * 进度条后端的坐标 */ float progressX ,progressY; /** * 进度条半径 */ float progressRadius; public CustomVolumeProgressView(Context context) { super(context); init(); } public CustomVolumeProgressView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } public CustomVolumeProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init(){ // 初始化圆弧进度画笔 progressBarPaint = new Paint(); progressBarPaint.setStyle(Paint.Style.STROKE); // 只描边,不填充 progressBarPaint.setStrokeCap(Paint.Cap.ROUND); // 设置圆角 progressBarPaint.setAntiAlias(true); // 设置抗锯齿 progressBarPaint.setDither(true); // 设置抖动 progressBarPaint.setStrokeWidth(progressBarWidth); progressBarPaint.setColor(getResources().getColor(R.color.progress)); //初始化跟随进度条小圆的画笔 smallCirclePaint = new Paint(); smallCirclePaint.setStyle(Paint.Style.FILL); // 填充 smallCirclePaint.setStrokeCap(Paint.Cap.ROUND); // 设置圆角 smallCirclePaint.setAntiAlias(true); // 设置抗锯齿 smallCirclePaint.setDither(true); // 设置抖动 smallCirclePaint.setStrokeWidth(smallCircleRadius); smallCirclePaint.setColor(getResources().getColor(R.color.progress)); // 初始化内心圆画笔 innerCirclePaint = new Paint(); innerCirclePaint.setStyle(Paint.Style.STROKE); // 只描边,不填充 innerCirclePaint.setStrokeCap(Paint.Cap.ROUND); // 设置圆角 innerCirclePaint.setAntiAlias(true); // 设置抗锯齿 innerCirclePaint.setDither(true); // 设置抖动 innerCirclePaint.setStrokeWidth(innerCircleWidth); innerCirclePaint.setColor(getResources().getColor(R.color.innerCircle)); //初始化音量值画笔 textPaint = new Paint(); textPaint.setAntiAlias(true); textPaint.setColor(getResources().getColor(R.color.white)); textPaint.setFakeBoldText(true); textPaint.setTextSize(textSize); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //获取圆弧进度的区域 int viewWide = getMeasuredWidth() - getPaddingLeft() - getPaddingRight(); int viewHigh = getMeasuredHeight() - getPaddingTop() - getPaddingBottom(); int mRectLength = (int) ((viewWide > viewHigh ? viewHigh : viewWide) - progressBarWidth); int mRectL = getPaddingLeft() + (viewWide - mRectLength) / 2; int mRectT = getPaddingTop() + (viewHigh - mRectLength) / 2; progressBarRectF = new RectF(mRectL, mRectT, mRectL + mRectLength, mRectT + mRectLength ); //获取圆弧中心坐标 centerX = getMeasuredWidth() / 2; centerY = getMeasuredHeight() / 2; //计算小圆到圆弧进度圆心的半径(距离) smallCircleRadius = Math.min(getMeasuredWidth(), getMeasuredHeight()) / 2; progressRadius = smallCircleRadius; smallCircleRadius -= 30;//适当调整 } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //绘制进度条 canvas.drawArc(progressBarRectF, 90 + ((360 - ARC_FULL_DEGREE) >> 1), ARC_FULL_DEGREE * mProgress / 100, false, progressBarPaint); //绘制跟随进度条的小圆 float swipe = ARC_FULL_DEGREE * mProgress / mProgressMax; float radians = (float) ((90 + ((360 - ARC_FULL_DEGREE) >> 1) + swipe) / 180 * Math.PI); smallX = centerX + smallCircleRadius * (float) Math.cos(radians); smallY = centerY + smallCircleRadius * (float) Math.sin(radians); progressX = centerX + progressRadius * (float) Math.cos(radians); progressY = centerY + progressRadius * (float) Math.sin(radians); canvas.drawCircle(smallX, smallY, smallCircleWidth, smallCirclePaint); //绘制内心圆 canvas.drawCircle(centerX, centerY, smallCircleRadius + 14, innerCirclePaint); //绘制文字 float textWidth = textPaint.measureText(mProgress + ""); int textHeight = (int) (Math.ceil(textPaint.getFontMetrics().descent - textPaint.getFontMetrics().ascent) + 2); canvas.drawText(mProgress + "", centerX - textWidth / 2, centerX + textHeight / 4, textPaint); } private boolean isDragging = false; @Override public boolean onTouchEvent(@NonNull MotionEvent event) { //处理拖动事件 float currentX = event.getX(); float currentY = event.getY(); int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: //判断是否在进度条thumb位置 if (checkOnArc(currentX, currentY)) { int newProgress = (int) (calDegreeByPosition(currentX, currentY) / ARC_FULL_DEGREE * mProgressMax); setProgressSync(newProgress); isDragging = true; } break; case MotionEvent.ACTION_MOVE: if (isDragging) { //判断拖动时是否移出去了 if (checkOnArc(currentX, currentY)) { setProgressSync((int)(calDegreeByPosition(currentX, currentY) / ARC_FULL_DEGREE * mProgressMax)); } else { isDragging = false; } } break; case MotionEvent.ACTION_UP: isDragging = false; break; } return true; } /** * 判断该点是否在弧线上(附近) */ private boolean checkOnArc(float currentX, float currentY) { float distance = calDistance(currentX, currentY, centerX, centerY); float degree = calDegreeByPosition(currentX, currentY); return distance > progressRadius - progressBarWidth * 4 && distance < progressRadius + progressBarWidth * 4 && (degree >= -30 && degree <= ARC_FULL_DEGREE + 30); } private float calDistance(float x1, float y1, float x2, float y2) { return (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); } /** * 根据当前位置,计算出进度条已经转过的角度。 */ private float calDegreeByPosition(float currentX, float currentY) { float a1 = (float) (Math.atan(1.0f * (centerX - currentX) / (currentY - centerY)) / Math.PI * 180); if (currentY < centerY) { a1 += 180; } else if (currentY > centerY && currentX > centerX) { a1 += 360; } return a1 - (360 - ARC_FULL_DEGREE) / 2; } public void setProgressSync(int progress) { mProgress = checkProgress(progress); volumeProgressInterface.updatVolumeProgress(progress); invalidate(); } private VolumeProgressInterface volumeProgressInterface; public void setVolumeProgressInterface(VolumeProgressInterface volumeProgressInterface){ this.volumeProgressInterface = volumeProgressInterface; } //保证progress的值位于[0,max] private int checkProgress(int progress) { if (progress < 0) { return 0; } return progress > mProgressMax ? mProgressMax : progress; } public interface VolumeProgressInterface{ //更新音量调节进度 void updatVolumeProgress(int index); }}
Layout布局如下:
<?xml version="1.0" encoding="utf-8"?>
MainActivity的主要操作如下:
CustomVolumeProgressView customVolumeProgressView = findViewById(R.id.volume_progress); customVolumeProgressView.setProgressSync(66);
页面更新:2024-05-11
本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828
© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号