1. 程式人生 > >Android自定義圓弧進度條,手動控制進度

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);  
    }  

三.原始碼在此處: