最近要完成這樣的一個效果圖:
要求是中間圓環進度條的顏色是漸變的,75%的#FFFFFF漸變到100%,寬度6pt,兩端都是圓的,并且有一個20% #000000、8pt的外發光。
看著這要求,很是頭疼,還外發光。。
主要的難點有幾個:
- 背景顏色的漸變
- 圓弧的漸變
- 讓文字畫在圓形的中間,圖片也畫在中間
首先背景顏色的漸變,可以定義一個drawable解決:
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:angle="315"
android:endColor="#00A1F8"
android:startColor="#84CFFB" />
</shape>
注意的是 angle 屬性角度必須是45度的倍數,至于什么度數的漸變方位在哪,那就自己試一下就行,315度就是在左上角。
第二個,圓弧的漸變
- 讓弧線的兩端是圓滑的,需要給Pain設置一個屬性:
currentPaint.setStrokeCap(Paint.Cap.ROUND); //讓弧線兩邊是圓滑的
- 線性漸變 LinearGradient
LinearGradient有兩個構造方法,分別是:
/** Create a shader that draws a linear gradient along a line.
@param x0 The x-coordinate for the start of the gradient line
@param y0 The y-coordinate for the start of the gradient line
@param x1 The x-coordinate for the end of the gradient line
@param y1 The y-coordinate for the end of the gradient line
@param colors The colors to be distributed along the gradient line
@param positions May be null. The relative positions [0..1] of
each corresponding color in the colors array. If this is null,
the the colors are distributed evenly along the gradient line.
@param tile The Shader tiling mode
*/
public LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[],
TileMode tile) {
}
和
/** Create a shader that draws a linear gradient along a line.
@param x0 The x-coordinate for the start of the gradient line
@param y0 The y-coordinate for the start of the gradient line
@param x1 The x-coordinate for the end of the gradient line
@param y1 The y-coordinate for the end of the gradient line
@param color0 The color at the start of the gradient line.
@param color1 The color at the end of the gradient line.
@param tile The Shader tiling mode
*/
public LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1,
TileMode tile) {
}
前面 4 個 參數都是起始坐標和結束坐標,第一個colors數組是沿漸變線分布的顏色,positions數組是 相對位置[0..1]的的顏色陣列中的每個相應的顏色。如果這是空,該顏色是沿著漸變線均勻分布。(其實就是對注釋的翻譯,我翻譯不好)
第二個構造方法后面直接就是開始漸變的顏色和結束漸變的顏色。最后一個參數都傳 Shader.TileMode.MIRROR 就好。
其實這里比較想不通的是漸變如果是沿著一條直線還好,怎么才能讓它沿著一條弧線來漸變呢,就是說 float x1, float y1 這兩個坐標不知道怎么傳。
先看怎么畫弧線:
mOval = new RectF(
mStrokeWidth + dp(4),
mStrokeWidth + dp(4),
getWidth() - mStrokeWidth - dp(4),
getHeight() - mStrokeWidth - dp(4)
);
float sweepAngle = ((float) currentValue / (float) totalValue) * 360f;
canvas.drawArc(mOval, mStartAngle, sweepAngle, false, currentPaint);
首先定義一個范圍,再畫出來。
至于剛剛的問題,參考了一個開源控件的寫法,原來這樣設置 LinearGradient 的參數就可以達到效果:
mShader = new LinearGradient(
mOval.left,
mOval.top,
mOval.left,
mOval.bottom,
mFgColorStart,
mFgColorEnd,
Shader.TileMode.MIRROR
);
第三個,讓文字畫在圓形的中間,圖片也畫在中間
當你想把文字繪制在中間時,你可能第一時間會想到坐標是這樣的:
int textX = getWidth() / 2;
int textY = getHeight() / 2;
可是這樣是不行的,你必須要考慮加上文字的寬度和高度才能算正確,
獲取文字寬度的方法:
textPaint.measureText(text + ""); //測量文字,得到寬度
因為文字也是數字,也是可以測量的。
得到文字高度,必須理解基準線這東西,這里有個文章介紹。
再貼個圖:
可以看到,得到高度,就相當于 ascent 減去 descent:
float textHeight = textPaint.ascent() + textPaint.descent(); //根據基準線得到文字的高
所有這樣的坐標才能畫在正中間:
int textX = getWidth() / 2;
int textY = getHeight() / 2;
textX = textX - textWidth / 2;
textY = textY - textHeight / 2;
圖片的話,Bitmap 都有相應的 get 方法,就不說了。
==================分割線=2016.6.3==========================
更正一個錯誤,沿著弧線漸變用線性漸變是不行的,如果用以上方法它會有一種效果就是從淺到深后又會由深到淺。
正確的方法是使用梯度漸變 SweepGradient 和 Matrix結合起來才有效果。
首先先看下 SweepGradient 的大致效果如下:
這樣的效果就不會出現線性漸變的問題了。
代碼如下:
private void init() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(50);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setColor(Color.WHITE);
mMatrix = new Matrix();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int x = getWidth() / 2;
int y = getHeight() / 2;
float startAngle = 270;
float sweepAngle = 340;
mMatrix.setRotate(-150, x, y);
mShader = new SweepGradient(startAngle, sweepAngle, new int[]{0x4dffffff, 0xffffffff}, null);
mShader.setLocalMatrix(mMatrix);
mPaint.setShader(mShader);
RectF mOval = new RectF(25, 25, getWidth() - 25, getHeight() - 25);
canvas.drawArc(mOval, startAngle, sweepAngle, false, mPaint);
}
其中,核心代碼是:
mMatrix.setRotate(-150, x, y);
mShader.setLocalMatrix(mMatrix);
沒有這兩句,怎么寫都是白搭。
其中 x 和 y 是圓心坐標,-150的意思是 將掃描起始的地方逆時針選擇90°后,圓角畫筆的地方還有半個圓沒有遮蓋。
因為我是從12點方向開始畫的,而掃描開始的方向是3點鐘,所以我填的是 -90 ,剩下那 -60 度是微調,這個需要按實際情況而定,看下這兩個圖或自己試一下就知道什么意思了:
填 -90 度的時候效果:
加上 -60 度的微調后的效果:
這樣,應該能夠明白什么意思了。
不過有一個bug,當結束角度接近360度的時候,會顯示成這樣:
暫時沒有解決辦法,哪位大神看到有辦法的,請支招。