Android自定義圓弧進度條,手動控制進度
阿新 • • 發佈:2019-01-30
一.展示
Android開發中由於需求的不同會遇到各種各樣的進度條,本文實現一個自定義手動控制的進度條,先來看一下效果:
- 通過按鈕控制進度條進度
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);
}