Android——Canvas切割出扇形表盤式進度
一、知識點
(1)Matrix數學原理
(2)shader渲染
(3)PathEffect之DashPathEffect
原創者:It一zhai男
博客地址:http://www.cnblogs.com/ityizhainan/p/6306748.html
二、效果圖

(1)Matrix的數學原理[1]
在Android中,如果你用Matrix進行過圖像處理,那么一定知道Matrix這個類。Android中的Matrix是一個3 x 3的矩陣,其內容如下:

Matrix的對圖像的處理可分為四類基本變換:
Translate 平移變換
Rotate 旋轉變換
Scale 縮放變換
Skew 錯切變換
本例用到的是旋轉變換。
(2)shader渲染[2]
在Android中,提供了Shader類專門用來渲染圖像以及一些幾何圖形。
Shader類包括了5個直接子類,分別為:BitmapShader、ComposeShader、LinearGradient、RadialGradient以及SweepGradient。其中,BitmapShader用於圖像渲染;ComposeShader用於混合渲染;LinearGradient用於線性渲染;RadialGradient用於環形渲染;而SweepGradient則用於梯度渲染。
使用Shader類進行圖像渲染時,首先需要構建Shader對象,然后通過Paint的setShader()方法來設置渲染對象,最后將這個Paint對象繪制到屏幕上即可。
有一點需要注意,使用不同的方式渲染圖像時需要構建不同的對象。
SweepGradient掃描渲染
public SweepGradient(float cx,float cy,int[] colors,float[] position)
parameters:
float cx:渲染中心x坐標
float cy:渲染中心y坐標
int[] colors:圍繞中心渲染的顏色數組,顏色數組里至少要有兩種顏色
float[] position:在顏色數組里每種顏色的相對位置,取值范圍0到1.0
(3)PathEffect之DashPathEffect[3]
DashPathEffect主要用於畫虛線。構造函數,看注釋,intervals必須大於大於2,phase是偏移量
DashPathEffect(new float[]{2,4,6,8},1)
的第一個數組參數依次是畫個長度為2的實線,再畫個長度為4的空白再畫個長度為6的實線,再畫個長度為8的虛線,寬度是由mPaint.setStrokeWidth(mHeight/10)設置。
第二個參數指定了繪制的虛線相對了起始地址(Path起點)的取余偏移(對路徑總長度)。
new DashPathEffect(new float[] { 8, 10, 8, 10}, 0);
這時偏移為0,先繪制實線,再繪制透明。
new DashPathEffect(new float[] { 8, 10, 8, 10}, 8);
這時偏移為8,先繪制了透明,再繪制了實線.(實線被偏移過去了)
可是通過不斷地遞增/遞減來改變phase的值,達到一個路徑自身不斷循環移動的動畫效果。
三、主要代碼
package com.example.yds.circleprogressbar; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.DashPathEffect; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PathEffect; import android.graphics.RectF; import android.graphics.Shader; import android.graphics.SweepGradient; import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; /** * Created by yds on 2017/1/16. */ //@SuppressLint("DrawAllocation") public class SampleView extends View { private Paint mPaint; private float mRotate; private Matrix mMatrix; private Shader mShader; private int mWidth; private int mHeight; public SampleView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public SampleView(Context context, AttributeSet attrs) { super(context, attrs); } public SampleView(Context context) { super(context); } public void getArc(Canvas canvas, float o_x, float o_y, float r, float startangel, float endangel, Paint paint){ RectF rect = new RectF(o_x - r, o_y - r, o_x + r, o_y + r); Path path = new Path(); //將畫筆移動到指定點,與lineTo結合畫線。可以理解成畫線的起始點 path.moveTo(o_x,o_y); //最關鍵的是下面兩句,划線終點,改點是畫布切割線與圖形的交點處 path.lineTo((float)(o_x+r*Math.cos(startangel*Math.PI/180)) , (float)(o_y+r*Math.sin(startangel*Math.PI/180))); path.lineTo((float)(o_x+r*Math.cos(endangel*Math.PI/180)) , (float)(o_y+r*Math.sin(endangel*Math.PI/180))); //將要繪制的圖形添加小path中 path.addArc(rect, startangel, endangel-startangel); //將path切割出來,這里切割的是畫布,並不是圖形本身 canvas.clipPath(path); //畫圓 canvas.drawCircle(o_x, o_y, r, paint); } @Override protected void onDraw(Canvas canvas) { /** * Set whether this view can receive the focus. *設置這個視圖是否能夠得到焦點 * Setting this to false will also ensure that this view is not focusable * in touch mode. *設置為false即使在觸摸模式下也不能得到焦點 * @param focusable If true, this view can receive the focus. *設置為true,這個視圖可以獲得焦點 * @see #setFocusableInTouchMode(boolean) * @attr ref android.R.styleable#View_focusable * */ setFocusable(true); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mMatrix = new Matrix(); //設置獲取焦點是否在觸摸模式下 setFocusableInTouchMode(true); // float x = 320; // float y = 200; /** * A subclass of Shader that draws a sweep gradient around a center point. *圍繞一個中心點渲染繪制的Shader父類 * @param cx The x-coordinate of the center 中心點x坐標 * @param cy The y-coordinate of the center 中心點y坐標 * @param colors The colors to be distributed between around the center. * There must be at least 2 colors in the array. * 顏色分布在中心點周圍,在這個數組中必須至少有2種顏色 * @param positions May be NULL. The relative position of * each corresponding color in the colors array, beginning * with 0 and ending with 1.0. If the values are not * monotonic, the drawing may produce unexpected results. * If positions is NULL, then the colors are automatically * spaced evenly. * 可能為空。在顏色數組里的每種顏色的相對位置,取值范圍為0到1.0。 * 如果值不變,繪畫可能產生意想不到的結果。如果為空,那么顏色會自動均勻分布。 */ mShader = new SweepGradient(mWidth/2-1, mHeight/2-1, new int[] {0xFF09F68C, 0xFFB0F44B, 0xFFE8DD30, 0xFFF1CA2E, 0xFFFF902F, 0xFFFF6433}, null); mPaint.setShader(mShader); mPaint.setStyle(Paint.Style.STROKE); /**DashPathEffect(new float[]{2,4,6,8},1)的第一個數組參數依次是畫個長度為2的實線,再畫個長度為4的空白 * 再畫個長度為6的實線,再畫個長度為8的虛線,寬度是由mPaint.setStrokeWidth(mHeight/10)設置的 *第二個參數指定了繪制的虛線相對了起始地址(Path起點)的取余偏移(對路徑總長度)。 * *new DashPathEffect(new float[] { 8, 10, 8, 10}, 0); *這時偏移為0,先繪制實線,再繪制透明。 * *new DashPathEffect(new float[] { 8, 10, 8, 10}, 8); *這時偏移為8,先繪制了透明,再繪制了實線.(實線被偏移過去了) * *可是通過不斷地遞增/遞減來改變phase的值,達到一個路徑自身不斷循環移動的動畫效果。 */ PathEffect effect = new DashPathEffect(new float[] { 10, 10, 10,10}, 0); //給畫筆設置繪制路徑時的特效 mPaint.setPathEffect(effect); //設置線條寬度 mPaint.setStrokeWidth(mHeight/10); Paint paint = mPaint; //// float x = 320; //// float y = 200; // canvas.drawColor(Color.WHITE); //setRotate(mRotate,mx,my)旋轉,mRotate旋轉角度,mx,my為旋轉中心坐標。 mMatrix.setRotate(mRotate,mWidth/2-1, mHeight/2-1); mShader.setLocalMatrix(mMatrix); mRotate += 3; if (mRotate >= 405) { mRotate = 135; } invalidate(); // RectF rect = new RectF( mWidth/20, mWidth/20,mWidth-mWidth/20,mHeight-mWidth/20); // canvas.drawArc(rect,135,270,false,paint); getArc(canvas,mWidth/2-1, mHeight/2-1,mHeight/2-1,135,405,paint); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); if (widthSpecMode==MeasureSpec.EXACTLY||heightSpecMode == MeasureSpec.EXACTLY){ mWidth = widthSpecSize;//這里的值為實際的值的3倍 mHeight =heightSpecSize; }else{ float defaultSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,200,getContext().getResources().getDisplayMetrics()); mHeight = (int) defaultSize; mWidth = (int) defaultSize; } if(mWidth<mHeight){ mHeight = mWidth; }else{ mWidth = mHeight; } // Log.e("mHeight,mWidth",mHeight+","+mWidth); setMeasuredDimension(mWidth,mHeight); } }
代碼下載:
http://download.csdn.net/detail/u013293125/9741449
參考內容:
[1] http://www.cnblogs.com/qiengo/archive/2012/06/30/2570874.html#code
[2] http://www.cnblogs.com/menlsh/archive/2012/12/09/2810372.html
[3] http://blog.csdn.net/abcdef314159/article/details/51777353
