菜鸡wing遇敌PorterDuffXferMode,不料过于轻敌,应战吃力。随后与其大战三天三夜,三百余回合不分胜负。幸得 @咪咪控 相助,侥幸获胜。

    关键字:PorterDuffXferMode 错误 不正确 不达到预期 bug

    上一篇带来一个使用PorterDuffXferMode 做的 水波纹loadingview,中间遇到了点小困难。

    (说人话) PorterDuffXferMode总是不能按照效果图预期的效果执行。关于PorterDuffXferMode的错误显示是一个对初学者十分深的坑,到底是bug呢,还是有需要注意的地方呢。这里就跟随我 带上手电筒,去一探究竟。

    转载请注明出处:http://blog.csdn.net/wingichoy/article/details/50534175

    首先,大家都知道有一个图片:

    有坑?? 为何wing坠入PorterDuffXferMode的万丈深渊(PorterDuffXferMode深入试验) - 图1

    然后,大部分时候 是看到了觉得很神奇,就跃跃欲试,尤其是src_in 和dstIn可以实现遮罩效果,例如圆角图片 圆形图片都用了这种模式。

    于是就挨个测试各种模式是否生效,结果往往不能达到预期效果。我们来做个测试。

    从最简单的开始:

    1.直接在canvas上面绘制图形

    1. @Override
    2. protected void onDraw(Canvas canvas) {
    3. //dst
    4. canvas.drawRect(20,20,80,80,mDstPaint);
    5. //src
    6. canvas.drawCircle(30,30,30,mSrcPaint);
    7. }

    原图效果是这样的:

    有坑?? 为何wing坠入PorterDuffXferMode的万丈深渊(PorterDuffXferMode深入试验) - 图2

    现在加一个mode上来,XOR

    1. @Override
    2. protected void onDraw(Canvas canvas) {
    3. //dst
    4. canvas.drawRect(20,20,80,80,mDstPaint);
    5. mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
    6. //src
    7. canvas.drawCircle(30,30,30,mSrcPaint);
    8. }

    跑起来的结果是这样的:

    有坑?? 为何wing坠入PorterDuffXferMode的万丈深渊(PorterDuffXferMode深入试验) - 图3

    WTF!!?? 这是什么鬼。不应该是相交部分消失吗。 网上说“硬件加速”对这个有影响,那么在构造器里关闭硬件加速试一下:

    1. public TestView(Context context, AttributeSet attrs, int defStyleAttr) {
    2. super(context, attrs, defStyleAttr);
    3. mDstPaint = new Paint();
    4. mSrcPaint = new Paint();
    5. mDstPaint.setColor(Color.YELLOW);
    6. mSrcPaint.setColor(Color.BLUE);
    7. setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    8. }

    运行的结果:

    有坑?? 为何wing坠入PorterDuffXferMode的万丈深渊(PorterDuffXferMode深入试验) - 图4

    这下正常了。相交的部分消失了。

    结论1:硬件加速对PorterDuffXferMode有影响,使用前请关闭硬件加速。

    那么这下真的天下太平了吗?nonono~不要太天真,不然怎么能叫万丈深渊呢。

    继续试验其他模式: 将模式改为SRC_IN

    有坑?? 为何wing坠入PorterDuffXferMode的万丈深渊(PorterDuffXferMode深入试验) - 图5

    WTF?????跟效果图根本不一致好吗!!!! 在试试DST_IN

    有坑?? 为何wing坠入PorterDuffXferMode的万丈深渊(PorterDuffXferMode深入试验) - 图6

    你确定你没有在逗我???? 怎么是这个鬼东西。 (当时鼓捣了我三天四夜,一直在日狗,不过先别急,慢慢来。)

    为什么一定要按照那个效果图来呢。。。 因为特么的那个图是官方的一个demo。。 那么我们就来看看这个demo长什么样!

    1. package io.appium.android.apis.graphics;
    2. import android.content.Context;
    3. import android.graphics.Bitmap;
    4. import android.graphics.BitmapShader;
    5. import android.graphics.Canvas;
    6. import android.graphics.Color;
    7. import android.graphics.Matrix;
    8. import android.graphics.Paint;
    9. import android.graphics.PorterDuff;
    10. import android.graphics.PorterDuffXfermode;
    11. import android.graphics.RectF;
    12. import android.graphics.Shader;
    13. import android.graphics.Xfermode;
    14. import android.os.Bundle;
    15. import android.view.View;
    16. public class Xfermodes extends GraphicsActivity {
    17. // create a bitmap with a circle, used for the "dst" image
    18. static Bitmap makeDst(int w, int h) {
    19. Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
    20. Canvas c = new Canvas(bm);
    21. Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
    22. p.setColor(0xFFFFCC44);
    23. c.drawOval(new RectF(0, 0, w*3/4, h*3/4), p);
    24. return bm;
    25. }
    26. // create a bitmap with a rect, used for the "src" image
    27. static Bitmap makeSrc(int w, int h) {
    28. Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
    29. Canvas c = new Canvas(bm);
    30. Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
    31. p.setColor(0xFF66AAFF);
    32. c.drawRect(w/3, h/3, w*19/20, h*19/20, p);
    33. return bm;
    34. }
    35. @Override
    36. protected void onCreate(Bundle savedInstanceState) {
    37. super.onCreate(savedInstanceState);
    38. setContentView(new SampleView(this));
    39. }
    40. private static class SampleView extends View {
    41. private static final int W = 64;
    42. private static final int H = 64;
    43. private static final int ROW_MAX = 4; // number of samples per row
    44. private Bitmap mSrcB;
    45. private Bitmap mDstB;
    46. private Shader mBG; // background checker-board pattern
    47. private static final Xfermode[] sModes = {
    48. new PorterDuffXfermode(PorterDuff.Mode.CLEAR),
    49. new PorterDuffXfermode(PorterDuff.Mode.SRC),
    50. new PorterDuffXfermode(PorterDuff.Mode.DST),
    51. new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),
    52. new PorterDuffXfermode(PorterDuff.Mode.DST_OVER),
    53. new PorterDuffXfermode(PorterDuff.Mode.SRC_IN),
    54. new PorterDuffXfermode(PorterDuff.Mode.DST_IN),
    55. new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),
    56. new PorterDuffXfermode(PorterDuff.Mode.DST_OUT),
    57. new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),
    58. new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),
    59. new PorterDuffXfermode(PorterDuff.Mode.XOR),
    60. new PorterDuffXfermode(PorterDuff.Mode.DARKEN),
    61. new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),
    62. new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY),
    63. new PorterDuffXfermode(PorterDuff.Mode.SCREEN)
    64. };
    65. private static final String[] sLabels = {
    66. "Clear", "Src", "Dst", "SrcOver",
    67. "DstOver", "SrcIn", "DstIn", "SrcOut",
    68. "DstOut", "SrcATop", "DstATop", "Xor",
    69. "Darken", "Lighten", "Multiply", "Screen"
    70. };
    71. public SampleView(Context context) {
    72. super(context);
    73. mSrcB = makeSrc(W, H);
    74. mDstB = makeDst(W, H);
    75. // make a ckeckerboard pattern
    76. Bitmap bm = Bitmap.createBitmap(new int[] { 0xFFFFFFFF, 0xFFCCCCCC,
    77. 0xFFCCCCCC, 0xFFFFFFFF }, 2, 2,
    78. Bitmap.Config.RGB_565);
    79. mBG = new BitmapShader(bm,
    80. Shader.TileMode.REPEAT,
    81. Shader.TileMode.REPEAT);
    82. Matrix m = new Matrix();
    83. m.setScale(6, 6);
    84. mBG.setLocalMatrix(m);
    85. }
    86. @Override protected void onDraw(Canvas canvas) {
    87. canvas.drawColor(Color.WHITE);
    88. Paint labelP = new Paint(Paint.ANTI_ALIAS_FLAG);
    89. labelP.setTextAlign(Paint.Align.CENTER);
    90. Paint paint = new Paint();
    91. paint.setFilterBitmap(false);
    92. canvas.translate(15, 35);
    93. int x = 0;
    94. int y = 0;
    95. for (int i = 0; i < sModes.length; i++) {
    96. // draw the border
    97. paint.setStyle(Paint.Style.STROKE);
    98. paint.setShader(null);
    99. canvas.drawRect(x - 0.5f, y - 0.5f,
    100. x + W + 0.5f, y + H + 0.5f, paint);
    101. // draw the checker-board pattern
    102. paint.setStyle(Paint.Style.FILL);
    103. paint.setShader(mBG);
    104. canvas.drawRect(x, y, x + W, y + H, paint);
    105. // draw the src/dst example into our offscreen bitmap
    106. int sc = canvas.saveLayer(x, y, x + W, y + H, null,
    107. Canvas.MATRIX_SAVE_FLAG |
    108. Canvas.CLIP_SAVE_FLAG |
    109. Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
    110. Canvas.FULL_COLOR_LAYER_SAVE_FLAG |
    111. Canvas.CLIP_TO_LAYER_SAVE_FLAG);
    112. canvas.translate(x, y);
    113. canvas.drawBitmap(mDstB, 0, 0, paint);
    114. paint.setXfermode(sModes[i]);
    115. canvas.drawBitmap(mSrcB, 0, 0, paint);
    116. paint.setXfermode(null);
    117. canvas.restoreToCount(sc);
    118. // draw the label
    119. canvas.drawText(sLabels[i],
    120. x + W/2, y - labelP.getTextSize()/2, labelP);
    121. x += W + 10;
    122. // wrap around when we've drawn enough for one row
    123. if ((i % ROW_MAX) == ROW_MAX - 1) {
    124. x = 0;
    125. y += H + 30;
    126. }
    127. }
    128. }
    129. }
    130. }

    一点一点看,截取onDraw里面的片段,这里

    1. canvas.drawBitmap(mDstB, 0, 0, paint);
    2. paint.setXfermode(sModes[i]);
    3. canvas.drawBitmap(mSrcB, 0, 0, paint);
    4. paint.setXfermode(null);

    他是画了两个bitmap。网上有人说好像只对bitmap生效,那到底是不是这样呢。我们来试验一下。

    我们新定义一个canvas 再定义一个bitmap 现在bitmap上画出来src 然后将bitmap画到canvas上:

    1. public TestView(Context context, AttributeSet attrs, int defStyleAttr) {
    2. super(context, attrs, defStyleAttr);
    3. mDstPaint = new Paint();
    4. mSrcPaint = new Paint();
    5. mDstPaint.setColor(Color.YELLOW);
    6. mSrcPaint.setColor(Color.BLUE);
    7. setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    8. mSrcBitmap = Bitmap.createBitmap(50,50, Bitmap.Config.ARGB_8888);
    9. mCanvas = new Canvas(mSrcBitmap);
    10. }
    1. @Override
    2. protected void onDraw(Canvas canvas) {
    3. //dst
    4. canvas.drawRect(20,20,80,80,mDstPaint);
    5. //src
    6. // mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
    7. mCanvas.drawCircle(25,25,25,mSrcPaint);
    8. canvas.drawBitmap(mSrcBitmap,0,0,null);
    9. }

    现在的效果是这样的:

    有坑?? 为何wing坠入PorterDuffXferMode的万丈深渊(PorterDuffXferMode深入试验) - 图7

    加一个XOR 试试。。

    日了狗了!!!!!没反应啊,到底是什么鬼。

    有坑?? 为何wing坠入PorterDuffXferMode的万丈深渊(PorterDuffXferMode深入试验) - 图8

    是不是两个都需要bitmap才可以呢,再创建一个dstBitmap和dstCanvas?

    1. mDstBitmap = Bitmap.createBitmap(50,50, Bitmap.Config.ARGB_8888);
    2. mDstCanvas = new Canvas(mDstBitmap);

    加一个XOR 试试

    1. @Override
    2. protected void onDraw(Canvas canvas) {
    3. //dst
    4. mDstCanvas.drawRect(20,20,80,80,mDstPaint);
    5. canvas.drawBitmap(mDstBitmap,0,0,mDstPaint);
    6. //src
    7. mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
    8. mSrcCanvas.drawCircle(25,25,25,mSrcPaint);
    9. canvas.drawBitmap(mSrcBitmap,0,0,mSrcPaint);
    10. }

    效果如下:

    有坑?? 为何wing坠入PorterDuffXferMode的万丈深渊(PorterDuffXferMode深入试验) - 图9

    终于他妈的出来了!!!!那其他效果呢?

    clear

    有坑?? 为何wing坠入PorterDuffXferMode的万丈深渊(PorterDuffXferMode深入试验) - 图10

    一致的!!!!好激动有没有!!!!搞了4天 越来越接近结论了!!!

    结论2:只有两个bitmap的时候,才可以生效。

    不要高兴太早。。如果坑到这里就完了,那还叫坑么。

    继续试。。嗯 好多模式都是一致的。

    直到!!!SRC_IN和DST_IN ,会发现。。。都消失了。 为毛呢??

    检查代码 发现 在往bitmap画圆之前就set了mode 这样会有影响

    纠正

    1. @Override
    2. protected void onDraw(Canvas canvas) {
    3. //dst
    4. mDstCanvas.drawRect(20,20,80,80,mDstPaint);
    5. canvas.drawBitmap(mDstBitmap,0,0,mDstPaint);
    6. //src
    7. mSrcCanvas.drawCircle(25,25,25,mSrcPaint);
    8. //再画圆之后 设置mode
    9. mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
    10. canvas.drawBitmap(mSrcBitmap,0,0,mSrcPaint);
    11. }

    发现

    有坑?? 为何wing坠入PorterDuffXferMode的万丈深渊(PorterDuffXferMode深入试验) - 图11

    艹!!!!!!终于好了!!!!!!!!经过不懈努力!!!撒花!★,°:.☆( ̄▽ ̄)/$:.°★

    其实我们刚才bitmap的大小是一样的。 然后都是从0,0开始 完全覆盖了。

    那么错开一点点 是什么效果呢,调整代码如下

    1. protected void onDraw(Canvas canvas) {
    2. //dst
    3. mDstCanvas.drawRect(20,20,80,80,mDstPaint);
    4. canvas.drawBitmap(mDstBitmap,20,20,mDstPaint);
    5. //src
    6. mSrcCanvas.drawCircle(25,25,25,mSrcPaint);
    7. mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
    8. canvas.drawBitmap(mSrcBitmap,0,0,mSrcPaint);
    9. }

    效果如下:

    有坑?? 为何wing坠入PorterDuffXferMode的万丈深渊(PorterDuffXferMode深入试验) - 图12

    可以看有效果了!!!! 但是是一个什么鬼!!! 矩形缺角??加蓝色一点??

    这样看是很难看出效果的。。来调整一下bitmap的底色 和矩形的大小:

    把两个bitmap的底色都画成灰色, 矩形不要占满画布 留出空间

    1. mDstCanvas.drawColor(Color.GRAY);
    1. @Override
    2. protected void onDraw(Canvas canvas) {
    3. //dst 黄色
    4. mDstCanvas.drawRect(0,0,40,40,mDstPaint);
    5. canvas.drawBitmap(mDstBitmap,20,20,mDstPaint);
    6. //src 蓝色
    7. mSrcCanvas.drawCircle(25,25,25,mSrcPaint);
    8. canvas.drawBitmap(mSrcBitmap,0,0,mSrcPaint);
    9. }

    效果如下。。 嗯 这样就是两个bitmap了。。很明了 bitmap的内容 位置,

    有坑?? 为何wing坠入PorterDuffXferMode的万丈深渊(PorterDuffXferMode深入试验) - 图13

    然后再搞SRC_IN模式

    有坑?? 为何wing坠入PorterDuffXferMode的万丈深渊(PorterDuffXferMode深入试验) - 图14

    dst_in

    有坑?? 为何wing坠入PorterDuffXferMode的万丈深渊(PorterDuffXferMode深入试验) - 图15

    那么把bitmap底色去掉。 改成透明的呢?

    dst_in:

    有坑?? 为何wing坠入PorterDuffXferMode的万丈深渊(PorterDuffXferMode深入试验) - 图16

    SRC_IN:

    有坑?? 为何wing坠入PorterDuffXferMode的万丈深渊(PorterDuffXferMode深入试验) - 图17

    总结3:两个bitmap位置不完全重叠的效果如上,并不能总结出效果,要按实际效果来。

    ————————————————————————————————————————————————————-华丽丽的分割线————————————————————————————————————————————-

    最终大总结,如果想让PorterDuffXferMode按照预期Demo(或者效果图)的效果图像实现,必须满足以下条件:

    1、关闭硬件加速。

    2、两个bitmap大小尽量一样。

    3、背景色为透明色。

    4、如果两个bitmap位置不完全一样,可能也是预期效果,只不过你看到的效果和你自己脑补的预期效果不一致。

    最后想再说几句。鼓捣这个模式鼓捣了几乎一周,每天晚上下班都在搞。查了无数资料。但是好多不完整,甚至有一些误导性。所以为了避免后来者入坑。亲自试验,尽量总结。 如果有说的不正确的地方请及时向我提出。我会及时改正。

    如果本文帮助到了你,请点一个顶,或者评论一下,蟹蟹!!!!