之前突然想到过弄一个point样式为圆环的进度条,类似于铁环划过一根棍子那样的场景,采用了利用Path绘制两个椭圆,一大一小嵌套,看着有点奇怪,但是效果勉强实现了。
1.Path.op()相关
Path类提供了一个op方法,该方法的作用是对两个Path做交集/补集/并集操作
/**
* Set this path to the result of applying the Op to this path and the specified path.
* The resulting path will be constructed from non-overlapping contours.
* The curve order is reduced where possible so that cubics may be turned
* into quadratics, and quadratics maybe turned into lines.
*
* @param path The second operand (for difference, the subtrahend)
*
* @return True if operation succeeded, false otherwise and this path remains unmodified.
*
* @see Op
* @see #op(Path, Path, android.graphics.Path.Op)
*/
public boolean op(@NonNull Path path, @NonNull Op op) {
return op(this, path, op);
}
其中参数op有五种取值:
public enum Op {
/**
* Subtract the second path from the first path.
*/
DIFFERENCE,
/**
* Intersect the two paths.
*/
INTERSECT,
/**
* Union (inclusive-or) the two paths.
*/
UNION,
/**
* Exclusive-or the two paths.
*/
XOR,
/**
* Subtract the first path from the second path.
*/
REVERSE_DIFFERENCE
}
对两个圆的op操作效果如下图:
因为我要画的是圆环,所以采用了Path.Op.DIFFERENCE来处理两个封闭的Path.
除了Path,页可以使用Region的裁剪,Region表示的是canvas图层上的某一块封闭的区域,它也提供了一系列组合的方法:
public final boolean union(Rect r)
public boolean op(Rect r, Op op) {
public boolean op(int left, int top, int right, int bottom, Op op)
public boolean op(Region region, Op op)
public boolean op(Rect rect, Region region, Op op)
2.Region.op()对比Path.op()
两个方法都能达到组合目的,相对前者是对Region的操作,要根据使用场景选取。
1.使用Region,其实还是path设置到Region,然后让两个Region去做op组合操作,再将组合后的Region绘制出来:
outClipRegin = new Region(new Rect(50, 50, 200, 500));
// 外椭圆
outOvalPath = new Path();
RectF outOvalRect = new RectF(50, 50, 200, 500);
outOvalPath.addOval(outOvalRect, Path.Direction.CCW);
outRegin = new Region();
outRegin.setPath(outOvalPath, outClipRegin);
// 内椭圆
inOvalPath = new Path();
RectF inOvalRect = new RectF(80, 70, 179, 450);
Region inClipRegin = new Region(new Rect(80, 70, 179, 450));
inOvalPath.addOval(inOvalRect, Path.Direction.CCW);
inRegin = new Region();
inRegin.setPath(inOvalPath, inClipRegin);
// 组合处理
outRegin.op(inRegin, Region.Op.DIFFERENCE);
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
RegionIterator iterator = new RegionIterator(outRegin);
Rect rect = new Rect();
while (iterator.next(rect)) {
canvas.drawRect(rect, paint);
}
}
2.使用Path:
outOvalPath = new Path();
RectF outOvalRect = new RectF(50, 50, 200, 500);
outOvalPath.addOval(outOvalRect, Path.Direction.CCW);
inOvalPath = new Path();
RectF inOvalRect = new RectF(80, 70, 179, 450);
inOvalPath.addOval(inOvalRect, Path.Direction.CCW);
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
outOvalPath.op(inOvalPath, Path.Op.DIFFERENCE);
canvas.drawPath(outOvalPath, paint);
}
效果对比:
3.进度条point为图片时候的绘制:
首先根据.xml布局传入的progressPoint大小缩放图标,然后绘制bitmap:
缩放:
private fun imageScale(bitmap: Bitmap, newWidth: Int, newHeight: Int): Bitmap {
val oldWidth = bitmap.width
val oldHeight = bitmap.height
val scaleForWidth = newWidth.toFloat() / oldWidth
val scaleForHeight = newHeight.toFloat() / oldHeight
val mMatrix = Matrix()
mMatrix.postScale(scaleForWidth, scaleForHeight)
val newBitmap = Bitmap.createBitmap(bitmap, 0, 0, oldWidth, oldHeight, mMatrix, true)
bitmap.recycle()
return newBitmap
}
绘制缩放后的bitmap:
canvas.save()
canvas.drawBitmap(pointBitmap,passLength,0f, pointPaint)
canvas.restore()
4.进度条渐变
利用着色器Shader给Paint设置渐变色,Shader的实现类有以下几种:
对进度条选择线性渐变:
var colorArray = intArrayOf(lineColor, lineColor2!!, lineColor3!!)
// left,top,right,height,颜色数组, 浮点型数组代表各色彩所占比例,TileMode采用重复着色
mLineShader = LinearGradient(
0f, 0f, pointSize.toFloat() * 5, pointSize.toFloat(), colorArray,
floatArrayOf(0.3f, 0.6f, 0.9f), Shader.TileMode.REPEAT
)
mLineShader?.let {
// 设置给画笔
linePaint.shader = it
}