馬上要離開現單位了,蛋蛋的憂傷。。。以此紀念吧!
需求:
分析:
1、三條不同顏色的圓弧
2、最外層圓弧顏色根據進度不同漸變
3、外層圓弧內部邊緣文字描述
4、中心部分文字:健康指數
5、中間層圓弧進度顯示
解析:
1、畫三條圓弧角度爲120°~300°
/**
* 準備工作。。
*
* @param canvas
*/
private void drawAllArc(Canvas canvas) {
float flagWidth = 0.0f;
flagWidth = (mWidth - strokeOuterWidth);
initPaint(outerColor, strokeOuterWidth, strokeOuterWidth,
strokeOuterWidth, flagWidth, flagWidth);
mColors = new int[] {// 漸變色數組
0xFFFF0000, 0xFFFF00FF, 0xFF0000FF, 0xFF00FFFF, 0xFF00FF00, 0xFFFFFF00,
0xFFFF0000 };
// 漸變着色器
Shader s = new SweepGradient(0, 0, mColors, null);
mPaint.setShader(s);
// 外層 第一段
canvas.drawArc(oval, startAngle, sweetAngle / 3, false, mPaint);
mPaint.setColor(getContext().getResources().getColor(
R.color.outerColor2));
// 外層 第二段
canvas.drawArc(oval, startAngle + sweetAngle / 3, sweetAngle / 3,
false, mPaint);
mPaint.setColor(getContext().getResources().getColor(
R.color.outerColor3));
// 外層 第三段
canvas.drawArc(oval, startAngle + sweetAngle / 3 * 2, sweetAngle / 3,
false, mPaint);
flagWidth = flagWidth - strokeMidWidth;
initPaint(innerColor,
strokeMidWidth, mOffset + strokeMidWidth + strokeOuterWidth,
mOffset + strokeMidWidth + strokeOuterWidth,
flagWidth - mOffset,
flagWidth - mOffset);
// 畫中間層
canvas.drawArc(oval, startAngle, sweetAngle, false, mPaint);
flagWidth = flagWidth - strokeOuterWidth;
initPaint(innerColor, strokeInnerWidth, mOffset*2 + strokeInnerWidth + strokeMidWidth + strokeOuterWidth,
mOffset*2 + strokeInnerWidth + strokeMidWidth + strokeOuterWidth,
flagWidth - mOffset*2,
flagWidth - mOffset*2);
// 畫內層
canvas.drawArc(oval, startAngle, sweetAngle, false, mPaint);
}
其中initPaint()方法是對畫筆的一些初始化操作,如下:
/**
* 初始化/重置 畫筆
*
* @param outerColor
* @param strokeOuterWidth
* @param left
* @param top
* @param right
* @param bottom
*/
private void initPaint(int outerColor, float strokeOuterWidth, float left,
float top, float right, float bottom) {
mPaint.reset();
mPaint.setColor(outerColor);
mPaint.setStrokeWidth(strokeOuterWidth);
mPaint.setAntiAlias(true); // 消除鋸齒
mPaint.setStyle(Paint.Style.STROKE); // 設置空心
oval = new RectF(left, top, right, bottom);
}
oval 是它所在的矩形,由此確定位置。
2、顏色漸變
mColors = new int[] {// 漸變色數組
0xFFFF0000, 0xFFFF00FF, 0xFF0000FF, 0xFF00FFFF, 0xFF00FF00, 0xFFFFFF00,
0xFFFF0000 };
// 漸變着色器
Shader s = new SweepGradient(0, 0, mColors, null);
mPaint.setShader(s);
主要用到 SweepGradient 爲 mPaint 設置着色器,第三個參數爲顏色數組,以此來確定漸變顏色值。(該方法具體使用請參考官方Api)
3、外層圓弧內部邊緣文字描述
/**
* 主要用到api path.addArc 和 canvas.drawTextOnPath
* 具體參數可以上網查查,這個應該可以計算出一套算法的,我這裏是一點點試出來的,(好尷尬,不會算法)
*
* @param canvas
*/
private void drawShowTextOnPath(Canvas canvas) {
RectF ovalF = new RectF(strokeOuterWidth, strokeOuterWidth, mWidth
- strokeOuterWidth, mWidth - strokeOuterWidth);
float vOffset = 45;
mTextPath.reset();
mTextPath.addArc(ovalF, startAngle, 5);
canvas.drawTextOnPath("0", mTextPath, 0, vOffset, textPaint);
// 每次要 reset
mTextPath.reset();
mTextPath.addArc(ovalF, startAngle, 150 - 75);
canvas.drawTextOnPath("差", mTextPath, 0, vOffset, textPaint);
mTextPath.reset();
mTextPath.addArc(ovalF, startAngle, 150);
canvas.drawTextOnPath("25", mTextPath, 0, vOffset, textPaint);
mTextPath.reset();
mTextPath.addArc(ovalF, startAngle, 150 + 75);
canvas.drawTextOnPath("中", mTextPath, 0, vOffset, textPaint);
mTextPath.reset();
mTextPath.addArc(ovalF, startAngle, 300);
canvas.drawTextOnPath("50", mTextPath, 0, vOffset, textPaint);
mTextPath.reset();
mTextPath.addArc(ovalF, sweetAngle, 25);
canvas.drawTextOnPath("良", mTextPath, 0, vOffset, textPaint);
mTextPath.reset();
mTextPath.addArc(ovalF, sweetAngle, 100);
canvas.drawTextOnPath("75", mTextPath, 0, vOffset, textPaint);
mTextPath.reset();
mTextPath.addArc(ovalF, sweetAngle, 165);
canvas.drawTextOnPath("優", mTextPath, 0, vOffset, textPaint);
mTextPath.reset();
mTextPath.addArc(ovalF, sweetAngle , 225);
canvas.drawTextOnPath("100", mTextPath, 0, vOffset, textPaint);
}
主要用到api path.addArc 和 canvas.drawTextOnPath,具體使用方法參考官方Api
4、中心部分文字:健康指數
/**
* 畫文字--健康指數質量
*
* @param canvas
*/
private void setMidText(Canvas canvas) {
mTextStrPaint.setTextSize(22);
mTextStrPaint.getTextBounds(strText, 0, strText.length(), boundsText);
Log.e("VIEW_LOG_TAG", boundsText.right + "," + boundsText.bottom + ","
+ boundsText.top + "," + boundsText.left);
canvas.drawText(strText, mWidth/2 - (float) (boundsText.right / 2)
+ (float) (boundsText.left / 2), mWidth/2 + mTextOffset, mTextStrPaint);
}
/**
* 畫文字--健康指數數量
*
* @param canvas
*/
private void setMidTextNum(Canvas canvas) {
mTextNumPaint.setTextSize(45);
mTextNumPaint.getTextBounds(strTextNum, 0, strTextNum.length(), bounds);
System.out.println(bounds.right + "," + bounds.bottom);
Log.e("VIEW_LOG_TAG", bounds.right + "," + bounds.bottom + ","
+ bounds.top + "," + bounds.left);
canvas.drawText(strTextNum, mWidth/2 - (float) (bounds.right / 2)
+ (float) (bounds.left / 2), mWidth/2, mTextNumPaint);
}
其中難點就是要計算文字顯示位置,首先確定文字所在矩形
mTextStrPaint.getTextBounds(strText, 0, strText.length(), boundsText);
然後調用 drawText
android.graphics.Canvas.drawText(String text, float x, float y, Paint paint)
其中 x , y 是確定它所繪製的位置
x = mWidth/2 - (float) (boundsText.right / 2)
+ (float) (boundsText.left / 2);
y = mWidth/2 + mTextOffset;
由總寬度的一半減去文字的一半就可以確定文字開始繪製的位置了。
5、中間層圓弧進度顯示
/**
* 根據當前進度畫線
*
* @param canvas
*/
private void setAngleProgress(Canvas canvas) {
initPaint(midColor, strokeMidWidth,
mOffset + strokeMidWidth + strokeOuterWidth,
mOffset + strokeMidWidth + strokeOuterWidth,
mWidth - strokeOuterWidth - strokeMidWidth - mOffset,
mWidth - strokeOuterWidth - strokeMidWidth - mOffset);
float progress = this.mProgress * sweetAngle / 100.0f;
// 畫中間層
canvas.drawArc(oval, startAngle, progress, false, mPaint);
}
其原理就是在中間弧線上根據進度繪製一條弧線,因爲總弧度爲300°,所以其轉換爲當前進度(0-100)* 300 / 100 , 這樣就可以計算出需要繪製的進度了。
至此整個過程已經結束了,其中不足之處就是繪製外層圓弧文字的時候計算問題,沒有找到規律,對Api使用不是太熟悉,如果有哪位朋友能改善希望告知,不勝感激!
對了,還有適配的問題,在這裏我是重寫 onMeasure
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
/**
* 設置寬度
*/
int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
if (specMode == MeasureSpec.EXACTLY)// match_parent , accurate
{
mWidth = specSize;
}
/***
* 設置高度
*/
specMode = MeasureSpec.getMode(heightMeasureSpec);
specSize = MeasureSpec.getSize(heightMeasureSpec);
if (specMode == MeasureSpec.EXACTLY)// match_parent , accurate
{
mHeight = specSize;
}
mWidth = mHeight = Math.max(mWidth, mHeight);
if(mWidth > mScreenWidth || mWidth == 0){
mWidth = mScreenWidth;
}
Log.e("xxx", "EXACTLY" + mWidth + " " + mHeight);
setMeasuredDimension(mWidth,mWidth); // 強制長寬相等
}
如果 xml 裏沒有指定寬高或者指定的高度大於屏幕寬度的話則
mWidth = mScreenWidth;
然後 setMeasuredDimension(mWidth,mWidth); // 強制長寬相等
代碼下載地址demo