【原】使用Xfermode正確的繪制出遮罩效果


以前寫as3的時候,遮罩效果一個mask屬性就搞定了,真是方便。

 

轉到android上以后,發現要實現類似的效果,可以使用Xfermode,android一共提供了三種:

AvoidXfermode; PixelXorXfermode;
PorterDuffXfermode;

前兩種已經不被推薦使用了(據說是因為不支持硬件加速,要生效得強制關閉硬件加速),就不細說了,主要說說第三種,一共提供了十六種效果(as3里也提供了類似,但是更加復雜的方法,所以對我而言還是比較熟悉的),如圖所示:

但是要正確的在canvas上實現這些效果,還真是沒那么容易,我也是研究了半天,終於實現了自己想要的效果,下面用一個例子說明下我的操作流程。

 

想要實現的效果是這樣的:

簡單分析一下,繪制一個圓形和一個矩形,計算好相應的坐標位置,然后使用SRC_IN進行混合就可以了,類似這樣:

 

下面說一下我的操作流程:

1. 繪制border

2. 保存為單獨層(canvas.saveLayer),特別注意這一步必須要有,否則無論如何出不來正常效果,起碼我試了很久沒有成功

3. 繪制填充的圓形,同時也是遮罩

4. 設置筆觸的Xfermode為new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)

5. 使用該筆觸繪制矩形,進行混合

6. 恢復到canvas上

 

上代碼,大家可以對照看一下各個步驟的具體代碼

    @Override
    protected void onDraw(Canvas canvas) {
        float strokeWidth = getResources().getDimension(R.dimen.stroke_width);
        int borderColor = getResources().getColor(R.color.carnation);
        int fillColor = getResources().getColor(R.color.carnation_lighter);
        int percentColor = getResources().getColor(R.color.carnation_light);
        int width = getWidth();
        int height = getHeight();

        //border
        Paint stroke = new Paint(Paint.ANTI_ALIAS_FLAG);
        stroke.setStrokeWidth(strokeWidth);
        stroke.setStyle(Paint.Style.STROKE);
        stroke.setColor(borderColor);
        canvas.drawOval(new RectF(strokeWidth/2,strokeWidth/2,width-strokeWidth/2,height-strokeWidth/2),stroke);

        //save as new layer
        int save = canvas.saveLayer(0,0,width,height,null,Canvas.ALL_SAVE_FLAG);

        //fill background
        Paint fill = new Paint(Paint.ANTI_ALIAS_FLAG);
        fill.setStyle(Paint.Style.FILL);
        fill.setColor(fillColor);
        canvas.drawOval(new RectF(strokeWidth - 1, strokeWidth - 1, width - strokeWidth + 1, height - strokeWidth + 1), fill);

        //mix rect
        fill.setColor(percentColor);
        fill.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawRect(0,(1-mPercent)*height,width,height,fill);

        //restore to canvas
        canvas.restoreToCount(save);

        super.onDraw(canvas);
    }

之前也查了不少文章,貌似沒有看到多少着重說saveLayer的,還是我對照官方apidemos源碼試出來的,希望對遇到同樣疑問的朋友有所幫助!

 

2015/7/9 更新:

1. 要實現混合的兩個圖形,必須位於同一個layer上,經測試位於不同layer上是無法混合的,即使最后都繪制到了canvas上。

2. 不同的繪制順序,可能有不同的效果,注意一下邏輯即可。

更新一段復雜點的例子

使用了兩種混合方式SRC_IN和CLEAR,主要代碼如下:

 1     @Override
 2     protected void onDraw(Canvas canvas) {
 3         int width = getWidth();
 4         int height = getHeight();
 5 
 6         float strokeWidth = DimenUtils.dp2px(4);
 7         float pointRadius = DimenUtils.dp2px(4);
 8         float gap = DimenUtils.dp2px(4);
 9         float monthRadius = height * 0.2f;
10         float textSize = DimenUtils.dp2px(14);
11 
12         int color = getResources().getColor(R.color.carnation);
13         int lightColor = getResources().getColor(R.color.carnation_light);
14         int lighterColor = getResources().getColor(R.color.carnation_lighter);
15 
16         float degree = 360*mRate;
17 
18         Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
19 
20         //new layer
21         int save = canvas.saveLayer(0, 0, width, height, null, Canvas.ALL_SAVE_FLAG);
22 
23         //draw percent
24         canvas.save();
25         float fillDistance = pointRadius+gap+strokeWidth/2;
26         canvas.translate(fillDistance,fillDistance);
27         RectF fillRect = new RectF(0,0,width-2*fillDistance,height-2*fillDistance);
28         paint.setColor(lightColor);
29         paint.setStyle(Paint.Style.FILL);
30         canvas.drawOval(fillRect,paint);
31         //mix rect
32         paint.setColor(color);
33         paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
34         canvas.drawRect(0, (1 - mPercent) * (height-2*fillDistance), width-2*fillDistance, height-2*fillDistance, paint);
35         canvas.restore();
36 
37         //border
38         paint.setXfermode(null);
39         paint.setStrokeWidth(strokeWidth);
40         paint.setStyle(Paint.Style.STROKE);
41         paint.setColor(lighterColor);
42         RectF borderRect = new RectF(pointRadius,pointRadius,width-pointRadius,height-pointRadius);
43         canvas.drawOval(borderRect, paint);
44         paint.setColor(color);
45         canvas.drawArc(borderRect,270,degree,false,paint);
46         //draw point
47         canvas.save();
48         paint.setStyle(Paint.Style.FILL);
49         canvas.translate(width/2,height/2);
50         canvas.rotate(degree);
51         canvas.drawCircle(0,pointRadius-height/2,pointRadius,paint);
52         canvas.restore();
53 
54         //draw month
55         canvas.save();
56         canvas.translate(width*0.7f,height*0.8f);
57         paint.setColor(Color.BLACK);
58         paint.setStyle(Paint.Style.STROKE);
59         paint.setStrokeWidth(gap);
60         paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
61         canvas.drawCircle(0,0,monthRadius+gap/2,paint);
62         paint.setXfermode(null);
63         paint.setStyle(Paint.Style.FILL);
64         paint.setColor(getResources().getColor(R.color.orange));
65         canvas.drawCircle(0,0,monthRadius,paint);
66         canvas.restoreToCount(save);
67     }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM