1. 程式人生 > 程式設計 >Android自定義控制元件實現圓形進度CircleProgressBar

Android自定義控制元件實現圓形進度CircleProgressBar

近日有朋友問我有沒有如下圖效果的開源控制元件

Android自定義控制元件實現圓形進度CircleProgressBar

相信大家無論是用IOS還是Android,都對這種效果不陌生,很多主流APP都會有這樣或類似的效果,之前也打算研究一下這類控制元件的程式碼,苦於一直不知道應該怎麼搜尋這種效果(就是關鍵詞)或者所搜的結果不是自己想要的,所以就一直擱置了下來。

正好朋友需要這種效果,所以就忙裡偷閒寫了一個類似的、更加常見和適用範圍更多的控制元件,效果如下圖所示:

Android自定義控制元件實現圓形進度CircleProgressBar

自定義上圖所示效果的控制元件時,其實就是用Canvas繪製不同效果,比如漸變圓弧背景、圓周白色分割線、中間文字等,這篇部落格也根據繪製的順序依次闡述。

1.自定義CircleProgressBar,繼承View,並實現響應的建構函式

程式碼如下:

/**
 * Created by WangChunLei on 2016.1.16
 * E-mail:[email protected]
 */
public class GradientProgressBar extends View {
 public GradientProgressBar(Context context) {
 super(context);
 init();
 }

 public GradientProgressBar(Context context,AttributeSet attrs) {
 super(context,attrs);
 init();
 }

 public GradientProgressBar(Context context,AttributeSet attrs,int defStyleAttr) {
 super(context,attrs,defStyleAttr);
 init();
 }
}

其中init方法是對相關畫筆進行初始化的方法,init方法程式碼如下:

private void init() {
 backCirclePaint = new Paint();
 backCirclePaint.setStyle(Paint.Style.STROKE);
 backCirclePaint.setAntiAlias(true);
 backCirclePaint.setColor(Color.LTGRAY);
 backCirclePaint.setStrokeWidth(circleBorderWidth);
// backCirclePaint.setMaskFilter(new BlurMaskFilter(20,BlurMaskFilter.Blur.OUTER));

 gradientCirclePaint = new Paint();
 gradientCirclePaint.setStyle(Paint.Style.STROKE);
 gradientCirclePaint.setAntiAlias(true);
 gradientCirclePaint.setColor(Color.LTGRAY);
 gradientCirclePaint.setStrokeWidth(circleBorderWidth);

 linePaint = new Paint();
 linePaint.setColor(Color.WHITE);
 linePaint.setStrokeWidth(5);

 textPaint = new Paint();
 textPaint.setAntiAlias(true);
 textPaint.setTextSize(textSize);
 textPaint.setColor(Color.BLACK);
 }

2.測量控制元件的寬高-onMeasure

onMeasure是自定義控制元件的第一步,目的就是測量得到該控制元件應該佔有的寬高尺寸。其中onMeasure方法的程式碼如下:

@Override
 protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec) {
 int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
 int measureHeight = MeasureSpec.getSize(heightMeasureSpec);
 setMeasuredDimension(Math.min(measureWidth,measureHeight),Math.min(measureWidth,measureHeight));
 }

貼上onMeasure的程式碼後,大家估計是很少見過測量過程這麼簡單的onMeasure,不要介意,有興趣的同僚們可以細化一下這個測量過程,對不同的測量模式分別進行處理和測量,讓控制元件適配效果更好更完善!

onMeasure方法中,分別獲取期望的寬度和高度,並取其中較小的尺寸作為該控制元件的寬和高。

3.依次繪製不同的控制元件組成部分。

因為控制元件是直接繼承自View,所以不需要再處理onLayout方法,這也是自定義View的難度遠小於自定義ViewGroup的原因,但繼承ViewGroup也並不一定要重寫onMeasure。
要實現如圖所示的效果,需要分以下步驟依次實現

(1)繪製灰色空心圓環
(2)繪製顏色漸變的圓環
(3)繪製圓環上分割的白色線條
(4)繪製百分比文字等。

繪製過程過,後繪製的內容如果與之前繪製的內容存在交集,則後繪製的內容會覆蓋掉之前繪製的內容。

按照上述步驟依次介紹

在繪製過程中,會產生以下成員變數,下文中會用到:

/*圓弧線寬*/
 private float circleBorderWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,20,getResources().getDisplayMetrics());
 /*內邊距*/
 private float circlePadding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,getResources().getDisplayMetrics());
 /*字型大小*/
 private float textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,50,getResources().getDisplayMetrics());
 /*繪製圓周的畫筆*/
 private Paint backCirclePaint;
 /*繪製圓周白色分割線的畫筆*/
 private Paint linePaint;
 /*繪製文字的畫筆*/
 private Paint textPaint;
 /*百分比*/
 private int percent = 0;
 /*漸變圓周顏色陣列*/
 private int[] gradientColorArray = new int[]{Color.GREEN,Color.parseColor("#fe751a"),Color.parseColor("#13be23"),Color.GREEN};
 private Paint gradientCirclePaint;

3.1繪製灰色空心圓環

程式碼如下:

//1.繪製灰色背景圓環
 canvas.drawArc(
  new RectF(circlePadding * 2,circlePadding * 2,getMeasuredWidth() - circlePadding * 2,getMeasuredHeight() - circlePadding * 2),-90,360,false,backCirclePaint);

其中,-90為繪製圓弧的起始角度,360是圓弧繪製的角度,即sweepAngle.

3.2繪製顏色漸變的圓環

//2.繪製顏色漸變圓環
 LinearGradient linearGradient = new LinearGradient(circlePadding,circlePadding,getMeasuredWidth() - circlePadding,getMeasuredHeight() - circlePadding,gradientColorArray,null,Shader.TileMode.MIRROR);
 gradientCirclePaint.setShader(linearGradient);
 gradientCirclePaint.setShadowLayer(10,10,Color.RED);
 canvas.drawArc(
  new RectF(circlePadding * 2,(float) (percent / 100.0) * 360,gradientCirclePaint);

其中,linearGradient是Paint的shadow,是為了圓弧的顏色漸變效果的而需要設定的,日常開發中應用頻率不高,但的確是可以實現非常理想的顏色漸變效果。

3.3繪製圓環上分割的白色線條

繪製圓弧上的白色線條時,需要進行一些簡單的運算,比如線條的起始座標startX,startY和線條的終止座標stopX,stopY等,利用簡單的三角函式還是很容易去計算出來的。
效果中,將圓弧使用白色線條平分成100分,每一個的階級為1,可以滿足int型別的百分比與效果圖比例的一致。

//半徑
float radius = (getMeasuredWidth() - circlePadding * 3) / 2;
 //X軸中點座標
 int centerX = getMeasuredWidth() / 2;

 //3.繪製100份線段,切分空心圓弧
 for (float i = 0; i < 360; i += 3.6) {
  double rad = i * Math.PI / 180;
  float startX = (float) (centerX + (radius - circleBorderWidth) * Math.sin(rad));
  float startY = (float) (centerX + (radius - circleBorderWidth) * Math.cos(rad));

  float stopX = (float) (centerX + radius * Math.sin(rad) + 1);
  float stopY = (float) (centerX + radius * Math.cos(rad) + 1);

  canvas.drawLine(startX,startY,stopX,stopY,linePaint);
 }

3.4繪製百分比文字等

最後繪製百分比文字。
繪製文字時,為了保持文字的中心點和圓弧的原點一致,需要先測量得到要顯示文字的寬度和高度,然後再進行一些簡單的運算,原理不再贅述,相信大家數學一定都比我好。

//4.繪製文字
float textWidth = textPaint.measureText(percent + "%");
int textHeight = (int) (Math.ceil(textPaint.getFontMetrics().descent - textPaint.getFontMetrics().ascent) + 2);
 canvas.drawText(percent + "%",centerX - textWidth / 2,centerX + textHeight / 4,textPaint);

最後,暴漏一個公共的方法供改變顯示的百分比,程式碼如下:

/**
 * 設定百分比
 *
 * @param percent
 */
 public void setPercent(int percent) {
 if (percent < 0) {
  percent = 0;
 } else if (percent > 100) {
  percent = 100;
 }

 this.percent = percent;
 invalidate();
 }

至此,所有繪製過程簡述完畢,130行程式碼就能實現很炫酷的效果有木有?

最後,貼上專案完整程式碼,供懶得看實現過程的同僚們使用,O(∩_∩)O哈哈~

package com.example.myview;

import android.content.Context;
import android.graphics.*;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

/**
 * Created by WangChunLei on 2016.1.16
 * e-mail:[email protected]
 */
public class GradientProgressBar extends View {
 /*圓弧線寬*/
 private float circleBorderWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,Color.GREEN};
 private Paint gradientCirclePaint;

 public GradientProgressBar(Context context) {
 super(context);
 init();
 }

 public GradientProgressBar(Context context,defStyleAttr);
 init();
 }

 private void init() {
 backCirclePaint = new Paint();
 backCirclePaint.setStyle(Paint.Style.STROKE);
 backCirclePaint.setAntiAlias(true);
 backCirclePaint.setColor(Color.LTGRAY);
 backCirclePaint.setStrokeWidth(circleBorderWidth);
// backCirclePaint.setMaskFilter(new BlurMaskFilter(20,BlurMaskFilter.Blur.OUTER));

 gradientCirclePaint = new Paint();
 gradientCirclePaint.setStyle(Paint.Style.STROKE);
 gradientCirclePaint.setAntiAlias(true);
 gradientCirclePaint.setColor(Color.LTGRAY);
 gradientCirclePaint.setStrokeWidth(circleBorderWidth);

 linePaint = new Paint();
 linePaint.setColor(Color.WHITE);
 linePaint.setStrokeWidth(5);

 textPaint = new Paint();
 textPaint.setAntiAlias(true);
 textPaint.setTextSize(textSize);
 textPaint.setColor(Color.BLACK);
 }


 @Override
 protected void onMeasure(int widthMeasureSpec,measureHeight));
 }

 @Override
 protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 //1.繪製灰色背景圓環
 canvas.drawArc(
  new RectF(circlePadding * 2,backCirclePaint);
 //2.繪製顏色漸變圓環
 LinearGradient linearGradient = new LinearGradient(circlePadding,gradientCirclePaint);

 //半徑
 float radius = (getMeasuredWidth() - circlePadding * 3) / 2;
 //X軸中點座標
 int centerX = getMeasuredWidth() / 2;

 //3.繪製100份線段,切分空心圓弧
 for (float i = 0; i < 360; i += 3.6) {
  double rad = i * Math.PI / 180;
  float startX = (float) (centerX + (radius - circleBorderWidth) * Math.sin(rad));
  float startY = (float) (centerX + (radius - circleBorderWidth) * Math.cos(rad));

  float stopX = (float) (centerX + radius * Math.sin(rad) + 1);
  float stopY = (float) (centerX + radius * Math.cos(rad) + 1);

  canvas.drawLine(startX,linePaint);
 }

 //4.繪製文字
 float textWidth = textPaint.measureText(percent + "%");
 int textHeight = (int) (Math.ceil(textPaint.getFontMetrics().descent - textPaint.getFontMetrics().ascent) + 2);
 canvas.drawText(percent + "%",textPaint);
 }

 /**
 * 設定百分比
 *
 * @param percent
 */
 public void setPercent(int percent) {
 if (percent < 0) {
  percent = 0;
 } else if (percent > 100) {
  percent = 100;
 }

 this.percent = percent;
 invalidate();
 }
}

最後,貼上自定義控制元件程式碼(自定義控制元件、Activity,佈局檔案)下載地址: Android圓形進度CircleProgressBar

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。