本文主要是介绍Android Paint系列之Xfermode + 刮刮卡效果实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
效果图:
- 源码分析
- Alpha合成模式
- 混合模式
- 合成方程
- ADD
- CLEAR
- DARKEN
- DST
- DST_ATOP
- DST_IN
- DST_OUT
- DST_OVER
- LIGHTEN
- MULTIPLY
- OVERLAY
- SCREEN
- SRC
- SRC_ATOP
- SRC_IN
- SRC_OUT
- SRC_OVER
- XOR
- 源码理解
- 混合模式分类
- SRC类
- DST类
- 其他类
- 混合模式分类
- Demo测试
- 官方代码
- 自定义View
- 与官方Demo的差异
- 要注意的地方
- 刮刮卡效果实现
- Demo地址
源码分析
我们现在针对Xfermode源码来分析一波(Api 27):
paint.setXfermode(Xfermode xfermode);
接口里面主要为赋值操作,会将xfermode安装到paint中,设置或者清除传输模式对象(即xfermode),传输模式定义源像素(通过绘图命令生成)如何与目标像素(渲染目标的内容)进行合成,若设置为null,则会清除任何先前的传输模式。 为了方便,传递的参数也被返回。
我们可以将Xfermode称为图像混合模式
。
PorterDuffXfermode是最常见的图像混合模式,同时还有另外两个类,PixelXorXfermode和AvoidXfermode,由于这两个类已经被弃用,我们主讲PorterDuffXfermode模式。
PorterDuffXfermode中,定义了一个PorterDuff.Mode:
我们再跟进去看看PorterDuff类,由于源码注释过多,这里不全截图示例:
该类的注释为:
类名是对托马斯波特和汤姆达夫的作品的敬意,他们在1984年发表的题为“合成数字图像”的开创性论文中作了介绍。在本文中,作者描述了12个合成操作符,这些操作符管理如何计算由目标(渲染目标的内容)组成的源(要显示的图形对象)的颜色结果。“合成数字图像”于1984年7月在计算机图形学卷18,第3期出版。由于Porter和Duff的工作仅关注源和目的地的alpha通道的影响,原始文章中描述的12个操作员在这里被称为alpha合成模式。为了方便起见,这个类还提供了几种混合模式,它们类似地定义了合成源和目的地的结果,但没有被限制到alpha通道。这些混合模式并未由Porter和Duff定义,但为方便起见,已包含在本课程中。
定义了一个Mode枚举类,还有两个方法分别为模式转int和int转模式,下面我们再看看Mode这个枚举类:
public enum Mode {// these value must match their native equivalents. See SkXfermode.h/*** <p>* <img src="{@docRoot}reference/android/images/graphics/composite_CLEAR.png" />* <figcaption>Destination pixels covered by the source are cleared to 0.</figcaption>* </p>* <p>\(\alpha_{out} = 0\)</p>* <p>\(C_{out} = 0\)</p>*/CLEAR (0),/*** <p>* <img src="{@docRoot}reference/android/images/graphics/composite_SRC.png" />* <figcaption>The source pixels replace the destination pixels.</figcaption>* </p>* <p>\(\alpha_{out} = \alpha_{src}\)</p>* <p>\(C_{out} = C_{src}\)</p>*/SRC (1),/*** <p>* <img src="{@docRoot}reference/android/images/graphics/composite_DST.png" />* <figcaption>The source pixels are discarded, leaving the destination intact.</figcaption>* </p>* <p>\(\alpha_{out} = \alpha_{dst}\)</p>* <p>\(C_{out} = C_{dst}\)</p>*/DST (2),/*** <p>* <img src="{@docRoot}reference/android/images/graphics/composite_SRC_OVER.png" />* <figcaption>The source pixels are drawn over the destination pixels.</figcaption>* </p>* <p>\(\alpha_{out} = \alpha_{src} + (1 - \alpha_{src}) * \alpha_{dst}\)</p>* <p>\(C_{out} = C_{src} + (1 - \alpha_{src}) * C_{dst}\)</p>*/SRC_OVER (3),/*** <p>* <img src="{@docRoot}reference/android/images/graphics/composite_DST_OVER.png" />* <figcaption>The source pixels are drawn behind the destination pixels.</figcaption>* </p>* <p>\(\alpha_{out} = \alpha_{dst} + (1 - \alpha_{dst}) * \alpha_{src}\)</p>* <p>\(C_{out} = C_{dst} + (1 - \alpha_{dst}) * C_{src}\)</p>*/DST_OVER (4),/*** <p>* <img src="{@docRoot}reference/android/images/graphics/composite_SRC_IN.png" />* <figcaption>Keeps the source pixels that cover the destination pixels,* discards the remaining source and destination pixels.</figcaption>* </p>* <p>\(\alpha_{out} = \alpha_{src} * \alpha_{dst}\)</p>* <p>\(C_{out} = C_{src} * \alpha_{dst}\)</p>*/SRC_IN (5),/*** <p>* <img src="{@docRoot}reference/android/images/graphics/composite_DST_IN.png" />* <figcaption>Keeps the destination pixels that cover source pixels,* discards the remaining source and destination pixels.</figcaption>* </p>* <p>\(\alpha_{out} = \alpha_{src} * \alpha_{dst}\)</p>* <p>\(C_{out} = C_{dst} * \alpha_{src}\)</p>*/DST_IN (6),/*** <p>* <img src="{@docRoot}reference/android/images/graphics/composite_SRC_OUT.png" />* <figcaption>Keeps the source pixels that do not cover destination pixels.* Discards source pixels that cover destination pixels. Discards all* destination pixels.</figcaption>* </p>* <p>\(\alpha_{out} = (1 - \alpha_{dst}) * \alpha_{src}\)</p>* <p>\(C_{out} = (1 - \alpha_{dst}) * C_{src}\)</p>*/SRC_OUT (7),/*** <p>* <img src="{@docRoot}reference/android/images/graphics/composite_DST_OUT.png" />* <figcaption>Keeps the destination pixels that are not covered by source pixels.* Discards destination pixels that are covered by source pixels. Discards all* source pixels.</figcaption>* </p>* <p>\(\alpha_{out} = (1 - \alpha_{src}) * \alpha_{dst}\)</p>* <p>\(C_{out} = (1 - \alpha_{src}) * C_{dst}\)</p>*/DST_OUT (8),/*** <p>* <img src="{@docRoot}reference/android/images/graphics/composite_SRC_ATOP.png" />* <figcaption>Discards the source pixels that do not cover destination pixels.* Draws remaining source pixels over destination pixels.</figcaption>* </p>* <p>\(\alpha_{out} = \alpha_{dst}\)</p>* <p>\(C_{out} = \alpha_{dst} * C_{src} + (1 - \alpha_{src}) * C_{dst}\)</p>*/SRC_ATOP (9),/*** <p>* <img src="{@docRoot}reference/android/images/graphics/composite_DST_ATOP.png" />* <figcaption>Discards the destination pixels that are not covered by source pixels.* Draws remaining destination pixels over source pixels.</figcaption>* </p>* <p>\(\alpha_{out} = \alpha_{src}\)</p>* <p>\(C_{out} = \alpha_{src} * C_{dst} + (1 - \alpha_{dst}) * C_{src}\)</p>*/DST_ATOP (10),/*** <p>* <img src="{@docRoot}reference/android/images/graphics/composite_XOR.png" />* <figcaption>Discards the source and destination pixels where source pixels* cover destination pixels. Draws remaining source pixels.</figcaption>* </p>* <p>\(\alpha_{out} = (1 - \alpha_{dst}) * \alpha_{src} + (1 - \alpha_{src}) * \alpha_{dst}\)</p>* <p>\(C_{out} = (1 - \alpha_{dst}) * C_{src} + (1 - \alpha_{src}) * C_{dst}\)</p>*/XOR (11),/*** <p>* <img src="{@docRoot}reference/android/images/graphics/composite_DARKEN.png" />* <figcaption>Retains the smallest component of the source and* destination pixels.</figcaption>* </p>* <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>* <p>\(C_{out} = (1 - \alpha_{dst}) * C_{src} + (1 - \alpha_{src}) * C_{dst} + min(C_{src}, C_{dst})\)</p>*/DARKEN (16),/*** <p>* <img src="{@docRoot}reference/android/images/graphics/composite_LIGHTEN.png" />* <figcaption>Retains the largest component of the source and* destination pixel.</figcaption>* </p>* <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>* <p>\(C_{out} = (1 - \alpha_{dst}) * C_{src} + (1 - \alpha_{src}) * C_{dst} + max(C_{src}, C_{dst})\)</p>*/LIGHTEN (17),/*** <p>* <img src="{@docRoot}reference/android/images/graphics/composite_MULTIPLY.png" />* <figcaption>Multiplies the source and destination pixels.</figcaption>* </p>* <p>\(\alpha_{out} = \alpha_{src} * \alpha_{dst}\)</p>* <p>\(C_{out} = C_{src} * C_{dst}\)</p>*/MULTIPLY (13),/*** <p>* <img src="{@docRoot}reference/android/images/graphics/composite_SCREEN.png" />* <figcaption>Adds the source and destination pixels, then subtracts the* source pixels multiplied by the destination.</figcaption>* </p>* <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>* <p>\(C_{out} = C_{src} + C_{dst} - C_{src} * C_{dst}\)</p>*/SCREEN (14),/*** <p>* <img src="{@docRoot}reference/android/images/graphics/composite_ADD.png" />* <figcaption>Adds the source pixels to the destination pixels and saturates* the result.</figcaption>* </p>* <p>\(\alpha_{out} = max(0, min(\alpha_{src} + \alpha_{dst}, 1))\)</p>* <p>\(C_{out} = max(0, min(C_{src} + C_{dst}, 1))\)</p>*/ADD (12),/*** <p>* <img src="{@docRoot}reference/android/images/graphics/composite_OVERLAY.png" />* <figcaption>Multiplies or screens the source and destination depending on the* destination color.</figcaption>* </p>* <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>* <p>\(\begin{equation}* C_{out} = \begin{cases} 2 * C_{src} * C_{dst} & 2 * C_{dst} \lt \alpha_{dst} \\* \alpha_{src} * \alpha_{dst} - 2 (\alpha_{dst} - C_{src}) (\alpha_{src} - C_{dst}) & otherwise \end{cases}* \end{equation}\)</p>*/OVERLAY (15);Mode(int nativeInt) {this.nativeInt = nativeInt;}/*** @hide*/public final int nativeInt;}
该类主要定义了一些常量,即十八个常量,十八种模式:
下面介绍的所有示例图都使用相同的源图像和目标图像:
下面的代码片段显示了用于生成每个图的绘图操作顺序:
Paint paint = new Paint();canvas.drawBitmap(destinationImage, 0, 0, paint);PorterDuff.Mode mode = // choose a modepaint.setXfermode(new PorterDuffXfermode(mode));canvas.drawBitmap(sourceImage, 0, 0, paint);
即先进行一个bitmap的绘制,然后设置Xfermode,再进行另一个的bitmap的绘制,这时,会根据Xfermode的混合原则,对这两个图像进行混合处理。
Alpha合成模式
混合模式
合成方程
以下每个单独的alpha合成或混合模式的文档提供了用于计算源和目标的合成结果的alpha值和颜色值的确切公式。
结果(或输出)alpha值记为αout。 结果(或输出)颜色值被标注为Cout。
又为alpha_{out}和C_{out},通过两个图像的计算得到这两个输出值就是图像混合的核心,而下面就是具体的那些计算公式:
ADD
饱和相加,对图像饱和度进行相加
CLEAR
清除图像
DARKEN
保留源像素和目标像素的最小分量,即变暗,较深的颜色覆盖较浅的颜色,若两者深浅程度相同则混合。
DST
只显示目标图像
DST_ATOP
丢弃未被源像素覆盖的目标像素,在源像素上绘制剩余的目标像素
DST_IN
保留覆盖源像素的目标像素,丢弃剩余的源像素和目标像素
DST_OUT
保留未被源像素覆盖的目标像素, 放弃源像素覆盖的目标像素,丢弃所有源像素。
DST_OVER
源像素绘制在目标像素后面
LIGHTEN
保留源像素和目标像素的最大构成,变亮,与DARKEN相反,DARKEN和LIGHTEN生成的图像结果与Android对颜色值深浅的定义有关
MULTIPLY
将源像素和目标像素相乘
OVERLAY
覆盖
SCREEN
将源像素和目标像素相加,然后减去目标像素和源像素的相乘。
SRC
只显示源图像
SRC_ATOP
丢弃没有覆盖到目标像素的源像素,在目标像素上绘制剩余的源像素
SRC_IN
保留覆盖目标像素的源像素,丢弃剩余的源像素和目标像素
SRC_OUT
保持不包含目标像素的源像素
SRC_OVER
源像素绘制在目标像素上
XOR
丢弃源像素覆盖目标像素的源像素和目标像素,绘制剩余的源像素
源码理解
在使用Paint画笔的时候,我们可以通过设置Xfermode来对两个图像像素按照一定的规则进行混合,形成新的像素,再绘制到canvas上面去。
每个像素点的颜色都是由四个分量组成,即RGBA,RGB表示的是颜色(红绿蓝),A表示的是我们Alpha值,在Xfermode中,
alpha值为αout,颜色值被标注为Cout。
我们大概可总结一下几个重要点
:
- alpha——透明度
- C——颜色值
- src——原图像
- dst——目标图像
- out——输出
混合模式分类
我们由源码可知,PorterDuff.Mode大概可以分为三类:
SRC类
优先显示源图像
- SRC
- SRC_OVER
- SRC_IN
- SRC_OUT
- SRC_ATOP
DST类
优先显示目标图像
- DST
- DST_OVER
- DST_IN
- DST_OUT
- DST_ATOP
其他类
其它的叠加效果
- CLEAR
- XOR
- DARKEN
- LIGHTEN
- MULTIPLY
- SCREEN
- ADD
- OVERLAY
Demo测试
测试环境为:Android 8.1
谷歌官方demo十六种组合效果:
官方代码
代码为:
public class Xfermodes extends GraphicsActivity {// create a bitmap with a circle, used for the "dst" imagestatic Bitmap makeDst(int w, int h) {Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);Canvas c = new Canvas(bm);Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);p.setColor(0xFFFFCC44);c.drawOval(new RectF(0, 0, w*3/4, h*3/4), p);return bm;}// create a bitmap with a rect, used for the "src" imagestatic Bitmap makeSrc(int w, int h) {Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);Canvas c = new Canvas(bm);Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);p.setColor(0xFF66AAFF);c.drawRect(w/3, h/3, w*19/20, h*19/20, p);return bm;}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(new SampleView(this));}private static class SampleView extends View {private static final int W = 64;private static final int H = 64;private static final int ROW_MAX = 4; // number of samples per rowprivate Bitmap mSrcB;private Bitmap mDstB;private Shader mBG; // background checker-board patternprivate static final Xfermode[] sModes = {new PorterDuffXfermode(PorterDuff.Mode.CLEAR),new PorterDuffXfermode(PorterDuff.Mode.SRC),new PorterDuffXfermode(PorterDuff.Mode.DST),new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),new PorterDuffXfermode(PorterDuff.Mode.DST_OVER),new PorterDuffXfermode(PorterDuff.Mode.SRC_IN),new PorterDuffXfermode(PorterDuff.Mode.DST_IN),new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),new PorterDuffXfermode(PorterDuff.Mode.DST_OUT),new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),new PorterDuffXfermode(PorterDuff.Mode.XOR),new PorterDuffXfermode(PorterDuff.Mode.DARKEN),new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY),new PorterDuffXfermode(PorterDuff.Mode.SCREEN)};private static final String[] sLabels = {"Clear", "Src", "Dst", "SrcOver","DstOver", "SrcIn", "DstIn", "SrcOut","DstOut", "SrcATop", "DstATop", "Xor","Darken", "Lighten", "Multiply", "Screen"};public SampleView(Context context) {super(context);mSrcB = makeSrc(W, H);mDstB = makeDst(W, H);// make a ckeckerboard patternBitmap bm = Bitmap.createBitmap(new int[] { 0xFFFFFFFF, 0xFFCCCCCC,0xFFCCCCCC, 0xFFFFFFFF }, 2, 2,Bitmap.Config.RGB_565);mBG = new BitmapShader(bm,Shader.TileMode.REPEAT,Shader.TileMode.REPEAT);Matrix m = new Matrix();m.setScale(6, 6);mBG.setLocalMatrix(m);}@Override protected void onDraw(Canvas canvas) {canvas.drawColor(Color.WHITE);Paint labelP = new Paint(Paint.ANTI_ALIAS_FLAG);labelP.setTextAlign(Paint.Align.CENTER);Paint paint = new Paint();paint.setFilterBitmap(false);canvas.translate(15, 35);int x = 0;int y = 0;for (int i = 0; i < sModes.length; i++) {// draw the borderpaint.setStyle(Paint.Style.STROKE);paint.setShader(null);canvas.drawRect(x - 0.5f, y - 0.5f,x + W + 0.5f, y + H + 0.5f, paint);// draw the checker-board patternpaint.setStyle(Paint.Style.FILL);paint.setShader(mBG);canvas.drawRect(x, y, x + W, y + H, paint);// draw the src/dst example into our offscreen bitmapint sc = canvas.saveLayer(x, y, x + W, y + H, null,Canvas.MATRIX_SAVE_FLAG |Canvas.CLIP_SAVE_FLAG |Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |Canvas.FULL_COLOR_LAYER_SAVE_FLAG |Canvas.CLIP_TO_LAYER_SAVE_FLAG);canvas.translate(x, y);canvas.drawBitmap(mDstB, 0, 0, paint);paint.setXfermode(sModes[i]);canvas.drawBitmap(mSrcB, 0, 0, paint);paint.setXfermode(null);canvas.restoreToCount(sc);// draw the labelcanvas.drawText(sLabels[i],x + W/2, y - labelP.getTextSize()/2, labelP);x += W + 10;// wrap around when we've drawn enough for one rowif ((i % ROW_MAX) == ROW_MAX - 1) {x = 0;y += H + 30;}}}}
}
自定义View
我们将其封装成一个View
public class XFerModesSampleView extends View {private static int W = 200;private static int H = 200;private static final int ROW_MAX = 4; // number of samples per rowprivate Bitmap mSrcB;private Bitmap mDstB;private Shader mBG; // background checker-board patternprivate static final Xfermode[] sModes = {new PorterDuffXfermode(PorterDuff.Mode.CLEAR),new PorterDuffXfermode(PorterDuff.Mode.SRC),new PorterDuffXfermode(PorterDuff.Mode.DST),new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),new PorterDuffXfermode(PorterDuff.Mode.DST_OVER),new PorterDuffXfermode(PorterDuff.Mode.SRC_IN),new PorterDuffXfermode(PorterDuff.Mode.DST_IN),new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),new PorterDuffXfermode(PorterDuff.Mode.DST_OUT),new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),new PorterDuffXfermode(PorterDuff.Mode.XOR),new PorterDuffXfermode(PorterDuff.Mode.DARKEN),new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY),new PorterDuffXfermode(PorterDuff.Mode.SCREEN)};private static final String[] sLabels = {"Clear", "Src", "Dst", "SrcOver","DstOver", "SrcIn", "DstIn", "SrcOut","DstOut", "SrcATop", "DstATop", "Xor","Darken", "Lighten", "Multiply", "Screen"};public XFerModesSampleView(Context context) {super(context);init();}private void init() {if(Build.VERSION.SDK_INT >= 11){setLayerType(LAYER_TYPE_SOFTWARE, null);}mSrcB = makeSrc(W, H);mDstB = makeDst(W, H);// make a ckeckerboard patternBitmap bm = Bitmap.createBitmap(new int[] { 0xFFFFFFFF, 0xFFCCCCCC,0xFFCCCCCC, 0xFFFFFFFF }, 2, 2,Bitmap.Config.RGB_565);mBG = new BitmapShader(bm,Shader.TileMode.REPEAT,Shader.TileMode.REPEAT);Matrix m = new Matrix();m.setScale(6, 6);mBG.setLocalMatrix(m);}@Override protected void onDraw(Canvas canvas) {canvas.drawColor(Color.WHITE);Paint labelP = new Paint(Paint.ANTI_ALIAS_FLAG);labelP.setTextAlign(Paint.Align.CENTER);Paint paint = new Paint();paint.setFilterBitmap(false);canvas.translate(15, 35);int x = 0;int y = 0;for (int i = 0; i < sModes.length; i++) {// draw the borderpaint.setStyle(Paint.Style.STROKE);paint.setShader(null);canvas.drawRect(x - 0.5f, y - 0.5f,x + W + 0.5f, y + H + 0.5f, paint);// draw the checker-board patternpaint.setStyle(Paint.Style.FILL);paint.setShader(mBG);canvas.drawRect(x, y, x + W, y + H, paint);// draw the src/dst example into our offscreen bitmapint sc = canvas.saveLayer(x, y, x + W, y + H, null,Canvas.ALL_SAVE_FLAG);canvas.translate(x, y);canvas.drawBitmap(mDstB, 0, 0, paint);paint.setXfermode(sModes[i]);canvas.drawBitmap(mSrcB, 0, 0, paint);paint.setXfermode(null);canvas.restoreToCount(sc);// draw the labelcanvas.drawText(sLabels[i],x + W/2, y - labelP.getTextSize()/2, labelP);x += W + 10;// wrap around when we've drawn enough for one rowif ((i % ROW_MAX) == ROW_MAX - 1) {x = 0;y += H + 30;}}}// create a bitmap with a circle, used for the "dst" imagestatic Bitmap makeDst(int w, int h) {Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);Canvas c = new Canvas(bm);Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);p.setColor(0xFFFFCC44);c.drawOval(new RectF(0, 0, w*3/4, h*3/4), p);return bm;}// create a bitmap with a rect, used for the "src" imagestatic Bitmap makeSrc(int w, int h) {Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);Canvas c = new Canvas(bm);Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);p.setColor(0xFF66AAFF);c.drawRect(w/3, h/3, w*19/20, h*19/20, p);return bm;}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);H = W = (int) (w / 4.5f);}
}
与官方Demo的差异
我们可以看到增加了下面代码:
if(Build.VERSION.SDK_INT >= 11){setLayerType(LAYER_TYPE_SOFTWARE, null);
}
若不增加这句代码,出现的结果为:
而增加这句代码,出现的结果为:
可以看到不增加上面代码的话,Clear,Darken,Lighten这三种与官方描述的不一致,而增加上面代码的话,与官方描述是一样的,那么这是什么原因引起的呢?
setLayerType作用:
指定支持此视图的图层的类型。 该图层可以是LAYER_TYPE_NONE,LAYER_TYPE_SOFTWARE或LAYER_TYPE_HARDWARE。
一个图层与一个可选的Paint实例相关联,该实例控制图层在屏幕上的组成方式。 组成图层时考虑以下涂料属性:
半透明(alpha)
混合模式
彩色滤光片
如果通过调用setAlpha(float)将此视图的alpha值设置为<1.0,则该图层的alpha值将被此视图的alpha值所取代。
LAYER_TYPE_SOFTWARE作用:
表示该视图具有软件层。一个软件层由一个bitmap支持,并使视图使用Android的软件渲染管道渲染,即使启用了硬件加速。
软件层有各种用途:
当应用程序未使用硬件加速时,软件层对于将特定颜色过滤器和/或混合模式和/或半透明应用于视图及其所有子项很有用。
当应用程序使用硬件加速时,软件层可用于渲染硬件加速管道不支持的绘制原语。它也可用于将复杂的视图树缓存到纹理中,并降低绘制操作的复杂性。例如,当使用翻译对复杂视图树进行动画处理时,可以使用软件层仅渲染一次视图树。
受影响的视图树经常更新时应避免软件层。每次更新都需要重新渲染软件层,这可能会很慢(特别是当硬件加速打开时,因为在每次更新后必须将图层上载到硬件纹理中)。
即调用这句代码意思为对混合模式起作用;
要注意的地方
注意,我们要想调用Xfermode生效,须按照以下顺序进行draw:
即先画目标bitmap,在调用setXfermode,再画源bitmap,若调用位置相反,则会显示相反的效果。
刮刮卡效果实现
我们知道了Xfermode的作用,那么我们可以运用一下,来实现一个刮刮卡的效果,我们将
设置为底图,紧接着我们创建一个图层,在这个图层上画了一个新建的bitmap,并将其作为目标图,若手指移动的话,则在目标图上绘制在目标图上进行手指轨迹的移动,因为我们新建的bitmap为透明底,因此手指轨迹实为图像的内容,根据PorterDuff.Mode.SRC_OUT
的特性,会将src和dst图像相交的像素点过滤且显示src图像,这时会漏出底图即”恭喜你中了一等奖”的图像,因此实现了刮刮卡的效果。
完整代码
public class ScratchCardView extends View {private Paint paint;private Bitmap dstBitmap, srcBitmap, realRewardBitmap;private Path path;private float touchX, touchY;public ScratchCardView(Context context) {super(context);setLayerType(View.LAYER_TYPE_SOFTWARE, null);paint = new Paint();paint.setColor(Color.GREEN);paint.setStyle(Paint.Style.STROKE);paint.setStrokeWidth(40);//真实的奖励图realRewardBitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.scratch_2,null);//原图,即要刮走的图srcBitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.scratch_1,null);//目标图dstBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), Bitmap.Config.ARGB_8888);path = new Path();}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.drawBitmap(realRewardBitmap,0,0, paint); //画底图int count = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG); //创建画布new Canvas(dstBitmap).drawPath(path, paint); //新建一个canvas,将手指轨迹画到目标Bitmap上canvas.drawBitmap(dstBitmap,0,0, paint);//将目标图像画到画布上paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT)); //设置图像混合模式canvas.drawBitmap(srcBitmap,0,0,paint); //将最上面的刮刮奖图片画到画布上paint.setXfermode(null); //清空Xfermodecanvas.restoreToCount(count);}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()){case MotionEvent.ACTION_DOWN:path.moveTo(event.getX(),event.getY());touchX = event.getX();touchY = event.getY();return true;case MotionEvent.ACTION_MOVE:float endX = (touchX + event.getX()) / 2;float endY = (touchY + event.getY()) / 2;path.quadTo(touchX,touchY,endX,endY);touchX = event.getX();touchY = event.getY();break;case MotionEvent.ACTION_UP:break;}invalidate();return super.onTouchEvent(event);}
}
效果图:
Demo地址
Demo地址:https://github.com/samlss/PaintXfermode
个人总结:https://github.com/samlss/AsAndroidDevelop
这篇关于Android Paint系列之Xfermode + 刮刮卡效果实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!