往常慣例,先上圖寡夹, 不會的童鞋真的只需要十分鐘就可以學(xué)會
效果比較簡單,就是繪制6段圓弧厂置、然后指針的旋轉(zhuǎn)菩掏、弧線型的下標(biāo)文字,中心的數(shù)字隨進(jìn)度條變化昵济。
這里我直接貼 onDraw 里面的方法了智绸。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawInstrument(canvas);//進(jìn)度底色
drawText(canvas);//進(jìn)度條下標(biāo)文字
drawPointer(canvas);//指針
drawSecond(canvas);//第二層
drawCalorie(canvas);//繪制中間的卡路里
}
接下來就一個一個方法看吧:
1、drawInstrument():
/*
* 繪制6段圓弧
* */
private void drawInstrument(Canvas canvas) {
//70度一個弧形访忿,中間隔5度
canvas.drawArc(mBigRectF, offset[0], 70, false, mPaintBig);//繪制圓弧瞧栗,不含圓心
canvas.drawArc(mBigRectF, offset[1], 70, false, mPaintBig);//繪制圓弧,不含圓心
canvas.drawArc(mBigRectF, offset[2], 70, false, mPaintBig);//繪制圓弧海铆,不含圓心
canvas.drawArc(mSmallRectF, offset[0], 70, false, mPaintSmall);//繪制圓弧迹恐,不含圓心
canvas.drawArc(mSmallRectF, offset[1], 70, false, mPaintSmall);//繪制圓弧,不含圓心
canvas.drawArc(mSmallRectF, offset[2], 70, false, mPaintSmall);//繪制圓弧卧斟,不含圓心
}
就是 canvas 類里面一個基本方法 canvas.drawArc()繪制圓弧殴边,第一個參數(shù)是矩陣(控制圓弧的區(qū)域),第二個參數(shù)是開始時候得角度(UI給的)珍语,第三個參數(shù)70是圓弧的度數(shù)锤岸,第四個參數(shù)是是否繪制圓心,第五個參數(shù)是畫筆廊酣。
其中畫筆是在構(gòu)造方法里面初始化能耻,代碼我就不在這貼了赏枚。矩陣是在 onMeasure 里面初始化亡驰,代碼如下:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = MeasureSpec.getSize(widthMeasureSpec);
mHeight = MeasureSpec.getSize(heightMeasureSpec);
int i = (mWidth - mHeight) / 2;
mBigRectF = new RectF(10 + i, 10, mWidth - 10 - i, mHeight - 10);//外圓弧
mTextRectF = new RectF(40 + i, 40, mWidth - 40 - i, mHeight - 40);//文字弧線
mSmallRectF = new RectF(70 + i, 70, mWidth - 70 - i, mHeight - 70);//內(nèi)圓弧
mBigRectF.offset(0, 80);
mSmallRectF.offset(0, 80);
mTextRectF.offset(0, 80);
mMatrix = new Matrix();
mMatrix.postTranslate(mWidth / 2 - mGreenBitmap.getWidth() / 2, mHeight / 4);
}
在這里 初始化了三個 RectF晓猛,用戶繪制弧相關(guān),Matrix是用與指針旋轉(zhuǎn)的凡辱,后面我會說明戒职。
噢,對了透乾,這里有個疑問洪燥,在 onMeasure 方法里面初始化的這三個變量,在 studio 里面報黃色警告乳乌,說避免在 draw/layout 期間初始化變量捧韵,但是要獲取到控件的寬高之后再初始化,onDraw里面肯定更加不合適汉操。有木有大牛知道的請告知再来,onSizeChanged?
2磷瘤、drawText():
/**
* 繪制圓弧下標(biāo) 文字
*/
private void drawText(Canvas canvas) {
mTextPaint.setTextSize(28);
Path path = new Path();
path.addArc(mTextRectF, offset[0], 20);
canvas.drawTextOnPath("0", path, 10f, 10f, mTextPaint);
path.reset();
path.addArc(mTextRectF, offset[1] - 15, 20);
canvas.drawTextOnPath(“1000”, path, 10f, 10f, mTextPaint);
path.reset();
path.addArc(mTextRectF, offset[2] - 15, 20);
canvas.drawTextOnPath(“2000”, path, 10f, 10f, mTextPaint);
path.reset();
path.addArc(mTextRectF, offset[3] - 20, 20);
canvas.drawTextOnPath(“3000”, path, 10f, 10f, mTextPaint);
}
由于這里的下標(biāo)文字是弧線走勢芒篷,所以這里用了 canvas 的另一個方法drawTextOnPath(),也是基本方法采缚,我就不多贅述了针炉。
3、drawPointer():
/*
* 繪制指針
* */
private void drawPointer(Canvas canvas) {
mMatrix.reset();
mMatrix.postTranslate(mWidth / 2 - mGreenBitmap.getWidth() / 2, mHeight / 4);
if (progress > 666) {
//紅色
mMatrix.postRotate(38, mWidth / 2f, mHeight / 2f + 80);
mMatrix.postRotate(70 * (progress - 666) / 333f, mWidth / 2f, mHeight / 2f + 80);
canvas.drawBitmap(mRedBitmap, mMatrix, null);
} else if (progress > 333) {
//黃色
mMatrix.postRotate(-35, mWidth / 2f, mHeight / 2f + 80);
mMatrix.postRotate(70 * (progress - 333) / 333f, mWidth / 2f, mHeight / 2f + 80);
canvas.drawBitmap(mYellowBitmap, mMatrix, null);
} else {
//綠色
mMatrix.postRotate(-108, mWidth / 2f, mHeight / 2f + 80);
mMatrix.postRotate(70 * progress / 333f, mWidth / 2f, mHeight / 2f + 80);
canvas.drawBitmap(mGreenBitmap, mMatrix, null);
}
}
progress就是用來控制進(jìn)
這里我們使用了三個不同顏色的 icon 作為指針扳抽,在不同的進(jìn)度顯示不同的顏色篡帕。然后由于指針不是純色的,所以不能用 drawable.setTint()方法來控制指針的顏色贸呢,要不然用一個指針就行了赂苗。Matrix控制 bitmap的位置的偏移旋轉(zhuǎn),記得使用Matrix的時候要用Matrix.postXXX方法贮尉, 使用 setXXX 系列的方法會覆蓋上次的設(shè)置拌滋。
Matrix:The Matrix class holds a 3x3 matrix for transforming coordinates. 這里給Matrix做個簡單的說明,就是用來控制矩陣變化的猜谚,感興趣的小伙伴請自行 google败砂。
4、drawSecond():
/*
*
*動態(tài)進(jìn)度條
**/
private void drawSecond(Canvas canvas) {
//不能修改 mPaint 畫筆的顏色復(fù)用 mPaint魏铅,初步估計 canvas.drawArc 方法為異步
if (progress > 666) {
//紅色
canvas.drawArc(mBigRectF, offset[2], 70 * (progress - 666) * 3 / 1000, false, mPaintRed);
} else if (progress > 333) {
//黃色
canvas.drawArc(mBigRectF, offset[1], 70 * (progress - 333) * 3 / 1000, false, mPaintYellow);
} else {
//綠色
canvas.drawArc(mBigRectF, offset[0], 70 * progress * 3 / 1000, false, mPaintGreen);
}
}
這里踩了一個坑昌犹,一開始我的想法是所有的圓弧都用同一支畫筆 mPaint,因為畫筆的參數(shù)都是一樣的,就只有顏色不同览芳,在需要繪制不同顏色的時候調(diào)用 mPaint.setColor()方法即可斜姥。比如說我先用 mPaint 畫筆畫了一段灰色的圓弧,然后調(diào)用 mPaint.setColor(Color.BLUE)方法將畫筆設(shè)置為藍(lán)色,然后再繪制一段藍(lán)色弧線铸敏,但是最終的結(jié)果是兩段圓弧的顏色都是藍(lán)色的缚忧。由于 canvas.drawArc()方法是native方法,所以我沒有找到原因杈笔,初步估計 canvas.drawArc()方法是異步原因引起的闪水,有木有大牛知道原因~~~
5、drawCalorie():
/*
*
* 圓弧中間文字
* */
private void drawCalorie(Canvas canvas) {
mTextPaint.setTextSize(56);
String num = String.valueOf((int) (mFullEat * progress / 1000f));
int numberWidth = (int) mTextPaint.measureText(num);
mTextPaint.setTextSize(28);
int textWidth = (int) mTextPaint.measureText("千卡");
mTextPaint.setTextSize(56);
canvas.drawText(num, (mWidth - numberWidth - textWidth) / 2, mHeight / 2 + 80 - 20, mTextPaint);
mTextPaint.setTextSize(28);
canvas.drawText("千卡", (mWidth - numberWidth - textWidth) / 2 + numberWidth + 5, mHeight / 2 + 80 - 20, mTextPaint);
String state = progress > 333 ? (progress > 666 ? "吃多了" : "正合適") : "還需吃";
mTextPaint.setTextSize(32);
canvas.drawText(state, (mWidth - mTextPaint.measureText(state)) / 2, mHeight / 2 + 80 + 30, mTextPaint);
}
這里沒什么好說的蒙具,代碼簡潔明了球榆。就是計算了幾個寬高,讓文字居中而已禁筏。
好持钉,控件畫完了。
接下來篱昔,只需要添加一個方法右钾,修改progress的值,然后調(diào)用invalidate()重新走 ondraw()方法即可旱爆。
private void setProgress(int progress) {
this.progress = progress;
invalidate();
}
嗯~然后還要動起來舀射,所以我把這個方法的權(quán)限設(shè)置為 pravite。
public void setData(int hasEat, int fullEat, boolean needAnimator) {
progress = hasEat * 1000 / fullEat;
mFullEat = fullEat;
if (progress > 1000)
progress = 1000;
if (progress < 0)
progress = 0;
if (needAnimator) {
ObjectAnimator objectAnimator = ObjectAnimator.ofInt(this, "progress", progress);
objectAnimator.setDuration(2000);
objectAnimator.setInterpolator(new DecelerateInterpolator());
objectAnimator.start();
} else {
setProgress(progress);
}
}
就醬紫怀伦,很簡單的代碼邏輯脆烟。哦,對了房待,ObjectAnimator動畫用到了反射邢羔,代碼混淆的時候記得keep 這個類不被混淆哦。
~~Over
對了桑孩,代碼比較簡單拜鹤,我就不傳 github 的,直接貼代碼吧流椒,兩百行不到
/**
* Author: Diamond_Lin
* Version V1.0
* Date: 2017/6/26 上午10:06
* Description:
* Modification History:
* Date Author Version Description
* -----------------------------------------------------------------------------------
* 2017/6/26 Diamond_Lin 1.0 1.0
* Why & What is modified:
*/
public class InstrumentView extends View {
private Paint mPaintBig, mPaintSmall, mPaintYellow, mPaintGreen, mPaintRed;
private int mWidth;
private int mHeight;
private RectF mBigRectF, mSmallRectF, mTextRectF;
private TextPaint mTextPaint;
private int[] offset = {-198, -125, -52, 18};//分別是第一段起點敏簿,第二段起點,第三段起點宣虾,第三段終點
private Bitmap mGreenBitmap;
private Bitmap mYellowBitmap;
private Bitmap mRedBitmap;
private int progress = 0;
private int mFullEat;
private Matrix mMatrix;
public InstrumentView(Context context) {
this(context, null);
}
public InstrumentView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public InstrumentView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mPaintBig = new Paint();//外環(huán)畫筆
mPaintBig.setAntiAlias(true);//使用抗鋸齒功能
mPaintBig.setColor(Color.argb(128, 0, 0, 0)); //設(shè)置畫筆的顏色
mPaintBig.setStyle(Paint.Style.STROKE);//設(shè)置畫筆類型為描邊
mPaintBig.setStrokeWidth(16);
mPaintYellow = new Paint();//黃色
mPaintYellow.setAntiAlias(true);//使用抗鋸齒功能
mPaintYellow.setColor(Color.rgb(0xf7, 0xb5, 0x00)); //設(shè)置畫筆的顏色
mPaintYellow.setStyle(Paint.Style.STROKE);//設(shè)置畫筆類型為描邊
mPaintYellow.setStrokeWidth(16);
mPaintGreen = new Paint();//綠色
mPaintGreen.setAntiAlias(true);//使用抗鋸齒功能
mPaintGreen.setColor(Color.rgb(0x5c, 0xd1, 0xb4)); //設(shè)置畫筆的顏色
mPaintGreen.setStyle(Paint.Style.STROKE);//設(shè)置畫筆類型為描邊
mPaintGreen.setStrokeWidth(16);
mPaintRed = new Paint();//紅色
mPaintRed.setAntiAlias(true);//使用抗鋸齒功能
mPaintRed.setColor(Color.rgb(0xff, 0x64, 0x6f)); //設(shè)置畫筆的顏色
mPaintRed.setStyle(Paint.Style.STROKE);//設(shè)置畫筆類型為描邊
mPaintRed.setStrokeWidth(16);
mPaintSmall = new Paint();//內(nèi)環(huán)畫筆
mPaintSmall.setAntiAlias(true);//使用抗鋸齒功能
mPaintSmall.setColor(Color.argb(128, 0, 0, 0)); //設(shè)置畫筆的顏色
mPaintSmall.setStyle(Paint.Style.STROKE);//設(shè)置畫筆類型為描邊
mPaintSmall.setStrokeWidth(8);
mTextPaint = new TextPaint();
mTextPaint.setAntiAlias(true);
mTextPaint.setColor(Color.WHITE);
mTextPaint.setTextSize(28);
mGreenBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_pointer_green);//綠色指針
mYellowBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_pointer_yellow);//黃色指針
mRedBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_pointer_red);//紅色指針
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = MeasureSpec.getSize(widthMeasureSpec);
mHeight = MeasureSpec.getSize(heightMeasureSpec);
int i = (mWidth - mHeight) / 2;
mBigRectF = new RectF(10 + i, 10, mWidth - 10 - i, mHeight - 10);
mTextRectF = new RectF(40 + i, 40, mWidth - 40 - i, mHeight - 40);
mSmallRectF = new RectF(70 + i, 70, mWidth - 70 - i, mHeight - 70);
mBigRectF.offset(0, 80);
mSmallRectF.offset(0, 80);
mTextRectF.offset(0, 80);
mMatrix = new Matrix();
mMatrix.postTranslate(mWidth / 2 - mGreenBitmap.getWidth() / 2, mHeight / 4);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.e("__-progress", progress + "");
drawInstrument(canvas);//進(jìn)度底色
drawText(canvas);//文字
drawPointer(canvas);//指針
drawSecond(canvas);//第二層
drawCalorie(canvas);//繪制中間的卡路里
}
/*
*
* 圓弧中間文字
* */
private void drawCalorie(Canvas canvas) {
mTextPaint.setTextSize(56);
String num = String.valueOf((int) (mFullEat * progress / 1000f));
int numberWidth = (int) mTextPaint.measureText(num);
mTextPaint.setTextSize(28);
int textWidth = (int) mTextPaint.measureText("千卡");
mTextPaint.setTextSize(56);
canvas.drawText(num, (mWidth - numberWidth - textWidth) / 2, mHeight / 2 + 80 - 20, mTextPaint);
mTextPaint.setTextSize(28);
canvas.drawText("千卡", (mWidth - numberWidth - textWidth) / 2 + numberWidth + 5, mHeight / 2 + 80 - 20, mTextPaint);
String state = progress > 333 ? (progress > 666 ? "吃多了" : "正合適") : "還需吃";
mTextPaint.setTextSize(32);
canvas.drawText(state, (mWidth - mTextPaint.measureText(state)) / 2, mHeight / 2 + 80 + 30, mTextPaint);
}
/*
*
*動態(tài)進(jìn)度條
**/
private void drawSecond(Canvas canvas) {
//不能修改 mPaint 畫筆的顏色復(fù)用 mPaint惯裕,初步估計 canvas.drawArc 方法為異步
if (progress > 666) {
//紅色
canvas.drawArc(mBigRectF, offset[2], 70 * (progress - 666) * 3 / 1000, false, mPaintRed);
} else if (progress > 333) {
//黃色
canvas.drawArc(mBigRectF, offset[1], 70 * (progress - 333) * 3 / 1000, false, mPaintYellow);
} else {
//綠色
canvas.drawArc(mBigRectF, offset[0], 70 * progress * 3 / 1000, false, mPaintGreen);
}
}
/*
* 繪制指針
* */
private void drawPointer(Canvas canvas) {
mMatrix.reset();
mMatrix.postTranslate(mWidth / 2 - mGreenBitmap.getWidth() / 2, mHeight / 4);
if (progress > 666) {
//紅色
mMatrix.postRotate(38, mWidth / 2f, mHeight / 2f + 80);
mMatrix.postRotate(70 * (progress - 666) / 333f, mWidth / 2f, mHeight / 2f + 80);
canvas.drawBitmap(mRedBitmap, mMatrix, null);
} else if (progress > 333) {
//黃色
mMatrix.postRotate(-35, mWidth / 2f, mHeight / 2f + 80);
mMatrix.postRotate(70 * (progress - 333) / 333f, mWidth / 2f, mHeight / 2f + 80);
canvas.drawBitmap(mYellowBitmap, mMatrix, null);
} else {
//綠色
mMatrix.postRotate(-108, mWidth / 2f, mHeight / 2f + 80);
mMatrix.postRotate(70 * progress / 333f, mWidth / 2f, mHeight / 2f + 80);
canvas.drawBitmap(mGreenBitmap, mMatrix, null);
}
}
/**
* 繪制圓弧標(biāo)記 文字
*/
private void drawText(Canvas canvas) {
mTextPaint.setTextSize(28);
Path path = new Path();
path.addArc(mTextRectF, offset[0], 20);
canvas.drawTextOnPath("0", path, 10f, 10f, mTextPaint);
path.reset();
path.addArc(mTextRectF, offset[1] - 15, 20);
canvas.drawTextOnPath(String.valueOf(mFullEat / 3), path, 10f, 10f, mTextPaint);
path.reset();
path.addArc(mTextRectF, offset[2] - 15, 20);
canvas.drawTextOnPath(String.valueOf(mFullEat / 3 * 2), path, 10f, 10f, mTextPaint);
path.reset();
path.addArc(mTextRectF, offset[3] - 20, 20);
canvas.drawTextOnPath(String.valueOf(mFullEat), path, 10f, 10f, mTextPaint);
}
/*
*
* 繪制6段圓弧
* */
private void drawInstrument(Canvas canvas) {
//70度一個弧形,中間隔5度
canvas.drawArc(mBigRectF, offset[0], 70, false, mPaintBig);//繪制圓弧绣硝,不含圓心
canvas.drawArc(mBigRectF, offset[1], 70, false, mPaintBig);//繪制圓弧蜻势,不含圓心
canvas.drawArc(mBigRectF, offset[2], 70, false, mPaintBig);//繪制圓弧,不含圓心
canvas.drawArc(mSmallRectF, offset[0], 70, false, mPaintSmall);//繪制圓弧鹉胖,不含圓心
canvas.drawArc(mSmallRectF, offset[1], 70, false, mPaintSmall);//繪制圓弧握玛,不含圓心
canvas.drawArc(mSmallRectF, offset[2], 70, false, mPaintSmall);//繪制圓弧够傍,不含圓心
}
public void setData(int hasEat, int fullEat, boolean needAnimator) {
progress = hasEat * 1000 / fullEat;
mFullEat = fullEat;
if (progress > 1000)
progress = 1000;
if (progress < 0)
progress = 0;
if (needAnimator) {
ObjectAnimator objectAnimator = ObjectAnimator.ofInt(this, "progress", progress);
objectAnimator.setDuration(2000);
objectAnimator.setInterpolator(new DecelerateInterpolator());
objectAnimator.start();
} else {
setProgress(progress);
}
}
private void setProgress(int progress) {
this.progress = progress;
invalidate();
}
}