1

示例路径

我想绘制与图像类似的路径。它有两个黑色边框,内部是白色而不是透明的。如何实现呢?尤其是关节位置。

4

2 回答 2

23

要获得所需的结果,您必须分段绘制路径。

您将使用两个不同的Paint对象绘制部件两次,仅在描边宽度和颜色方面有所不同。绘制完每个部分后,路径将被重置。代码注释应解释其余部分:

public class SView extends View {

    Path path;
    Paint paint, paintWhite;
    RectF rf, rf2, rf3;

    public SView(Context context) {
        super(context);

        path = new Path();

        // 'paint' has a wider stroke-width 
        // compared to 'paintWhite' and 
        // thus becomes the border paint
        paint = new Paint();
        paint.setColor(Color.BLUE);
        paint.setAntiAlias(true);
        paint.setStrokeWidth(15);
        paint.setStyle(Style.STROKE);

        // 'paintWhite' colors the inner path
        paintWhite = new Paint();
        paintWhite.setColor(Color.WHITE);
        paintWhite.setAntiAlias(true);
        paintWhite.setStrokeWidth(12);
        paintWhite.setStyle(Style.STROKE);

        // For this example, we'll draw three
        // arcs bound by the following RectF
        // objects
        rf = new RectF(200, 200, 500, 500);
        rf2 = new RectF(200, 200, 400, 500);
        rf3 = new RectF(100, 200, 400, 500);                    
    }

    @Override
    protected void onDraw(Canvas canvas) {          

        // First arc bound by 'rf'
        path.arcTo(rf, 0, 180);

        // Draw 'path' using 'paint' 
        // and then, again using 'paintWhite'       
        canvas.drawPath(path, paint);
        canvas.drawPath(path, paintWhite);

        // Reset 'path' so as to clear 
        // history
        path.reset();

        // Repeat with the rest of arcs
        path.arcTo(rf2, 180, 180);      
        canvas.drawPath(path, paint);
        canvas.drawPath(path, paintWhite);      
        path.reset();           

        path.arcTo(rf3, 0, 180);            
        canvas.drawPath(path, paint);
        canvas.drawPath(path, paintWhite);  

    }       
}

输出:

在此处输入图像描述

注意:重叠的弧以 为界RectF rf3,最后绘制。

由于我们将white部分与 一起绘制borders,因此您不会以四路交叉结束。这些部分将以flyover一种方式重叠:按照它们被绘制的顺序。

为了提高性能(我想),您可以在重置路径之前检查路径的下一部分是否会与之前的任何部分相交。如果下一部分确实相交,请重置并使用两个 Paint 对象绘制该部分。如果没有,只需将该部分附加到路径并等到下一个交叉点来绘制它。您当然需要维护绘制部分的历史记录(在上面的示例中,历史记录将包含边界:'RectF' 对象)。但是,我不能 100% 确定这是否比反复重置路径然后绘制零件更好。

于 2013-11-04T02:11:44.840 回答
1

您可以尝试使用两个不同的 Paint 对象来绘制相同的路径。第一个 Paint 对象将具有您想要的边框颜色和较大的笔划宽度。第二个 Paint 将具有较小的笔画宽度,PorterDuff.Mode.CLEAR并在其上设置。

linePaint = new Paint();
linePaint.setAntiAlias(true);
linePaint.setColor(Color.YELLOW);
linePaint.setStrokeWidth(6);
linePaint.setStyle(Paint.Style.STROKE);

clearPaint = new Paint();
clearPaint.setAntiAlias(true);
clearPaint.setXfermode(new PorterDuffXfermode(Mode.CLEAR));
clearPaint.setStyle(Paint.Style.STROKE);
clearPaint.setStrokeWidth(3);

然后在您的 onDraw() 方法中,使用上述两种颜料绘制两次相同的路径。

canvas.drawPath(path, linePaint);
canvas.drawPath(path, clearPaint);

这在性能方面可能不是很有效,但这是我能想到的最简单的方法。

编辑:我自己还没有彻底测试过,所以如果有任何问题,请告诉我。

更新:经过测试,我发现 PorterDuff.Mode.CLEAR 将清除画布支持位图中存在的整个像素信息。所以通常会得到这样的结果:

充满黑色的路径

解决此问题的解决方案是创建一个屏幕外画布来绘制路径。

Canvas canvas2 = new Canvas();
Bitmap backingBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);


canvas2.setBitmap(backingBitmap);
canvas2.drawPaint(clearPaint);
canvas2.drawPath(path, linePaint);
canvas2.drawPath(path, clearPaint);

canvas.drawBitmap(backingBitmap, 0, 0, null);

使用上述方法,您会得到以下结果: 正确的边界

不利的一面是,如果您每帧都创建位图,则可能会影响性能。位图的大小也会对性能产生不利影响。因此,您只需要在必要时创建屏幕外画布,并且只要您不需要重置路径信息,就可以继续在之前创建的 backingBitmap 上绘图。

于 2013-11-03T03:55:19.693 回答