Paint API —— setXfermode() 详解
Posted 我想月薪过万
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Paint API —— setXfermode() 详解相关的知识,希望对你有一定的参考价值。
效果展示
API解释
setXfermode(Xfermode xfermode)
@param xfermode May be null. The xfermode to be installed in the paint
译文:xfermode 可以为空,并且必须安装到画笔上
Xfermode对象由 PorterDuffXfermode(PorterDuff.Mode mode)构造方法产生
PorterDuff.Mode mode 有18种
使用步骤:
第一步:绘制 目标(dst)图
第二步: 给 画笔 设置模式
第三步:绘制 源 (src) 图
第四步:清除 画笔 模式
注意: 绘制 dst 和 src 的时候要指定画笔 并且是设置了模式的同一个画笔
有兄弟说上面只有16种,对,说明你数学很好。
还有两种是 新增的,分别为 ADD和OVERLAY两种模式
模式讲解
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代码
package com.wust.xfmode;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import androidx.annotation.Nullable;
/**
* ClassName: MyXfModeView <br/>
* Description: <br/>
* date: 2021/8/9 17:29<br/>
*
* @author yiqi<br />
* @QQ 1820762465
* @微信 yiqiideallife
* @技术交流QQ群 928023749
*/
public class MyXfModeView extends View {
private Paint paint;
private Bitmap bitmap;
//定义模式
private PorterDuffXfermode pdXfermode;
private Bitmap dstBm;
private Bitmap srcBm;
private int screenWidth;
private int screenHeight;
//定义模式 Array
private PorterDuff.Mode[] modes = {
PorterDuff.Mode.ADD,PorterDuff.Mode.CLEAR,PorterDuff.Mode.DARKEN,PorterDuff.Mode.DST,PorterDuff.Mode.DST_ATOP,
PorterDuff.Mode.DST_IN,PorterDuff.Mode.DST_OUT,PorterDuff.Mode.DST_OVER,PorterDuff.Mode.LIGHTEN,PorterDuff.Mode.MULTIPLY,
PorterDuff.Mode.OVERLAY,PorterDuff.Mode.SCREEN,PorterDuff.Mode.SRC,PorterDuff.Mode.SRC_ATOP,PorterDuff.Mode.SRC_IN,
PorterDuff.Mode.SRC_OUT,PorterDuff.Mode.SRC_OVER,PorterDuff.Mode.XOR
};
private int mCurMode = 0;
public MyXfModeView(Context context) {
super(context);
}
public MyXfModeView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initPaint();
initData();
}
public MyXfModeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initPaint();
initData();
}
private void initData() {
//获取屏幕宽高
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
screenWidth = displayMetrics.widthPixels;
screenHeight = displayMetrics.heightPixels;
//获取两张 bm 图
dstBm = makeDst(screenWidth, screenHeight);
srcBm = makeSrc(screenWidth, screenHeight);
}
private void initPaint() {
paint = new Paint();
paint.setAntiAlias(true);
paint.setDither(true);
paint.setStyle(Paint.Style.FILL);
}
@Override
protected void onDraw(Canvas canvas) {
//绘制圆图形
canvas.drawBitmap(dstBm,0,0,null);
canvas.drawBitmap(srcBm,0,0,null);
//绘制分界线
canvas.drawLine(0,screenHeight*2/4,screenWidth,screenHeight*2/4,paint);
//创建图层 sc保存着创建图层前的画布状态
RectF bounds = new RectF(0,screenHeight/2,screenWidth,screenHeight);
int sc = canvas.saveLayer(bounds, null);
//必须先 绘制 目标 bm
canvas.drawBitmap(dstBm,0,screenHeight/2,paint);
//然后 设置模式
pdXfermode = new PorterDuffXfermode(modes[mCurMode]);
paint.setXfermode(pdXfermode);
//绘制 目标 bm
canvas.drawBitmap(srcBm,0,screenHeight/2,paint);
//一定要记得 模式用完之后去除
paint.setXfermode(null);
canvas.restoreToCount(sc);
}
//创建目标 bm 圆形
private Bitmap makeDst(int w,int h){
Bitmap bitmap = Bitmap.createBitmap(w,h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0xFF26AAD1);
canvas.drawOval(w * 1 / 4, w * 1 / 4,w * 1 / 2, w * 1 / 2,p);
return bitmap;
}
//创建源 bm 矩形
private Bitmap makeSrc(int w,int h){
Bitmap bitmap = Bitmap.createBitmap(w,h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0xFFFFCE43);
canvas.drawRect(w *3/ 8, w *3/ 8, w * 5/8, w *5/8, p);
return bitmap;
}
//暴露设置模式的方法
public void setMode(int mode){
this.mCurMode = mode;
invalidate();
}
}
xml布局代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_dscript"
android:layout_centerInParent="true"
android:layout_width="match_parent"
android:layout_height="180dp"
android:paddingTop="90dp"
android:textColor="#F15A5A"
android:gravity="center"/>
<com.wust.xfmode.MyXfModeView
android:id="@+id/mxmv_xfmode"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
MainActivity.java
package com.wust.xfmode;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private MyXfModeView mxmv_xfmode;
private TextView tv_dscript;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindView();
bindData();
}
private void bindData() {
}
private void bindView() {
mxmv_xfmode = findViewById(R.id.mxmv_xfmode);
tv_dscript = findViewById(R.id.tv_dscript);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.mode_menu,menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()){
case R.id.menu_add:
{
mxmv_xfmode.setMode(0);
tv_dscript.setText("PorterDuff.Mode.ADD:饱和度叠加");
}
break;
case R.id.menu_clear:
{
mxmv_xfmode.setMode(1);
tv_dscript.setText("PorterDuff.Mode.CLEAR:清除");
}
break;
case R.id.menu_darken:
{
mxmv_xfmode.setMode(2);
tv_dscript.setText("PorterDuff.Mode.DARKEN:取两图层全部区域,交集部分颜色加深");
}
break;
case R.id.menu_dst:
{
mxmv_xfmode.setMode(3);
tv_dscript.setText("PorterDuff.Mode.DST:只保留目标图的alpha和color,所以绘制出来只有目标图");
}
break;
case R.id.menu_dst_atop:
{
mxmv_xfmode.setMode(4);
tv_dscript.setText("PorterDuff.Mode.DST_ATOP:源图和目标图相交处绘制目标图,不相交的地方绘制源图");
}
break;
case R.id.menu_dst_in:
{
mxmv_xfmode.setMode(5);
tv_dscript.setText("PorterDuff.Mode.DST_IN:两者相交的地方绘制目标图,绘制的效果会受到原图处的透明度影响");
}
break;
case R.id.menu_dst_out:
{
mxmv_xfmode.setMode(6);
tv_dscript.setText("PorterDuff.Mode.DST_OUT:在不相交的地方绘制目标图");
}
break;
case R.id.menu_dst_over:
{
mxmv_xfmode.setMode(7);
tv_dscript.setText("PorterDuff.Mode.DST_OVER:目标图绘制在上方");
}
break;
case R.id.menu_lighten:
{
mxmv_xfmode.setMode(8);
tv_dscript.setText("PorterDuff.Mode.LIGHTEN:取两图层全部区域,点亮交集部分颜色");
}
break;
case R.id.menu_multiply:
{
mxmv_xfmode.setMode(9);
tv_dscript.setText("PorterDuff.Mode.MULTIPLY:取两图层交集部分叠加后颜色");
}
break;
case R.id.menu_overlay:
{
mxmv_xfmode.setMode(10);
tv_dscript.setText("PorterDuff.Mode.OVERLAY:叠加");
}
break;
case R.id.menu_screen:
{
mxmv_xfmode.setMode(11);
tv_dscript.setText("PorterDuff.Mode.SCREEN:取两图层全部区域,交集部分变为透明色");
}
break;
case R.id.menu_src:
{
mxmv_xfmode.setMode(12);
tv_dscript.setText("PorterDuff.Mode.SRC:只保留源图像的alpha和color,所以绘制出来只有源图");
}
break;
case R.id.menu_src_atop:
{
mxmv_xfmode.setMode(13);
tv_dscript.setText("PorterDuff.Mode.SRC_ATOP:源图和目标图相交处绘制源图,不相交的地方绘制目标图");
}
break;
case R.id.menu_src_in:
{
mxmv_xfmode.setMode(14);
tv_dscript.setText("PorterDuff.Mode.SRC_IN:两者相交的地方绘制源图");
}
break;
case R.id.menu_src_out:
{
mxmv_xfmode.setMode(15);
tv_dscript.setText("PorterDuff.Mode.SRC_OUT:不相交的地方绘制源图");
}
break;
case R.id.menu_src_over:
{
mxmv_xfmode.setMode(16);
tv_dscript.setText("PorterDuff.Mode.SRC_OVER:把源图绘制在上方");
}
break;
case R.id.menu_xor:
{
mxmv_xfmode.setMode(17);
tv_dscript.setText("PorterDuff.Mode.XOR:不相交的地方按原样绘制源图和目标图");
}
break;
}
return super.onOptionsItemSelected(item);
}
}
menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_add" android:title="PorterDuff.Mode.ADD"/>
<item android:id="@+id/menu_clear" android:title="PorterDuff.Mode.CLEAR"/>
<item android:id="@+id/menu_darken" android:title="PorterDuff.Mode.DARKEN"/>
<item android:id="@+id/menu_dst" android:title="PorterDuff.Mode.DST"/>
<item android:id="@+id/menu_dst_atop" android:title="PorterDuff.Mode.DST_ATOP"/>
<item android:id="@+id/menu_dst_in" android:title="PorterDuff.Mode.DST_IN"/>
<item android:id="@+id/menu_dst_out" android:title="PorterDuff.Mode.DST_OUT"/>
<item android:id="@+id/menu_dst_over" android:title="PorterDuff.Mode.DST_OVER"/>
<item android:id="@+id/menu_lighten" android:title="PorterDuff.Mode.LIGHTEN"/>
<item android:id="@+id/menu_multiply" android:title="PorterDuff.Mode.MULTIPLY"/>
<item android:id="@+id/menu_overlay" android:title="PorterDuff.Mode.OVERLAY"/>
<item android:id="@+id/menu_screen" android:title="PorterDuff.Mode.SCREEN"/>
<item android:id="@+id/menu_src" android:title="PorterDuff.Mode.SRC"/>
<item android:id="@+id/menu_src_atop" android:title="PorterDuff.Mode.SRC_ATOP"/>
<item android:id="@+id/menu_src_in" android:title="PorterDuff.Mode.SRC_IN"/>
<item android:id="@+id/menu_src_out" android:title="PorterDuff.Mode.SRC_OUT"/>
<item android:id="@+id/menu_src_over" android:title="PorterDuff.Mode.SRC_OVER"/>
<item android:id="@+id/menu_xor" android:title="PorterDuff.Mode.XOR"/>
</menu>
出错点
你在绘制的过程中可能会发现与预期结果不一样,如下:
错误图
正确图
其中的解决方法有两种:
1、给画布添加背景
2、添加图层
以上是关于Paint API —— setXfermode() 详解的主要内容,如果未能解决你的问题,请参考以下文章
Android Shader 颜色图像渲染 paint.setXfermode
Android Paint之 setXfermode PorterDuffXfermode 讲解