PorterduffXfermode:清除位图的一部分

2022-09-01 06:43:30

目标是绘制一个位图并覆盖某些内容的顶部,并绘制擦除位图底层区域的形状。

我有一个概念证明,试图理解我应该如何去做。我发现了许多关于使用:

android.graphics.PorterDuff.Mode.CLEAR

下面的代码创建一个具有蓝色背景的屏幕,并添加一个自定义视图。此视图从下到上在其画布上绘制:粉红色背景,带有轻微插图以显示粉红色背景的位图,以及每个 PorterDuffXfermode 的黄色圆圈。

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuffXfermode;
import android.graphics.Paint.Style;
import android.graphics.PorterDuff.Mode;
import android.graphics.RectF;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.widget.RelativeLayout;

public class Test extends Activity {
    Drawing d = null;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        
        RelativeLayout.LayoutParams rlp = null;
        
        // Create the view for the xfermode test
        d = new Drawing(this);
        rlp = new RelativeLayout.LayoutParams(600, 900);
        rlp.addRule(RelativeLayout.CENTER_IN_PARENT);
        d.setLayoutParams(rlp);
        
        RelativeLayout rl = new RelativeLayout(this);
        rl.setBackgroundColor(Color.rgb(0, 0, 255));
        rl.addView(d);
        
        // Show the layout with the test view
        setContentView(rl);
    }
    
    public class Drawing extends View {
        Paint[] pDraw = null;
        Bitmap bm = null;
        
        public Drawing(Context ct) {
            super(ct);
            
            // Generate bitmap used for background
            bm = BitmapFactory.decodeFile("mnt/sdcard/Pictures/test.jpg");
            
            // Generate array of paints
            pDraw = new Paint[16];
            
            for (int i = 0; i<pDraw.length; i++) {
                pDraw[i] = new Paint();
                pDraw[i].setARGB(255, 255, 255, 0);
                pDraw[i].setStrokeWidth(20);
                pDraw[i].setStyle(Style.FILL);
            }
            
            // Set all transfer modes
            pDraw[0].setXfermode(new PorterDuffXfermode(Mode.CLEAR));
            pDraw[1].setXfermode(new PorterDuffXfermode(Mode.DARKEN));
            pDraw[2].setXfermode(new PorterDuffXfermode(Mode.DST));
            pDraw[3].setXfermode(new PorterDuffXfermode(Mode.DST_ATOP));
            pDraw[4].setXfermode(new PorterDuffXfermode(Mode.DST_IN));
            pDraw[5].setXfermode(new PorterDuffXfermode(Mode.DST_OUT));
            pDraw[6].setXfermode(new PorterDuffXfermode(Mode.DST_OVER));
            pDraw[7].setXfermode(new PorterDuffXfermode(Mode.LIGHTEN));
            pDraw[8].setXfermode(new PorterDuffXfermode(Mode.MULTIPLY));
            pDraw[9].setXfermode(new PorterDuffXfermode(Mode.SCREEN));
            pDraw[10].setXfermode(new PorterDuffXfermode(Mode.SRC));
            pDraw[11].setXfermode(new PorterDuffXfermode(Mode.SRC_ATOP));
            pDraw[12].setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
            pDraw[13].setXfermode(new PorterDuffXfermode(Mode.SRC_OUT));
            pDraw[14].setXfermode(new PorterDuffXfermode(Mode.SRC_OVER));
            pDraw[15].setXfermode(new PorterDuffXfermode(Mode.XOR));
        }
        
        @Override
        public void onDraw(Canvas canv) {
            // Background colour for canvas
            canv.drawColor(Color.argb(255, 255, 0, 255));
            
            // Draw the bitmap leaving small outside border to see background
            canv.drawBitmap(bm, null, new RectF(15, 15, getMeasuredWidth() - 15, getMeasuredHeight() - 15), null);

            float w, h;
            w = getMeasuredWidth();
            h = getMeasuredHeight();

            // Loop, draws 4 circles per row, in 4 rows on top of bitmap
            // Drawn in the order of pDraw (alphabetical)
            for(int i = 0; i<4; i++) {
                for(int ii = 0; ii<4; ii++) {
                    canv.drawCircle((w / 8) * (ii * 2 + 1), (h / 8) * (i * 2 + 1), w / 8 * 0.8f, pDraw[i*4 + ii]);
                }
            }
        }
    }

}

这是测试的结果:

enter image description here

清除模式是左上角,显示为黑色。

在另一个我试图使用它的例子中,我有一个DialogFragment,其中CLEAR模式擦除了整个DialogFragment,以便可以看到下面的活动。因此,我在这个测试中使用了许多不同的背景颜色。

这是否可能像其他例子一样清除整个活动的像素,使我相信?我本来以为只能擦除与视图相关的画布像素,但在我的另一个示例中,自定义视图,底层图像视图和DialogFragment背景的像素都被清除了。

有人可以帮我了解到底发生了什么吗?

编辑:我复制了一个例子来证实我的怀疑。在 DialogFragment 中添加此确切的自定义视图时,基础活动将变为可见。

enter image description here

这对我来说似乎是一个非常明确的指标,表明它以某种方式也抹去了下面视图的画布?我的猜测是第一个示例中的黑色是顶级视图的黑色吗?Mode.CLEAR


答案 1

问题是硬件加速。为要使用 CLEAR 绘制的特定视图将其关闭。如果您使用的是自定义视图,请在构造函数中执行此操作:

if (android.os.Build.VERSION.SDK_INT >= 11) 
{
     setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}

您还可以在视图引用上调用 setLayerType。


答案 2

我没有看到任何意想不到的事情。在 的特定情况下,目标的颜色和 alpha 都被清除,这允许显示黑色背景。该实用程序允许人们尝试各种模式,颜色和alpha值,并且源可以提供一些见解。在下面(有些过时的)图像中,这些区域显示了平台代表提供的淡淡的细条纹灰色。Mode.CLEARCLEARPanelUI

image
(资料来源:sites.google.com 综合


推荐