1. 程式人生 > >Android 自定義View實現圓形進度條 深入理解onDraw和onMeasure及自定義屬性

Android 自定義View實現圓形進度條 深入理解onDraw和onMeasure及自定義屬性

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>