当前位置:

首页 > 系统应用 > 8.3.5 Paint API之—— Xfermode与PorterDuff详解(二)

8.3.5 Paint API之—— Xfermode与PorterDuff详解(二)

本节引言: 上一节已经看过 Xfermode 那两个“退场已久”的儿子:AvoidXfermode、PixelXorXfermode。它们并非完全没用,只是时代往前走,这两位终究还是被淘汰了。接下来,把重点放到 Xfermode 目前还在用的“三儿子”身上:PorterDuffXfermode。 先

本文目录

    本节引言:

    上一节已经看过 Xfermode 那两个“退场已久”的儿子:AvoidXfermode、PixelXorXfermode。它们并非完全没用,只是时代往前走,这两位终究还是被淘汰了。接下来,把重点放到 Xfermode 目前还在用的“三儿子”身上:PorterDuffXfermode。

    先祭上官方API文档:PorterDuffXfermode!文档内容很少,我们可以看到他的构造方法:

    参数只有一个:PorterDuff.Mode mode,而Android给我们提供了16种图片混排模式,简单点可以 理解为两个图层按照不同模式,可以组合成不同的结果显示出来!16种混排模式的结果图如下:

    这里两个图层:先绘制的图是目标图(DST),后绘制的图是源图(SRC)!

    当然,在文档中我们发现可供使用的模式并不是16种,而是18种,新增了ADD和OVERLAY两种模式!

    嗯,说多也白说,代码最实际,本节我们写下代码来验证下这18种模式吧!

    PS:这个PorterDuff的命名其实是两个人名的组合:Tomas Proter和 Tom Duff组成的,他们是最早在 最早在SIGGRAPH上提出图形混合概念的大神级人物,有兴趣的自行百度~

    写个例子来验证上面的这个图:

    好的,我们来写个例子验证下上面这个图,通过修改不同的模式,来对结果进行对比分析!

    代码实现:

    Step 1:我们先写个获取屏幕宽高的工具类吧!ScreenUtil.java:

    /**
     * Created by Jay on 2015/10/23 0023.
     */
    public class ScreenUtil {
        /**
         * 获取屏幕宽高,sdk17后不建议采用
         *
         * @param context
         */
        public static int[] getScreenHW(Context context) {
            WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            Display display = manager.getDefaultDisplay();
            int width = display.getWidth();
            int height = display.getHeight();
            int[] HW = new int[] { width, height };
            return HW;
        }
    /**
         * 获取屏幕宽高,建议采用
         *
         * @param context
         */
        public static int[] getScreenHW2(Context context) {
            WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            DisplayMetrics dm = new DisplayMetrics();
            manager.getDefaultDisplay().getMetrics(dm);
            int width = dm.widthPixels;
            int height = dm.heightPixels;
            int[] HW = new int[] { width, height };
            return HW;
        }
    /**
         * 获取屏幕的宽度
         *
         * @param context
         * @return
         */
        public static int getScreenW(Context context) {
            return getScreenHW2(context)[0];
        }
    /**
         * 获取屏幕的高度
         *
         * @param context
         * @return
         */
        public static int getScreenH(Context context) {
            return getScreenHW2(context)[1];
        }
    }

    Step 2:编写我们的自定义View类,在这里做试验!XfermodeView.java:

    /**
     * Created by Jay on 2015/10/23 0023.
     */
    public class XfermodeView extends View {
    private PorterDuffXfermode pdXfermode;   //定义PorterDuffXfermode变量
        //定义MODE常量,等下直接改这里即可进行测试
        private static PorterDuff.Mode PD_MODE = PorterDuff.Mode.ADD;
        private int screenW, screenH; //屏幕宽高
        private int width = 200;      //绘制的图片宽高
        private int height = 200;
        private Bitmap srcBitmap, dstBitmap;     //上层SRC的Bitmap和下层Dst的Bitmap
    public XfermodeView(Context context) {
            this(context, null);
        }
    public XfermodeView(Context context, AttributeSet attrs) {
            super(context, attrs);
            screenW = ScreenUtil.getScreenW(context);
            screenH = ScreenUtil.getScreenH(context);
            //创建一个PorterDuffXfermode对象
            pdXfermode = new PorterDuffXfermode(PD_MODE);
            //实例化两个Bitmap
            srcBitmap = makeSrc(width, height);
            dstBitmap = makeDst(width, height);
        }
    public XfermodeView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    //定义一个绘制圆形Bitmap的方法
        private 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(0xFF26AAD1);
            c.drawOval(new RectF(0, 0, w * 3 / 4, h * 3 / 4), p);
            return bm;
        }
    //定义一个绘制矩形的Bitmap的方法
        private 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(0xFFFFCE43);
            c.drawRect(w / 3, h / 3, w * 19 / 20, h * 19 / 20, p);
            return bm;
        }
    @Override
        protected void onDraw(Canvas canvas) {
            Paint paint = new Paint();
            paint.setFilterBitmap(false);
            paint.setStyle(Paint.Style.FILL);
            canvas.drawBitmap(srcBitmap, (screenW / 3 - width) / 2, (screenH / 2 - height) / 2, paint);
            canvas.drawBitmap(dstBitmap, (screenW / 3 - width) / 2 + screenW / 3, (screenH / 2 - height) / 2, paint);
    //创建一个图层,在图层上演示图形混合后的效果
            int sc = canvas.saveLayer(0, 0, screenW, screenH, 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.drawBitmap(dstBitmap, (screenW / 3 - width) / 2 + screenW / 3 * 2,
                    (screenH / 2 - height) / 2, paint);     //绘制i
            //设置Paint的Xfermode
            paint.setXfermode(pdXfermode);
            canvas.drawBitmap(srcBitmap, (screenW / 3 - width) / 2 + screenW / 3 * 2,
                    (screenH / 2 - height) / 2, paint);
            paint.setXfermode(null);
            // 还原画布
            canvas.restoreToCount(sc);
        }
    }

    乍一看这段代码确实有点绕,但拆开之后并不复杂。核心无非就几步:先拿到屏幕的宽高,再画一个矩形和一个圆形,顺手把它们的位置算出来;然后把图层按固定方式设好,接着通过画笔设置 PD_MODE 对应的 setXfermode,最后绘制到 canvas 上就行了。真正容易让人卡住的,往往是绘制位置的计算。不过这部分其实很灵活,位置怎么安排都可以,按自己的需求来定就行。接下来就逐个看看最终效果,只需要修改 PD_MODE 的取值,切换成不同模式即可。

    运行效果图:

    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:

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

    本节示例代码下载:

    PorterDuffXfermodeDemo.zip

    本节小结:

    嗯,本节就写了一个简单的View来验证这18种不同PorterDuff.Mode下的不同效果, 嘿嘿,蛮耗时间的,不过,读者看起来肯定清晰多了是吧~当然,这只是一些初步的见解!

    PorterDuffXfermode的PorterDuff.Mode对于我们自定义控件是非常重要的! 本节我们初步了解,下节我们挑几个例子来练练手!

    如果你想看关于PorterDuff.Mode更加详细的介绍可见: Android Paint之 setXfermode PorterDuffXfermode 讲解,别人写的不错的一篇文章! 嗯,就到这里,明早体检,今天就写这么多~

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    系统应用
    相关文章 更多
    Yandex官网入口及国际版登录指南
    Yandex官网入口及国际版登录指南

    Yandex官方网站快速入口是https://www.yondex.net/,该平台提供多语言搜索引擎、新闻聚合、翻译工具、图片视频检索及个性化推荐等核心服务,并集成地图、云存储、邮箱、日历等实用功能,支持移动端适配与夜间模式,界面简洁且加载迅速。

    Yandex官网入口及最新访问地址
    Yandex官网入口及最新访问地址

    Yandex搜索引擎官方入口是https://www.yondex.net/,该平台具备强大的多语言搜索能力、集成化的服务生态体系以及智能化的搜索体验优化功能。

    Yandex国际版入口及官网链接
    Yandex国际版入口及官网链接

    Yandex国际版官网为https://www.yondex.net/,提供英文界面,适合非俄语用户;2.https://www.yondex.net/为全球短域名,可自动跳转至对应地区版本;3.俄罗斯本地用户使用https://www.yondex.net/,内容以俄语为主,本地化程度高;4.普通用户可根据语言和需求选择相应入口。

    Yandex官网入口及网页版访问教程
    Yandex官网入口及网页版访问教程

    Yandex官方在线入口是https://www.yondex.net/,提供网页搜索、新闻、图片视频检索、地图导航等服务,并集成翻译、邮箱、云存储、日历等工具,支持个性化首页定制与电商比价功能。

    妖精漫画官网登录入口及首页指南
    妖精漫画官网登录入口及首页指南

    妖精漫画官网登录入口为https://www.yaojingmh.com。该平台资源丰富,涵盖多种题材,更新及时,分类清晰,支持多模式阅读与个性化收藏,具备评论、书架、弹幕等互动功能,界面简洁流畅,适配移动端,提供良好阅读体验。

    Yandex官网入口及最新搜索平台访问方式
    Yandex官网入口及最新搜索平台访问方式

    Yandex搜索平台最新访问入口是https://www.yondex.net/,该网站界面简洁,支持多语言切换和智能提示,具备强大的自然语言处理与多样化内容检索功能,并集成翻译、云存储、邮件等附加服务。

    Yandex搜索引擎官网入口及登录教程
    Yandex搜索引擎官网入口及登录教程

    俄罗斯Yandex搜索引擎创建于1993年,是俄语区主导的搜索服务,官网为https://www.yondex.net/,用户可直接访问并切换语言,无需注册即可使用其搜索、地图、邮箱等集成服务。

    Yandex浏览器官网登录与在线体验入口
    Yandex浏览器官网登录与在线体验入口

    Yandex浏览器官方网站登录入口是https://www.yondex.net/,提供多语言下载、账户同步、广告拦截、Turbo模式及隐私保护等功能,支持跨设备数据同步与安全浏览。

    2026年AO3镜像最新地址及稳定入口
    2026年AO3镜像最新地址及稳定入口

    ao3镜像2026最新入口包括主站archiveofourown.org、镜像站ao3mirror.com和xiaozhan.icu,建议收藏备用并根据网络情况切换使用。

    Win10搜索不到蓝牙耳机的排查与连接方法
    Win10搜索不到蓝牙耳机的排查与连接方法

    面向在Windows 10系统中无法发现蓝牙耳机的用户,本文提供从耳机配对模式到系统设置的完整排查路径。首先确保耳机已充电并进入可被搜索的配对状态,断开与其他设备的连接;随后在Win10设置中开启蓝牙并添加设备。若蓝牙开关缺失或适配器异常,需通过设备管理器检查驱动状态并更新,以恢复正常的蓝牙音频设备识别功能。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。