最近要完成这样的一个效果图:
要求是中间圆环进度条的颜色是渐变的,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度的时候,会显示成这样:
暂时没有解决办法,哪位大神看到有办法的,请支招。