Android中图片的镂空效果(不规则图形的镂空)

2023-05-16

一、说在前面的话

我们在做新手引导时,经常会遇到凸显某一块功能时需求,类似于下图:





















看到这个功能点可能会有点头大,不过好在Android为我们提供一个美好的工具:PorterDuffXfermode
至于它的功能介绍和简单使用,大家可以参考这篇文章

今天我们就用到其中一个模式:DST_OUT,它的解释:只在源图像和目标图像不相交的地方绘制【目标图像】,在相交的地方根据源图像的alpha进行过滤,源图像完全不透明则完全过滤,完全透明则不过滤

至于怎么使用它呢,下面开始吧
1、我们需要把镂空的Bitmap给绘制出来和灰色的背景进行叠加绘制。也可以把从View中获取Bitmap:

    public static Bitmap buildViewDrawCache(View target, @ColorInt int color) {
        Bitmap bitmap = null;
        if (target == null) {
            return null;
        }
        try {
            target.destroyDrawingCache();
            target.setDrawingCacheBackgroundColor(color);
            boolean isEnable = target.isDrawingCacheEnabled();
            if (!isEnable) {
                target.setDrawingCacheEnabled(true);
            }
            bitmap = target.getDrawingCache();
        } catch (Throwable error) {
        }
        return bitmap;
    }

具体绘制:

 @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        int layoutId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
        //绘制背景
        canvas.drawColor(mBackgroundColor);
        //绘制需要镂空的图形
 		canvas.drawBitmap(bitmap, null, hollowingInfo.rectF, mPaint);
        canvas.restoreToCount(layoutId);

2、设置使用PorterDuffXfermode的模式

public class HomeBgGuideView extends View {
    private int mBackgroundColor;
    private PorterDuffXfermode mBitmapfermode;
    private Paint mPaint;
    public HomeBgGuideView(Context context) {
        super(context);
        initLayout();
    }

    public HomeBgGuideView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initLayout();
    }

    private void initLayout() {
        if (DEBUG) {
            Log.d(TAG, "initLayout() called");
        }
        Resources resources = getResources();
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mBackgroundColor = resources.getColor(R.color.black_alpha_80);
        mBitmapfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        int layoutId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
        canvas.drawColor(mBackgroundColor);
        //设置镂空的模式
        mPaint.setXfermode(mBitmapfermode);
        canvas.drawBitmap(bitmap, null, rectF, mPaint);
        //清空镂空模式
        mPaint.setXfermode(null);
        canvas.restoreToCount(layoutId);
    }

 ...
}

3、确定图形镂空的定位,我们可以通过getLocationOnScreen方法获取当前需要镂空View的位置

 int[] location = new int[2];
 anchorView.getLocationOnScreen(location);
 float x = location[0];
 float y = location[1];
 mAnchorRect.set(x, y, x + anchorView.getWidth() * 2, y + anchorView.getHeight() * 2);

至此 整个不规则镂空的使用方式介绍完毕

完成代码如下:

public class HomeBgGuideView extends View {
    private static final boolean DEBUG = BuildConfig.DEBUG;
    private static final String TAG = DEBUG ? "BarracksGuideView" : "";
    private int mBackgroundColor;
    private PorterDuffXfermode mXfermode;
    private PorterDuffXfermode mBitmapfermode;
    private Paint mPaint;
    //镂空的集合
    private List<HollowingInfo> mHollowingOutList = new ArrayList<>();
    private float mRadius;

    public HomeBgGuideView(Context context) {
        super(context);
        initLayout();
    }

    public HomeBgGuideView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initLayout();
    }

    private void initLayout() {
        if (DEBUG) {
            Log.d(TAG, "initLayout() called");
        }
        Resources resources = getResources();
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mBackgroundColor = resources.getColor(R.color.black_alpha_80);
        mXfermode = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
        mBitmapfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
        mRadius = getResources().getDimension(R.dimen.dp_10);
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        int layoutId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
        canvas.drawColor(mBackgroundColor);
        //判断镂空集合不为空时,则进行镂空处理
        if (mHollowingOutList != null) {
            //遍历集合
            for (HollowingInfo hollowingInfo : mHollowingOutList) {
                //判断如果为图形类型
                if (hollowingInfo.hollowingState == HollowingState.SHAPE) {
                    mPaint.setXfermode(mXfermode);
                    float radius = hollowingInfo.mRadius == 0 ? mRadius : hollowingInfo.mRadius;
                    canvas.drawRoundRect(hollowingInfo.rectF, radius, radius, mPaint);
                }
                //判断如果为图片类型时,则进行镂空处理
                else if (hollowingInfo.bitmap != null && hollowingInfo.bitmap.get() != null &&
                        hollowingInfo.hollowingState == HollowingState.BITMAP) {
                    mPaint.setXfermode(mBitmapfermode);
                    canvas.drawBitmap(hollowingInfo.bitmap.get(), null, hollowingInfo.rectF, mPaint);
                }
            }
        }
        mPaint.setXfermode(null);
        canvas.restoreToCount(layoutId);
    }

    /**
     * 设置圆角
     */
    public void setRadius(float radius) {
        if (DEBUG) {
            Log.d(TAG, "setRadius() called with: radius = [" + radius + "]");
        }
        mRadius = radius;
        postInvalidate();
    }

    /**
     * 设置数据集合
     */
    public void setShapeDataList(List<RectF> rectList) {
        if (DEBUG) {
            Log.d(TAG, "setDataList() called with: rectList = [" + rectList + "]");
        }
        if (rectList == null || rectList.isEmpty()) {
            return;
        }
        List<HollowingInfo> dataList = new ArrayList<>(rectList.size());
        for (RectF rectF : rectList) {
            HollowingInfo hollowingInfo = new HollowingInfo();
            hollowingInfo.rectF = rectF;
            dataList.add(hollowingInfo);
        }
        setDataList(dataList);
    }

    /**
     * 设置数据集合
     */
    public void setDataList(List<HollowingInfo> rectList) {
        if (DEBUG) {
            Log.d(TAG, "setDataList() called with: rectList = [" + rectList + "]");
        }
        if (rectList == null || rectList.isEmpty()) {
            return;
        }

        mHollowingOutList.clear();
        mHollowingOutList.addAll(rectList);
        postInvalidate();
    }

    /**
     * 清除数据
     */
    public void clearHollowingOutList() {
        if (mHollowingOutList == null) {
            return;
        }
        for (HollowingInfo hollowingInfo : mHollowingOutList) {
            hollowingInfo.rectF = null;
            if (hollowingInfo.bitmap != null) {
                hollowingInfo.bitmap.clear();
                hollowingInfo.bitmap = null;
            }
        }
        mHollowingOutList.clear();
        postInvalidate();
    }

    public static class HollowingInfo {
        public RectF rectF;
        public float mRadius;
        public SoftReference<Bitmap> bitmap;
        public HollowingState hollowingState = HollowingState.SHAPE;
    }

    public enum HollowingState {
        BITMAP, SHAPE
    }

}

在这里大家可以有一个疑问,为什么绘制镂空的不规则图形时使用的是DST_OUT,而使用绘制规则图形时使用CLEAR, 大家可以评论区讨论

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android中图片的镂空效果(不规则图形的镂空) 的相关文章

  • 在其他活动上显示列表视图[重复]

    这个问题在这里已经有答案了 可能的重复 现有活动的自定义列表视图 https stackoverflow com questions 4554770 custom listview over an exsisting activity 我如
  • Android 如何调整相机图片的大小并将其保存为位图以在活动内的固定矩形区域中显示

    我有一个可以拍摄项目照片的应用程序 并且这些项目必须在 ActivityScreen 上的某个固定区域中可见 问题是活动中显示的来自保存的位图的图片与我预览和拍摄的图片完全不同 它看起来放大了 并显示了我拍照时在预览中没有看到的区域 这是关
  • 点击 FCM 通知消息时如何获取消息正文?

    当应用程序在后台时 通知消息是通过Notification传递的 当点击通知时 应用程序启动 我如何获取消息正文 意图是这样的 Bundle google sent time 1470813025421 from 568540028909
  • 当导航到可组合项时,我们如何隐藏 BottomAppBar(包含导航)?

    我有个问题 问题是 当我导航到 添加问题 屏幕时 我不知道如何隐藏底部应用程序栏 我需要你的帮助 这是带有底部应用栏的 我的屏幕 Composable fun Navigation val navController rememberNav
  • PhoneStateListener 中的拨出呼叫检测

    我想检测 Android 应用程序中的拨出电话 实际上我可以检测拨出电话 但它也适用于拨入电话 但我不希望这样 我需要它仅用于传出 这是我的代码 boolean ringing false boolean offhook false pub
  • 将 timeout() 与 retryWhen() 结合起来

    我正在创建一个简单的应用程序 用于使用 RxAndroidBle 库连接蓝牙设备 干杯 大家干得好 我遇到的情况是 有时当我连接到设备时 我会收到状态为 133 的 Gatt 错误 我知道这可能会发生 所以我想做的就是在发生错误时重试所有操
  • 如何在调试中查看活动堆栈?

    我遇到一个问题 我的一项活动在之后弹出I think我完成了 有没有办法查看活动的堆栈 1 You can use Hierarchy Viewer within Eclipse You can see all connected devi
  • 如何在android项目中使用Roboto字体[重复]

    这个问题在这里已经有答案了 我开发了一个android项目 在此项目中 文本字体默认为 android sans 现在我想将整个项目的默认文本字体替换为 roboto 字体 我怎样才能做到这一点 您可以从这里下载 Roboto 字体 htt
  • Samsung Galaxy J1 4G 上的 webRTC 应用程序崩溃

    我正在开发视频通话 Android 应用程序 该应用程序可以在 Nexus 4 Moto g2 Moto g3 Moto e 等设备上进行视频通话 但当我在 Samsung Galaxy J1 4G 上拨打电话时 它崩溃了 这是堆栈跟踪 E
  • 反应调试模式和正常模式之间的本机不同行为

    嗨 我有一个非常复杂的问题 所以我在 github 上为这个问题创建了一个新的存储库 所以你可以克隆它并看看问题是什么 https github com guitar9 hafas client example https github c
  • 如何对文本文件执行二分搜索

    我有一个大文本文件 5Mb 我在 Android 应用程序中使用它 我将文件创建为预先排序的字符串列表 并且文件一旦创建就不会更改 如何对此文件的内容执行二分搜索 而不需要逐行读取来查找匹配的字符串 由于文件的内容不会改变 因此您可以将文件
  • 当应用程序离开后台并在 iOS 和 Android 中返回时,WebView 正在重新加载?

    我有一个小应用程序 它仅包含一个网络视图 我正在使用 url 加载网络视图 但是当我的应用程序进入后台并返回时 网络视图正在重新加载 对我来说 这不应该发生 在 iOS 和 Android 中 当应用程序进入后台时 如何停止 webview
  • 从我的应用程序中搜索 Youtube 应用程序中的特定字符串

    我有一个按钮 当我点击它时 我想调用 Youtube 应用程序并搜索预定义的字符串 搜索字符串是不变的 我的意思是 Youtube 应用程序将自动显示结果 我知道为了搜索频道 我们输入 Intent intent new Intent In
  • 如何在知道其十进制值的情况下找出菜单中项目的字符串 ID?

    我正在使用 android support v7 appcompat 在一个活动中 我想在操作栏中显示后退按钮 我愿意 public class News extends ActionBarActivity Override protect
  • 使用 Kotlin 在 Android 中渲染 PDF

    我已经在使用 Java 的 PDF 上看到了该线程 但找不到任何有关如何使用 Kotlin 执行此操作的信息 有什么方法或者我需要使用Java吗 在 Android 上使用 Java 渲染 PDF 文件 https stackoverflo
  • Volley ProgressDialog在获取大量数据时卡住/冻结

    这是我的代码 private void downloadSupplyTownData final int townId2 Using Volley Post params to be sent to the server HashMap
  • Android模拟器相机自定义图像

    有谁知道模拟器相机启动时可以打开一些个人照片吗 我有一个基于图像分析的应用程序 我希望当我单击相机按钮时 相机会打开而不是默认的 Android 模拟器移动图像 我希望它打开一些我设置为默认图像的图像 因此 当我选择拍照时 它只会显示该图像
  • 使用硬件键盘时 Android TabHost 选项卡会窃取焦点

    我目前有一个TabHost包含 4 个选项卡 在一些片段上我们有一些EditText布局内的视图 我们注意到 当您尝试输入任何内容时EditText使用硬件键盘的视图 焦点被窃取EditText并赋予当前活动选项卡TabHost 这只发生在
  • Android:getIntent() 已弃用

    我的程序由一个 MainActivity 和两个片段活动组成 我需要一个片段从用户那里获取一个字符串值并将其传递给第二个片段 我正在努力思考如何做到这一点 由于我熟悉意图 我发现这个答案 https stackoverflow com qu
  • 更新 Android Studio 后 Android 模拟器无法运行

    我通常使用 Android Studio 从 2 2 3 更新到 2 3 后 我的模拟器不再工作 这也很困难 因为 Google 决定弃用独立 SDK 管理器 我发现我可以检查 选择 显示包详细信息 但我仍然无法让我的 Google And

随机推荐

  • 官方源码包安装MariaDB-server10.6.8

    文章目录 官方源码包安装MariaDB server10 6 81 下载源码包 xff0c 解压文件2 安装依赖和创建运行用户3 编译安装4 修改环境变量5 初始化数据库 xff0c 默认以root用户登录 xff0c 初始无密码6 启动测
  • 多实例启动mariadb10.6.8(源码安装)

    文章目录 多实例启动mariadb10 6 8 xff08 源码安装 xff09 1 源码安装mariadb10 6 8的情况介绍2 多实例配置2 1 创建各种配置文件2 2 创建启动脚本2 3 配置启动脚本并启动服务2 4 连接测试2 5
  • 基于tensorflow的图像处理(一)TFRecord输入数据格式

    参考 基于tensorflow的图像处理 一 TFRecord输入数据格式 云 43 社区 腾讯云 TensorFlow提供了一种统一的格式来存储数据 xff0c 这个格式就是TFRecord xff0c TFRecord文件中的数据都是通
  • RabbitMQ一对多使用

    在开发工作中 xff0c 我们使用RabbitMQ进行系统之间数据的同步 xff0c 最初使用是一对一的情况 xff0c 其实MQ也有一对多和多对多的情况 xff0c 工作实际也需要一对多 xff0c 写了小demo来试试 Applicat
  • 在Centos系统下git 遇到fatal: HTTP request failed,error: while accessing ;SSL connect error错误

    在Go项目中go get的时候遇到以下错误 src github com lib pq git error while accessing https github com lib pq info refs fatal HTTP reque
  • Gazebo学习笔记(一)

    搭建一个移动车 ctrl 43 m 进入到gazebo编辑界面 ctrl 43 m 进入到gazebo编辑界面 1 搭建一个车底盘 xff0c 选择simple shapes 的box xff0c 点击后在编辑框中释放 2 利用resize
  • ubuntu 安装openssl-devel

    需要安装openssl devel xff0c Github上的说明要用yum工具安装 xff0c 反复安装不上 xff0c 提示没有repos xff08 回购 xff09 xff0c 最后找到了方法 在Ubuntu系统里这是分开的两个
  • 1分钟教会你二进制撩妹(汉)读心术

    近些年来 xff0c 小魔发现 xff0c 对于年轻的男女而言 xff0c 一些传统的节日似乎都变成了情人节或者脱单节 xff0c 就连 光棍节 xff0c 实际上很多人都是抱着节前或者是当天脱单而过的 双11 光棍节 即将来临 xff0c
  • VLAN基础配置及Access接口、Trunk接口、Hybrid接口

    文章说明 xff1a 1 本文使用的软件是eNSP 2 退到下一层用命令quit xff0c 简写q 3 部分命令使用的是简写 和完整命令一样的作用 xff0c 按键盘上的 34 Tab 34 建可自动补全命令 用于查询的命令 xff1a
  • c++pthread多线程消费者问题

    分布式选修课上讲了多线程编程 xff0c 布置了一个生产者消费者的作业 xff0c 觉得挺有意思 xff0c 并且网络上的消费者问题多使用c语言编写 xff0c 故在此记录c 43 43 解决方法 由于是消费者线程各自计数 xff0c 故使
  • 红包动画

    lt DOCTYPE html gt lt html lang 61 34 en 34 data dpr 61 34 1 34 style 61 34 font size 32px 34 gt lt head gt lt meta char
  • Android中<xliff:g></xliff:g>的用法

    在查阅修改Android源码的过程中经常能在字符串资源中看到类似下面的标签 xff1a span class hljs tag lt span class hljs title resources span span class hljs
  • vue单文件组件的格式规范

    lt template gt lt template gt lt script gt export default name 39 39 mixins components props data return computed watch
  • Linux命令行安装weblogic12c

    Linux命令行安装weblogic12c 一 安装jdk 若已安装可跳过 1 Oracle官网下载jdk linux安装包 2 卸载linux系统中自带的jdk 使用rpm qa grep java查询出系统自带的jdk xff1b 使用
  • Ubuntu 远程免密码登录设置

    我们正常使用 ssh 远程登录服务器进行操作 xff0c 需要输入用户名 服务器ip以及密码 xff0c 当我们需要同时管理多个服务器的时候 xff0c 每次都需要重复输入这些东西会显得特别麻烦和浪费时间 xff0c 因此我们可以通过配置密
  • Mybatis resultMap启动时报错:Could not resolve type alias userResultMap Cannot find class: userResultMap

    MyBatis中在查询进行select映射的时候 xff0c 返回类型可以用resultType xff0c 也可以用resultMap xff0c resultType是直接 表示返回类型的 xff0c 而resultMap则是对外部Re
  • synchronized-锁总结

    目录 一 相关知识点 1 1 对象头 1 2 锁相关概念 1 3 查看对象头工具 二 锁流程 2 1 加锁 xff1a monitorenter 2 2 释放锁 xff1a monitorexit 一 相关知识点 在 JDK1 6 之前 x
  • 软件工程师校招面试救急包

    LeetCode牛人总结 xff08 手撕代码前看看 xff0c 抱佛脚 xff09 https github com labuladong fucking algorithm blob master README md 剑指offer x
  • 微信SDK中含有的支付功能怎么去掉?

    一 说在前面的话 这两天遇到一个特别让我DT的问题 xff0c 估计大家通过标题就能知道问题了 没错 xff0c 就是在应用中集成了微信SDK后 xff0c 它自动支持了微信分享 登录 收藏 支付等功能 这一点没啥 xff0c TM的关键点
  • Android中图片的镂空效果(不规则图形的镂空)

    一 说在前面的话 我们在做新手引导时 xff0c 经常会遇到凸显某一块功能时需求 xff0c 类似于下图 xff1a 看到这个功能点可能会有点头大 xff0c 不过好在Android为我们提供一个美好的工具 xff1a PorterDuff