Android 自定義View實現圓形進度條 深入理解onDraw和onMeasure及自定義屬性
阿新 • • 發佈:2019-02-04
Android的View類是使用者介面的基礎構件,表示螢幕上的一塊矩形區域,負責這個區域的繪製和事件處理。自定義View的過程主要包括重寫onDraw及onMeasure方法 , 其中onMeasure方法的作用就是計算出自定義View的寬度和高度。這個計算的過程會參照父佈局給出的大小(widthMeasureSpec和heightMeasureSpec),以及自己特點算出結果 ;onDraw則根據onMeasure測量後的寬高進行介面的繪製。onDraw主要用到兩個類,Canvas和Paint。Canvas畫布,相當於現實中畫圖用的紙或布;Paint畫筆,相當於現實中的筆,基本方法有 drawLine 繪製直線 ,drawRect繪製矩形 , drawCirlce繪製圓形。
自定義圓形進度條效果如下
完整程式碼:
package com.circleprogress;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by Administrator on 2017/6/22.
*/
public class CirlceView extends View {
//定義第一支畫筆,畫背景的圓
private Paint mPaintBackCircle;
//定義第二支畫筆,畫前景色的圓(進度條的環)
private Paint mPaintFrontCircle;
//定義繪製文字的畫筆
private Paint mPaintText;
//定義環的寬度
private float mStrokeWidth = 50 ;
private float mhalfStrokeWidth = mStrokeWidth/2 ;
//定義圓心座標和半徑
private float mX = 200 + mhalfStrokeWidth ;
private float mY = 200 + mhalfStrokeWidth ;
private float mRadius = 200 ;
//定義矩形
private RectF mRectF ;
//開始進度
private int mProgress = 0 ;
//目標進度
private int mTargetProgress = 70 ;
//最大進度
private int mMax = 100 ;
//定義view的寬高
private int mWidth ;
private int mHeight ;
//預設半徑
private static final int DEFAULT_RADIUS = 200 ;
private static final int DEFAULT_STROKE_WIDTH = 50 ;
public CirlceView(Context context) {
this(context,null);
}
public CirlceView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CirlceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//獲取自定義屬性
if( attrs != null ){
TypedArray array = context.obtainStyledAttributes(attrs , R.styleable.CirlceView);
mRadius = array.getDimensionPixelSize(R.styleable.CirlceView_radius , DEFAULT_RADIUS);
mStrokeWidth = array.getDimensionPixelSize(R.styleable.CirlceView_stroke_width,DEFAULT_STROKE_WIDTH);
array.recycle();
}
init();
}
private void initRect(){
if(mRectF == null ){
mRectF = new RectF();
int viewSize = (int) (mRadius*2);
int left = (mWidth - viewSize)/2 ;
int top = (mHeight - viewSize)/2 ;
int right = left + viewSize ;
int bottom = top + viewSize ;
mRectF.set(left,top,right,bottom);
}
}
//初始化畫筆
private void init(){
mPaintBackCircle = new Paint();
//設定顏色
mPaintBackCircle.setColor(Color.WHITE);
//設定防鋸齒
mPaintBackCircle.setAntiAlias(true);
//設定為空心
mPaintBackCircle.setStyle(Paint.Style.STROKE);
mPaintBackCircle.setStrokeWidth(mStrokeWidth);
mPaintFrontCircle = new Paint();
mPaintFrontCircle.setColor(0xFF66C796);
mPaintFrontCircle.setAntiAlias(true);
mPaintFrontCircle.setStyle(Paint.Style.STROKE);
mPaintFrontCircle.setStrokeWidth(mStrokeWidth);
mPaintText = new Paint();
mPaintText.setColor(0xFF66C796);
mPaintText.setAntiAlias(true);
mPaintText.setTextSize(50);
//設定文字居中
mPaintText.setTextAlign(Paint.Align.CENTER);
}
@Override
protected void onDraw(Canvas canvas) {
initRect();
//獲取進度百分比
float angle = mProgress/(float)mMax * 360 ;
canvas.drawCircle(mWidth/2 , mHeight/2 , mRadius , mPaintBackCircle );
canvas.drawArc(mRectF , -90 , angle , false ,mPaintFrontCircle);
canvas.drawText(mProgress+"%", mWidth/2 , mHeight/2 , mPaintText);
if( mProgress < mTargetProgress) {
//更新進度
mProgress += 2;
//通知重新繪製
invalidate();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//測量寬高
mWidth = getRealSize(widthMeasureSpec);
mHeight = getRealSize(heightMeasureSpec);
//儲存測量的寬高
setMeasuredDimension(mWidth,mHeight);
}
//測量View的真實尺寸
public int getRealSize( int measureSpec ){
int result = -1 ;
int mode = MeasureSpec.getMode(measureSpec);
int size = MeasureSpec.getSize(measureSpec);
if( mode == MeasureSpec.AT_MOST || mode == MeasureSpec.UNSPECIFIED){
//自己計算
result = (int) (mRadius * 2 + mStrokeWidth);
}else {
result = size ;
}
return result;
}
}
在values目錄下新建attrs.xml檔案 , 增加自定義屬性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CirlceView">
<attr name="radius" format="dimension"></attr>
<attr name="stroke_width" format="dimension"></attr>
<attr name="back_circle_color" format="color"></attr>
<attr name="front_arc_color" format="color"></attr>
<attr name="text_visibility" format="boolean"></attr>
</declare-styleable>
</resources>
最後在佈局檔案中引入,設定自定義寬高和半徑
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.circleprogress.MainActivity">
<com.circleprogress.CirlceView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:radius="100dp"
app:stroke_width="20dp"/>
</RelativeLayout>