Android自定义View - 自定义进度条Path的使用

之前突然想到过弄一个point样式为圆环的进度条,类似于铁环划过一根棍子那样的场景,采用了利用Path绘制两个椭圆,一大一小嵌套,看着有点奇怪,但是效果勉强实现了。

ezgif-2-82ad2cacbc84.gif

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效果.png

因为我要画的是圆环,所以采用了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);
    }

效果对比:


200853.png

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
            }

你可能感兴趣的:(Android自定义View - 自定义进度条Path的使用)