在日常 Android 开发中,当我们涉及自定义 View 时,图层混合模式(Xfermode)和滤镜(ColorFilter)是绕不开的高级特性。 
一、图层混合模式(Xfermode)概述
图层混合模式的核心作用是:将所绘制图形的像素,与 Canvas 中对应位置已有的像素,按照一定的规则进行混合,从而更新出最终的像素颜色值。 Android 一共为我们提供了 18 种不同的混合模式。
如果我们翻看底层源码,会发现每一种混合模式都代表一种特定的混合规则,通常由两行计算代码决定。在混合过程中,主要涉及两个基本概念:
- SRC:原图像(上层正在绘制的图像)
- DEST:目标图像(下层已经存在的图像)
以 Clear 模式为例:它的混合效果由计算出的 Alpha 通道值(a_out)和最终颜色输出值(out)决定。Clear 模式向这两个参数都传入了 0,这就意味着完全清除该区域的颜色值。
常见的 API 使用场景
图层混合主要有以下几个入口:
- Paint.setXfermode:为画笔设置图层混合规则。这是我们日常开发中最常用的方式,也是本篇笔记的重点。
- Composition Shader :在构造方法中传入
xfermode参数,定义两个渲染器之间的混合规则。 - PorterDuffColorFilter:作为颜色过滤器使用(后文会详细讲到)。
二、避坑指南:硬件加速与离屏绘制
在使用图层混合模式时,如果不了解硬件加速和离屏绘制,常常会遇到各种"绘制无效"或者"背景发黑"的问题。
1. 关闭硬件加速
从 Android API 14 开始,系统默认开启了硬件加速,但部分图层混合 API 并不支持硬件加速。如果遇到渲染异常,通常需要针对特定的 View 关闭它:
java
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
2. 为什么一定要用离屏绘制?
在实际绘制中,如果不开启离屏绘制,整个 Canvas 的背景(例如一个灰色的底色)也会直接参与到图层混合计算中,导致最终得出的结果极不符合预期。
正确的做法是: 先创建一个独立的离屏缓冲图层,在这个图层上完成 SRC 和 DEST 的混合计算后,再把混合好的结果图层整体绘制回主画布上。
离屏缓冲的两种方式:
Canvas.saveLayer:日常首选。需要特别注意的是,在保存图层并绘制完毕后,必须 调用canvas.restoreToCount()恢复画布。View.setLayerType:将整个 View 绘制在离屏缓冲中,参数可选 GPU 缓冲或 Bitmap 缓冲(通常考虑前者)。
三、18 种常见图层混合模式详解
关于图层混合模式,有一个非常重要的大前提:图层混合模式永远只作用于 SRC(原图像)所在的区域,非相交区域将保持原样。

以下是几个最核心的模式:
- Clear:清空 SRC 原图像区域的颜色像素。
- Src / Dest:只显示上层图像(Src) / 只显示下层图像(Dest)。
- Src Over / Dest Over:正常层叠。Src Over 表现为正常的上下层覆盖;Dest Over 则是上下层都显示,但下层居上。
- Src In / Dest In :取两层交集。Src In 显示交集里的上层;Dest In 显示交集里的下层。
- Src Out / Dest Out :取非交集部分,交集部分变透明。Src Out 留下上层非交集,Dest Out 留下下层非交集。
- Src Atop:取上层交集部分与下层非交集部分拼接组合。
- Darken / Lighten:取两层全部区域交集部分,颜色加深(Darken)或颜色点亮(Lighten)。
- Screen:取两层全部区域交集部分,产生屏幕混合变亮的效果。
💡 关于底层公式(以 Darken 为例): 官方给出的公式(如 αout=αs+αd−αs⋅αd)看起来十分硬核。但在实际开发中,我们无需过度深究数学推导,只需要理解上述不同模式的作用区域和含义,就能准确地选择合适的模式。
实战案例:如何实现刮刮卡效果?
利用 Src Out 的特性,实现刮刮卡非常简单:
- 首先在底层绘制目标图像(Dest,即刮开后看到的底图)。
- 将画笔的图层混合模式设置为
Src Out。 - 在覆盖层上监听手指触摸轨迹,用贝塞尔曲线绘制路径(Src)。
- 原理解析 :
Src Out的特性是保留不相交的部分,清除相交的部分。因此手指划过的轨迹(交集)会变成透明,从而完美地透出底部的图像。

四、滤镜效果(ColorFilter)进阶
除了图形之间的混合,Android 还提供了强大的色彩滤镜(ColorFilter)来改变图像本身的颜色。
1. LightingColorFilter (光照颜色过滤器)
顾名思义,它用来模仿光照效果。构造函数接收两个十六进制的 int 型颜色值:mul(相乘)和 add(相加)。 计算公式:Result = Target * mul + add
常见用法:
- 保留原图 :
mul = 0xFFFFFFFF,add = 0x00000000 - 剔除红色 :
mul = 0x00FFFFFF(将 Red 通道的乘数设为00),add = 0x00000000 - 增强绿色亮度 :在
add参数中专门调大绿色通道的分量即可。
2. PorterDuffColorFilter
虽然它的名字里带有 PorterDuff,但和前文的图形混合有所区别。前文是"图片与图片的混合",而这里是创建一层单一的纯色图层与原图进行指定模式的混合。构造方法需要传入一个颜色值以及一个图层混合模式。
3. ColorMatrixColorFilter (颜色矩阵过滤器)
这是最底层的滤镜类。它需要传入一个长度为 20 的数组,构成一个 4 行 5 列的颜色矩阵:
- 第 1~4 行:分别决定 R、G、B、A 四个通道的值。
- 第 5 列:代表各个通道的偏移量。
默认情况下的单位矩阵会保持原图效果。我们可以通过以下方式魔改:
-
修改偏移量(第 5 列):例如同时增加红、绿通道的偏移量,能让图片产生泛黄的效果。
-
修改系数(对角线元素):例如把 R 通道的系数改为 2,整张图片就会显著偏红。
-
"胶片效果":把 RGB 三个通道的系数全部取反,再加上 255 的偏移量,就能快速实现反色的胶片底片效果。

💡 ColorMatrix 的常用辅助方法
直接推算 4x5 矩阵可能有些反人类,ColorMatrix 类贴心地提供了几个直接修改矩阵的辅助方法:
setScale(sx, sy, sz, sa):直接调整 RGBA 四个分量的系数,经常用于改变图像亮度。setSaturation(sat):调节色彩饱和度。sat = 0会变成纯粹的黑白图;sat = 1是默认原图;sat > 1图像色彩会更加浓烈鲜艳。setRotate(axis, angle):色度旋转。基于 RGB 三维坐标系,对特定轴(0代表红,1代表绿,2代表蓝)进行旋转。底层原理是对变换矩阵进行 sin 和 cos 运算。比如绕着红轴旋转,就会直接影响原本 G 和 B 的分量组合,产生独特的色调变化。
示例代码:xfermode、colorfilter