Android自定义圆弧进度条,手动控制进度

一.展示

Android开发中由于需求的不同会遇到各种各样的进度条,本文实现一个自定义手动控制的进度条,先来看一下效果:

  1. 通过按钮控制进度条进度

2.通过滑动进度条上的按钮控制进度

二.实现

如上展示效果可见,圆弧所跨弧度为270,其中可将绘制分为6个部分:

  • 进度条的圆弧
  • 指针的图片
  • 圆弧上的控制按钮
  • 指针上显示进度详情的白色区域
  • 控制进度减的按钮
  • 控制进度加的按钮

1.代码实现中各个属性的定义以及其作用:

    /** 
     * 进度条所占用的角度 
     */  
    private static final int ARC_FULL_DEGREE = 270;  
    /** 
     * 弧线的宽度 
     */  
    private int STROKE_WIDTH;  
    /** 
     * 组件的宽,高 
     */  
    private int width, height,sWidth,sHeight;  
    
    /** 
     * 进度条最大值和当前进度值 
     */  
    private float max, progress;  
    /** 
     * 是否允许拖动进度条 
     */  
    private boolean draggingEnabled = false;  
    /** 
     * 绘制弧线的矩形区域 
     */  
    private RectF circleRectF,zhizhenRectF;  
    /** 
     * 绘制弧线的画笔 
     */  
    private Paint progressPaint;  
    /** 
     * 绘制文字的画笔 
     */  
    private Paint textPaint;  
    /** 
     * 绘制当前进度值的画笔 
     */  
    private Paint thumbPaint;  
    /** 
     * 圆弧的半径 
     */  
    private int circleRadius;  
    /** 
     * 圆弧圆心位置 
     */  
    private int centerX, centerY;
    private int upBtCenterX,upBtCenterY,downBtCenterx,downBtCenterY;//控制按钮的坐标
    private Bitmap zhizhen;
    private Matrix matrix;//矩阵--控制指针图片的动画
    /**
     * 指针圆心
     */
    private float circleRectFCenterWidth;
    private float circleRectFCenterHeight;
    /**
     * 圆弧上渐变色的颜色值
     */
    private final int[] colors = {Color.parseColor("#FFF68F"),Color.parseColor("#FFE700")
    		,Color.parseColor("#FFD700"),Color.parseColor("#FFC700")
    		,Color.parseColor("#FFB700"),Color.parseColor("#FFA700")
    		,Color.parseColor("#FF9700"),Color.parseColor("#FF7F00")};

2.圆弧,控制按钮,以及其他显示的绘制:

    protected void onDraw(Canvas canvas) {  
        super.onDraw(canvas);  
        Log.e("ondraw", "测试2");
  
        float start = 90 + ((360 - ARC_FULL_DEGREE) >> 1); //进度条起始点  
        float sweep1 = ARC_FULL_DEGREE * (progress / max); //进度划过的角度  
        float sweep2 = ARC_FULL_DEGREE - sweep1; //剩余的角度  
        float progressRadians = (float) (((360.0f - ARC_FULL_DEGREE) / 2 + sweep1) / 180 * Math.PI);
        float thumbX = centerX - circleRadius * (float) Math.sin(progressRadians);  
        float thumbY = centerY + circleRadius * (float) Math.cos(progressRadians);
  
        //绘制起始位置小圆形  
        progressPaint.setColor(Color.WHITE);  
        progressPaint.setStrokeWidth(0);  
        progressPaint.setStyle(Paint.Style.FILL);  
        float radians = (float) (((360.0f - ARC_FULL_DEGREE) / 2) / 180 * Math.PI);  
        float startX = centerX - circleRadius * (float) Math.sin(radians);  
        float startY = centerY + circleRadius * (float) Math.cos(radians);  
        System.out.println("startX=" + startX + ";startY="+ startY);
        canvas.drawCircle(startX, startY, STROKE_WIDTH / 2, progressPaint);  
        
        //绘制控制进度减按钮
        buttonRadius = circleRadius/8;
        downBtCenterx=(int) (startX+buttonRadius);
        downBtCenterY=(int) (startY+4*buttonRadius);
        progressPaint.setColor(Color.parseColor("#8800FFFF"));
        canvas.drawCircle(startX+buttonRadius, startY+4*buttonRadius, buttonRadius, progressPaint);
        progressPaint.setColor(Color.parseColor("#F0FFFF"));
        progressPaint.setStrokeWidth(5);
        canvas.drawLine(startX+buttonRadius-buttonRadius*3/4, startY+4*buttonRadius, startX+buttonRadius+buttonRadius*3/4, startY+4*buttonRadius, progressPaint);
        Log.e("onDraw", "测试-画圆");
        //绘制进度条 
        for(int i=0;i<colors.length;i++){
            position[i]=(float)(0.37+i*(progressRadians*100/360)/colors.length);
        }
        progressPaint.setStrokeWidth(STROKE_WIDTH);  
        progressPaint.setStyle(Paint.Style.STROKE);//设置空心
        LinearGradient linearGradient = new LinearGradient(startX, startY, thumbX, thumbY, colors, position, TileMode.CLAMP);
        progressPaint.setShader(linearGradient);
        canvas.drawArc(circleRectF, start, sweep1, false, progressPaint);  
        //绘制进度条背景  
        linearGradient=null;
        progressPaint.setShader(null);
        progressPaint.setColor(Color.parseColor("#d64444"));  
        canvas.drawArc(circleRectF, start + sweep1, sweep2, false, progressPaint);  
        Log.e("onDraw", "测试-画进度条");
  
        //绘制结束位置小圆形  
        progressPaint.setStrokeWidth(0);  
        progressPaint.setStyle(Paint.Style.FILL);  
        float endX = centerX + circleRadius * (float) Math.sin(radians);  
        float endY = centerY + circleRadius * (float) Math.cos(radians);  
        canvas.drawCircle(endX, endY, STROKE_WIDTH / 2, progressPaint);  
        Log.e("onDraw", "测试-画按钮");
        
      // 绘制进度加按钮
        upBtCenterX=(int) (endX-buttonRadius);
        upBtCenterY=(int) (endY+4*buttonRadius);
        progressPaint.setColor(Color.parseColor("#8800FFFF"));
        canvas.drawCircle(endX-buttonRadius, endY+4*buttonRadius, buttonRadius, progressPaint);
        progressPaint.setColor(Color.parseColor("#F0FFFF"));
        progressPaint.setStrokeWidth(5);
        canvas.drawLine(endX-buttonRadius-buttonRadius*3/4, endY+4*buttonRadius, endX-buttonRadius+buttonRadius*3/4, endY+4*buttonRadius, progressPaint);
        canvas.drawLine(endX-buttonRadius, endY+4*buttonRadius-buttonRadius*3/4, endX-buttonRadius, endY+4*buttonRadius+buttonRadius*3/4, progressPaint);
        progressPaint.setColor(Color.WHITE);
      //画出指针动画
        matrix.reset();
        matrix.postTranslate(circleRectFCenterWidth-width/2, circleRectFCenterHeight-height/2);
        matrix.preRotate(40,width/2,height/2);
        matrix.postRotate((float)(progressRadians*(180/Math.PI)),circleRectFCenterWidth, circleRectFCenterHeight);
        System.out.println("宽="+ zhizhen.getWidth() + " 高=" + zhizhen.getHeight());
        canvas.drawBitmap(zhizhen, matrix, progressPaint);
        canvas.drawCircle(circleRectFCenterWidth, circleRectFCenterHeight, (float) (0.36*width), progressPaint);
        
        //上一行文字  
        textPaint.setTextSize(circleRadius >> 1);  
        String text = (int) (100 * progress / max) + "";  
        float textLen = textPaint.measureText(text);  
        //计算文字高度  
        textPaint.getTextBounds("8", 0, 1, textBounds);  
        float h1 = textBounds.height();  
        //% 前面的数字水平居中,适当调整  
        float extra = text.startsWith("1") ? -textPaint.measureText("1") / 2 : 0;  
        canvas.drawText(text, centerX - textLen / 2 + extra, centerY - 30 + h1 / 2, textPaint);  
        
        //绘制进度条上的按钮
        thumbPaint.setColor(Color.parseColor("#33d64444"));  
        canvas.drawCircle(thumbX, thumbY, STROKE_WIDTH * 2.0f, thumbPaint);  
        thumbPaint.setColor(Color.parseColor("#99d64444"));  
        canvas.drawCircle(thumbX, thumbY, STROKE_WIDTH * 1.4f, thumbPaint);  
        thumbPaint.setColor(Color.WHITE);  
        canvas.drawCircle(thumbX, thumbY, STROKE_WIDTH * 0.8f, thumbPaint);  
        Log.e("onDraw", "测试-完成");
        System.out.println("进度值progress="+progress);
    }  

3.点击事件的实现:

@Override  
    public boolean onTouchEvent(@NonNull MotionEvent event) {  
        if (!draggingEnabled) {  
            return super.onTouchEvent(event);  
        }  
        Log.e("onTouchEvent", "测试3");
  
        //处理拖动事件  
        float currentX = event.getX();  
        float currentY = event.getY();  
  
        int action = event.getAction();  
        switch (action) {  
            case MotionEvent.ACTION_DOWN:  
                //判断是否在进度条thumb位置  
                if (checkOnArc(currentX, currentY)) {  
                    newProgress = calDegreeByPosition(currentX, currentY) / ARC_FULL_DEGREE * max;  
                    setProgressSync(newProgress);  
                    isDragging = true;  
                } else if(checkOnButtonUp(currentX, currentY)){
							// TODO Auto-generated method stub
					setProgress(progress+10);
                	isDragging = false;
                }else if(checkOnButtonDwon(currentX, currentY)){
                	setProgress(progress-10);
                	isDragging = false;
                }
                break;  
            case MotionEvent.ACTION_MOVE:  
                if (isDragging) {  
                    //判断拖动时是否移出去了  
                    if (checkOnArc(currentX, currentY)) {  
                        setProgressSync(calDegreeByPosition(currentX, currentY) / ARC_FULL_DEGREE * max);  
                    } else {  
                        isDragging = false;  
                    }  
                }  
                break;  
            case MotionEvent.ACTION_UP:  
                isDragging = false;  
                break;  
        }  
        return true;  
    }  

点击事件中,分别对ACTION_DOWN事件,ACTION_UP事件和ACTION_MOVE事件进行了处理,当控制进度条的时候,需要判断点击事件是否在控制区域,所以,此时需要设置点击响应区域,该区域在圆弧上,圆弧控制上的按钮和控制按钮可适当扩大,防止点击时候点击不准确从而导致不能触发点击事件。除此之外,还需要注意在这几个部分扩大点击区域时,防止其中多个点击区域相交,导致点击该区域时,触发两个部分的点击事件。此处列出圆弧上点击区域的控制实现:

 /** 
     * 判断该点是否在弧线上(附近) 
     */  
    private boolean checkOnArc(float currentX, float currentY) {  
        float distance = calDistance(currentX, currentY, centerX, centerY);  
        float degree = calDegreeByPosition(currentX, currentY);  
        return distance > circleRadius - STROKE_WIDTH * 5 && distance < circleRadius + STROKE_WIDTH * 5  
                && (degree >= -8 && degree <= ARC_FULL_DEGREE + 8);  
    }  

三.源代码在此处:

https://download.csdn.net/download/lanhao21/10308826






  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Android中可以通过自定义View来实现圆弧进度条。以下是实现步骤: 1. 创建一个自定义View类,并继承View。在构造方法中初始化画笔和属性。 2. 重写onMeasure方法,指定View的宽高。 3. 重写onDraw方法,通过Canvas绘制圆弧。 4. 添加一个方法用于设置进度条进度值,例如setProgress(int progress)。 5. 在onDraw方法中使用Path和Canvas绘制圆弧。 6. 在setProgress方法中计算当前进度对应的角度,然后调用invalidate方法强制刷新视图。 7. 使用属性动画或ObjectAnimator来实现进度条动画效果。 8. 在布局文件中添加自定义View并设置相关属性。 9. 在Java代码中调用setProgress方法来更新进度条的值。 以下是一个简单的示例代码: ``` public class CustomArcProgressBar extends View { private Paint paint; private RectF rectF; private int progress; public CustomArcProgressBar(Context context) { super(context); init(); } public CustomArcProgressBar(Context context, AttributeSet attrs) { super(context, attrs); init(); } public CustomArcProgressBar(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { paint = new Paint(); rectF = new RectF(); progress = 0; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(Math.min(width, height), Math.min(width, height)); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int centerX = getWidth() / 2; int centerY = getHeight() / 2; int radius = centerX - 10; paint.setColor(Color.GRAY); paint.setStrokeWidth(5); paint.setStyle(Paint.Style.STROKE); rectF.set(centerX - radius, centerY - radius, centerX + radius, centerY + radius); canvas.drawArc(rectF, 0, 360, false, paint); paint.setColor(Color.BLUE); canvas.drawArc(rectF, -90, (float) (progress * 3.6), false, paint); } public void setProgress(int progress) { if(progress >= 0 && progress <= 100) { this.progress = progress; invalidate(); } } } ``` 希望以上回答对您有帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值