当前位置:   article > 正文

Paint API —— setXfermode() 详解_paint.setxfermode

paint.setxfermode

效果展示

 API解释

  1. setXfermode(Xfermode xfermode)
  2. @param xfermode May be null. The xfermode to be installed in the paint
  3. 译文:xfermode 可以为空,并且必须安装到画笔上
  4. Xfermode对象由 PorterDuffXfermode(PorterDuff.Mode mode)构造方法产生
  5. PorterDuff.Mode mode 有18
  6. 使用步骤:
  7. 第一步:绘制 目标(dst)图
  8. 第二步: 给 画笔 设置模式
  9. 第三步:绘制 源 (src) 图
  10. 第四步:清除 画笔 模式
  11. 注意: 绘制 dst 和 src 的时候要指定画笔 并且是设置了模式的同一个画笔

有兄弟说上面只有16种,对,说明你数学很好。

还有两种是 新增的,分别为 ADDOVERLAY两种模式

模式讲解

1)PorterDuff.Mode.ADD:

 饱和度叠加

2)PorterDuff.Mode.CLEAR:

 清除

3)PorterDuff.Mode.DARKEN:

 取两图层全部区域,交集部分颜色加深

4)PorterDuff.Mode.DST:

 只保留目标图的alpha和color,所以绘制出来只有目标图

5)PorterDuff.Mode.DST_ATOP:

 源图和目标图相交处绘制目标图,不相交的地方绘制源图

6)PorterDuff.Mode.DST_IN:

 两者相交的地方绘制目标图,绘制的效果会受到原图处的透明度影响

7)PorterDuff.Mode.DST_OUT:

 在不相交的地方绘制目标图

8)PorterDuff.Mode.DST_OVER:

 目标图绘制在上方

9)PorterDuff.Mode.LIGHTEN:

 取两图层全部区域,点亮交集部分颜色

10)PorterDuff.Mode.MULTIPLY:

 取两图层交集部分叠加后颜色

11)PorterDuff.Mode.OVERLAY:

 叠加

12)PorterDuff.Mode.SCREEN:

 取两图层全部区域,交集部分变为透明色

13)PorterDuff.Mode.SRC:

 只保留源图像的alpha和color,所以绘制出来只有源图

14)PorterDuff.Mode.SRC_ATOP:

 源图和目标图相交处绘制源图,不相交的地方绘制目标图

15)PorterDuff.Mode.SRC_IN:

 两者相交的地方绘制源图

16)PorterDuff.Mode.SRC_OUT:

 不相交的地方绘制源图

17)PorterDuff.Mode.SRC_OVER:

 把源图绘制在上方

18)PorterDuff.Mode.XOR:

 不相交的地方按原样绘制源图和目标图

 代码展示

自定义布局java代码

  1. package com.wust.xfmode;
  2. import android.content.Context;
  3. import android.graphics.Bitmap;
  4. import android.graphics.Canvas;
  5. import android.graphics.Paint;
  6. import android.graphics.PorterDuff;
  7. import android.graphics.PorterDuffXfermode;
  8. import android.graphics.RectF;
  9. import android.util.AttributeSet;
  10. import android.util.DisplayMetrics;
  11. import android.view.View;
  12. import androidx.annotation.Nullable;
  13. /**
  14. * ClassName: MyXfModeView <br/>
  15. * Description: <br/>
  16. * date: 2021/8/9 17:29<br/>
  17. *
  18. * @author yiqi<br />
  19. * @QQ 1820762465
  20. * @微信 yiqiideallife
  21. * @技术交流QQ群 928023749
  22. */
  23. public class MyXfModeView extends View {
  24. private Paint paint;
  25. private Bitmap bitmap;
  26. //定义模式
  27. private PorterDuffXfermode pdXfermode;
  28. private Bitmap dstBm;
  29. private Bitmap srcBm;
  30. private int screenWidth;
  31. private int screenHeight;
  32. //定义模式 Array
  33. private PorterDuff.Mode[] modes = {
  34. PorterDuff.Mode.ADD,PorterDuff.Mode.CLEAR,PorterDuff.Mode.DARKEN,PorterDuff.Mode.DST,PorterDuff.Mode.DST_ATOP,
  35. PorterDuff.Mode.DST_IN,PorterDuff.Mode.DST_OUT,PorterDuff.Mode.DST_OVER,PorterDuff.Mode.LIGHTEN,PorterDuff.Mode.MULTIPLY,
  36. PorterDuff.Mode.OVERLAY,PorterDuff.Mode.SCREEN,PorterDuff.Mode.SRC,PorterDuff.Mode.SRC_ATOP,PorterDuff.Mode.SRC_IN,
  37. PorterDuff.Mode.SRC_OUT,PorterDuff.Mode.SRC_OVER,PorterDuff.Mode.XOR
  38. };
  39. private int mCurMode = 0;
  40. public MyXfModeView(Context context) {
  41. super(context);
  42. }
  43. public MyXfModeView(Context context, @Nullable AttributeSet attrs) {
  44. super(context, attrs);
  45. initPaint();
  46. initData();
  47. }
  48. public MyXfModeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
  49. super(context, attrs, defStyleAttr);
  50. initPaint();
  51. initData();
  52. }
  53. private void initData() {
  54. //获取屏幕宽高
  55. DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
  56. screenWidth = displayMetrics.widthPixels;
  57. screenHeight = displayMetrics.heightPixels;
  58. //获取两张 bm 图
  59. dstBm = makeDst(screenWidth, screenHeight);
  60. srcBm = makeSrc(screenWidth, screenHeight);
  61. }
  62. private void initPaint() {
  63. paint = new Paint();
  64. paint.setAntiAlias(true);
  65. paint.setDither(true);
  66. paint.setStyle(Paint.Style.FILL);
  67. }
  68. @Override
  69. protected void onDraw(Canvas canvas) {
  70. //绘制圆图形
  71. canvas.drawBitmap(dstBm,0,0,null);
  72. canvas.drawBitmap(srcBm,0,0,null);
  73. //绘制分界线
  74. canvas.drawLine(0,screenHeight*2/4,screenWidth,screenHeight*2/4,paint);
  75. //创建图层 sc保存着创建图层前的画布状态
  76. RectF bounds = new RectF(0,screenHeight/2,screenWidth,screenHeight);
  77. int sc = canvas.saveLayer(bounds, null);
  78. //必须先 绘制 目标 bm
  79. canvas.drawBitmap(dstBm,0,screenHeight/2,paint);
  80. //然后 设置模式
  81. pdXfermode = new PorterDuffXfermode(modes[mCurMode]);
  82. paint.setXfermode(pdXfermode);
  83. //绘制 目标 bm
  84. canvas.drawBitmap(srcBm,0,screenHeight/2,paint);
  85. //一定要记得 模式用完之后去除
  86. paint.setXfermode(null);
  87. canvas.restoreToCount(sc);
  88. }
  89. //创建目标 bm 圆形
  90. private Bitmap makeDst(int w,int h){
  91. Bitmap bitmap = Bitmap.createBitmap(w,h, Bitmap.Config.ARGB_8888);
  92. Canvas canvas = new Canvas(bitmap);
  93. Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
  94. p.setColor(0xFF26AAD1);
  95. canvas.drawOval(w * 1 / 4, w * 1 / 4,w * 1 / 2, w * 1 / 2,p);
  96. return bitmap;
  97. }
  98. //创建源 bm 矩形
  99. private Bitmap makeSrc(int w,int h){
  100. Bitmap bitmap = Bitmap.createBitmap(w,h, Bitmap.Config.ARGB_8888);
  101. Canvas canvas = new Canvas(bitmap);
  102. Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
  103. p.setColor(0xFFFFCE43);
  104. canvas.drawRect(w *3/ 8, w *3/ 8, w * 5/8, w *5/8, p);
  105. return bitmap;
  106. }
  107. //暴露设置模式的方法
  108. public void setMode(int mode){
  109. this.mCurMode = mode;
  110. invalidate();
  111. }
  112. }

xml布局代码

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. tools:context=".MainActivity">
  8. <TextView
  9. android:id="@+id/tv_dscript"
  10. android:layout_centerInParent="true"
  11. android:layout_width="match_parent"
  12. android:layout_height="180dp"
  13. android:paddingTop="90dp"
  14. android:textColor="#F15A5A"
  15. android:gravity="center"/>
  16. <com.wust.xfmode.MyXfModeView
  17. android:id="@+id/mxmv_xfmode"
  18. android:layout_width="match_parent"
  19. android:layout_height="match_parent"/>
  20. </RelativeLayout>

MainActivity.java

  1. package com.wust.xfmode;
  2. import androidx.annotation.NonNull;
  3. import androidx.appcompat.app.AppCompatActivity;
  4. import android.os.Bundle;
  5. import android.util.DisplayMetrics;
  6. import android.view.Menu;
  7. import android.view.MenuItem;
  8. import android.view.View;
  9. import android.view.WindowManager;
  10. import android.widget.TextView;
  11. public class MainActivity extends AppCompatActivity {
  12. private MyXfModeView mxmv_xfmode;
  13. private TextView tv_dscript;
  14. @Override
  15. protected void onCreate(Bundle savedInstanceState) {
  16. super.onCreate(savedInstanceState);
  17. setContentView(R.layout.activity_main);
  18. bindView();
  19. bindData();
  20. }
  21. private void bindData() {
  22. }
  23. private void bindView() {
  24. mxmv_xfmode = findViewById(R.id.mxmv_xfmode);
  25. tv_dscript = findViewById(R.id.tv_dscript);
  26. }
  27. @Override
  28. public boolean onCreateOptionsMenu(Menu menu) {
  29. getMenuInflater().inflate(R.menu.mode_menu,menu);
  30. return super.onCreateOptionsMenu(menu);
  31. }
  32. @Override
  33. public boolean onOptionsItemSelected(@NonNull MenuItem item) {
  34. switch (item.getItemId()){
  35. case R.id.menu_add:
  36. {
  37. mxmv_xfmode.setMode(0);
  38. tv_dscript.setText("PorterDuff.Mode.ADD:饱和度叠加");
  39. }
  40. break;
  41. case R.id.menu_clear:
  42. {
  43. mxmv_xfmode.setMode(1);
  44. tv_dscript.setText("PorterDuff.Mode.CLEAR:清除");
  45. }
  46. break;
  47. case R.id.menu_darken:
  48. {
  49. mxmv_xfmode.setMode(2);
  50. tv_dscript.setText("PorterDuff.Mode.DARKEN:取两图层全部区域,交集部分颜色加深");
  51. }
  52. break;
  53. case R.id.menu_dst:
  54. {
  55. mxmv_xfmode.setMode(3);
  56. tv_dscript.setText("PorterDuff.Mode.DST:只保留目标图的alpha和color,所以绘制出来只有目标图");
  57. }
  58. break;
  59. case R.id.menu_dst_atop:
  60. {
  61. mxmv_xfmode.setMode(4);
  62. tv_dscript.setText("PorterDuff.Mode.DST_ATOP:源图和目标图相交处绘制目标图,不相交的地方绘制源图");
  63. }
  64. break;
  65. case R.id.menu_dst_in:
  66. {
  67. mxmv_xfmode.setMode(5);
  68. tv_dscript.setText("PorterDuff.Mode.DST_IN:两者相交的地方绘制目标图,绘制的效果会受到原图处的透明度影响");
  69. }
  70. break;
  71. case R.id.menu_dst_out:
  72. {
  73. mxmv_xfmode.setMode(6);
  74. tv_dscript.setText("PorterDuff.Mode.DST_OUT:在不相交的地方绘制目标图");
  75. }
  76. break;
  77. case R.id.menu_dst_over:
  78. {
  79. mxmv_xfmode.setMode(7);
  80. tv_dscript.setText("PorterDuff.Mode.DST_OVER:目标图绘制在上方");
  81. }
  82. break;
  83. case R.id.menu_lighten:
  84. {
  85. mxmv_xfmode.setMode(8);
  86. tv_dscript.setText("PorterDuff.Mode.LIGHTEN:取两图层全部区域,点亮交集部分颜色");
  87. }
  88. break;
  89. case R.id.menu_multiply:
  90. {
  91. mxmv_xfmode.setMode(9);
  92. tv_dscript.setText("PorterDuff.Mode.MULTIPLY:取两图层交集部分叠加后颜色");
  93. }
  94. break;
  95. case R.id.menu_overlay:
  96. {
  97. mxmv_xfmode.setMode(10);
  98. tv_dscript.setText("PorterDuff.Mode.OVERLAY:叠加");
  99. }
  100. break;
  101. case R.id.menu_screen:
  102. {
  103. mxmv_xfmode.setMode(11);
  104. tv_dscript.setText("PorterDuff.Mode.SCREEN:取两图层全部区域,交集部分变为透明色");
  105. }
  106. break;
  107. case R.id.menu_src:
  108. {
  109. mxmv_xfmode.setMode(12);
  110. tv_dscript.setText("PorterDuff.Mode.SRC:只保留源图像的alpha和color,所以绘制出来只有源图");
  111. }
  112. break;
  113. case R.id.menu_src_atop:
  114. {
  115. mxmv_xfmode.setMode(13);
  116. tv_dscript.setText("PorterDuff.Mode.SRC_ATOP:源图和目标图相交处绘制源图,不相交的地方绘制目标图");
  117. }
  118. break;
  119. case R.id.menu_src_in:
  120. {
  121. mxmv_xfmode.setMode(14);
  122. tv_dscript.setText("PorterDuff.Mode.SRC_IN:两者相交的地方绘制源图");
  123. }
  124. break;
  125. case R.id.menu_src_out:
  126. {
  127. mxmv_xfmode.setMode(15);
  128. tv_dscript.setText("PorterDuff.Mode.SRC_OUT:不相交的地方绘制源图");
  129. }
  130. break;
  131. case R.id.menu_src_over:
  132. {
  133. mxmv_xfmode.setMode(16);
  134. tv_dscript.setText("PorterDuff.Mode.SRC_OVER:把源图绘制在上方");
  135. }
  136. break;
  137. case R.id.menu_xor:
  138. {
  139. mxmv_xfmode.setMode(17);
  140. tv_dscript.setText("PorterDuff.Mode.XOR:不相交的地方按原样绘制源图和目标图");
  141. }
  142. break;
  143. }
  144. return super.onOptionsItemSelected(item);
  145. }
  146. }

menu.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <menu xmlns:android="http://schemas.android.com/apk/res/android">
  3. <item android:id="@+id/menu_add" android:title="PorterDuff.Mode.ADD"/>
  4. <item android:id="@+id/menu_clear" android:title="PorterDuff.Mode.CLEAR"/>
  5. <item android:id="@+id/menu_darken" android:title="PorterDuff.Mode.DARKEN"/>
  6. <item android:id="@+id/menu_dst" android:title="PorterDuff.Mode.DST"/>
  7. <item android:id="@+id/menu_dst_atop" android:title="PorterDuff.Mode.DST_ATOP"/>
  8. <item android:id="@+id/menu_dst_in" android:title="PorterDuff.Mode.DST_IN"/>
  9. <item android:id="@+id/menu_dst_out" android:title="PorterDuff.Mode.DST_OUT"/>
  10. <item android:id="@+id/menu_dst_over" android:title="PorterDuff.Mode.DST_OVER"/>
  11. <item android:id="@+id/menu_lighten" android:title="PorterDuff.Mode.LIGHTEN"/>
  12. <item android:id="@+id/menu_multiply" android:title="PorterDuff.Mode.MULTIPLY"/>
  13. <item android:id="@+id/menu_overlay" android:title="PorterDuff.Mode.OVERLAY"/>
  14. <item android:id="@+id/menu_screen" android:title="PorterDuff.Mode.SCREEN"/>
  15. <item android:id="@+id/menu_src" android:title="PorterDuff.Mode.SRC"/>
  16. <item android:id="@+id/menu_src_atop" android:title="PorterDuff.Mode.SRC_ATOP"/>
  17. <item android:id="@+id/menu_src_in" android:title="PorterDuff.Mode.SRC_IN"/>
  18. <item android:id="@+id/menu_src_out" android:title="PorterDuff.Mode.SRC_OUT"/>
  19. <item android:id="@+id/menu_src_over" android:title="PorterDuff.Mode.SRC_OVER"/>
  20. <item android:id="@+id/menu_xor" android:title="PorterDuff.Mode.XOR"/>
  21. </menu>

出错点

你在绘制的过程中可能会发现与预期结果不一样,如下:

错误图

正确图

其中的解决方法有两种:

1、给画布添加背景

2、添加图层

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/332621
推荐阅读
相关标签
  

闽ICP备14008679号