Android Paint Xfermode实现镂空相机扫描界面_android 拍照镂空-程序员宅基地

技术标签: 自定义View  Android 自定义控件  学习记录  Android 绘图  

demo比较简单,背景颜色是黄色,然后在上面覆盖上自定义View。实际需求是在相机的View上盖上一层遮罩,遮罩中心镂空一个透明显示的圆形部分。这个需求一般的布局比较难以实现,如果要ui给一张素材图片又存在不同分辨率手机对图片的拉伸问题,中间的圆会变形,适配不好。所以想要用一个自定义View来实现,这样不仅不存在适配问题而且也比较简单。


效果图:


简单粗暴直接上代码:

package com.xingyun.scandemo;


import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.Shader;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;


/**
 * Created by SY on 2018/5/11.
 */


public class ScanView extends View {


    private int measuredHeight;
    private int measuredWidth;
    //圆心x坐标
    private int centerX;
    //圆心y坐标
    private int centerY;
    //圆半径
    private int radius;
    //起始弧度、扫过弧度
    private float startAngle=-90, sweepAngle=180;


    public ScanView(Context context) {
        super(context);
    }


    public ScanView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }


    public ScanView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }




    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        measuredHeight = getMeasuredHeight();
        measuredWidth = getMeasuredWidth();
    }


    @Override
    protected void onDraw(Canvas canvas) {
        //创建镂空圆和遮罩bitmap
        Bitmap rectBitmap = createRectBitmap();
        Bitmap circleBitmap = createCircleBitmap();


        Paint paint = new Paint();
        paint.setFilterBitmap(false);
        //保存所有的标识
        canvas.saveLayer(0, 0, measuredWidth, measuredHeight, null,
                Canvas.MATRIX_SAVE_FLAG);
        //画圆
        canvas.drawBitmap(circleBitmap, 0, 0, paint);
        //setXfermode   为SRC_OUT   只在源图像和目标图像不相交的地方绘制
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
        //画遮罩矩形
        canvas.drawBitmap(rectBitmap, 0, 0, paint);


        //画外部圆环
        Paint loopPaint = new Paint();
        int strokeWidth = 20;
        loopPaint.setStrokeWidth(strokeWidth);
        loopPaint.setAntiAlias(true);
        loopPaint.setStyle(Paint.Style.STROKE);
        loopPaint.setStrokeCap(Paint.Cap.ROUND);


        float distance = 40;
        RectF oval = new RectF((int) (measuredWidth / 2f) - radius - distance, (int) (measuredHeight / 2f) - radius - distance,
                (int) (measuredWidth / 2f) - radius - distance + 2 * (radius + distance), (int) (measuredHeight / 2f) - radius - distance + 2 * (radius + distance));
        loopPaint.setColor(Color.GRAY);
        //底层灰色圆
        canvas.drawCircle(centerX, centerY, radius + distance, loopPaint);
        //渐变色
        LinearGradient lg = new LinearGradient(0, 0, 1000, 1000, Color.parseColor("#a5d2fe"), Color.parseColor("#519aff"), Shader.TileMode.MIRROR);
        loopPaint.setShader(lg);
        //蓝色圆弧
        canvas.drawArc(oval, startAngle, sweepAngle, false, loopPaint);
        startAngle = (startAngle + 1) % 360;
        //回收
        rectBitmap .recycle();
        circleBitmap .recycle();
        rectBitmap =null;
        circleBitmap =null;
        //重绘
        invalidate();
    }




    /**
     * 创建镂空层圆形形状
     */
    private Bitmap createCircleBitmap() {
        Bitmap bm = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bm);
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.WHITE);
        centerX = (int) (measuredWidth / 2f);
        centerY = (int) (measuredHeight / 2f);
        radius = measuredWidth / 3;
        canvas.drawCircle(centerX, centerY, radius, paint);
        return bm;
    }


    /**
     * 创建遮罩层形状
     */
    private Bitmap createRectBitmap() {
        Bitmap bm = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bm);
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.WHITE);
        canvas.drawRect(new RectF(0, 0, measuredWidth, measuredHeight), paint);
        return bm;
    }
}

Activity里添加:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/yellow"
    tools:context="com.xingyun.scandemo.MainActivity">

    <com.xingyun.scandemo.ScanView
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</FrameLayout>

直接用FrameLayout将自定义的View覆盖在其他View上面即可。

代码很简单,就是在onMeasure方法里获取了宽高,然后onDraw方法里通过createCircleBitmap和createRectBitmap方法获得遮罩矩形和中间圆形的bitmap,再分别绘制这两个bitmap,将画笔Xfermode设置为PorterDuff.Mode.SRC_OUT,表示只在源图像和目标图像不相交的地方绘制。最后在圆的外面绘制了一个圆弧,并且不断修改起始弧度再次绘制,从而实现旋转的动画。关于Paint的Xfermode的属性可以看下图:


具体可以参考文章https://www.cnblogs.com/libertycode/p/6290497.html。


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/mouse12138/article/details/80283062

智能推荐

Lua 入门_lua_setupvalue-程序员宅基地

文章浏览阅读1.4k次。此篇文章所有操作都是基于上一篇安装的docker容器内进行操作案例来自于菜鸟教程首先进入容器安装vimapk add vimLua 变量变量就是给一块内存区域赋予一个值。使得程序可以读取和修改相应内存中内容。变量由字母、数字、下划线组成。必须以字母或下划线开头。Lua 是大小写敏感的。变量分为全局变量和局部变量type variable_listlocal a, b = 1, 10 --局部变量c, d = 2, 20 -- 全局变量如果变量只定义了没有初始化_lua_setupvalue

3.3 ORACLE 的 EMP&DEPT表 建表语句_oracle emp建表语句-程序员宅基地

文章浏览阅读1.5k次,点赞2次,收藏15次。ORACLE 的 EMP&DEPT表 建表语句-- 创建表与数据CREATE TABLE EMP(EMPNO NUMBER(4) NOT NULL,ENAME VARCHAR2(10),JOB VARCHAR2(9),MGR NUMBER(4),HIREDATE DATE,SAL NUMBER(7, 2),COMM NUMBER(7, 2),DEPTNO NUMBER(..._oracle emp建表语句

使django支持PUT,DELETE的方案_django如何简单快速实现put、delete方法-程序员宅基地

文章浏览阅读9.5k次。第一种方案修改ajax中type方式并设置header,同时对put重新构建数据字典在对views中的方法进行类对象封装时,发现django并不支持像post和get一样将数据封装。 由于网页端无法设置method方法,在用postman和ajax开启pycharm对接口进行debug测试时发现:提交数据后并没有进入代码逻辑。 查阅资料得知,django支持put和delete方法..._django如何简单快速实现put、delete方法

vscode通过跳板机(堡垒机)连接remote服务器_vscode 连接堡垒机-程序员宅基地

文章浏览阅读2.9w次,点赞17次,收藏45次。先吐槽: 搞了一上午!!! 我太难了!!! 最近服务器不够用, 就差动手算深度学习梯度了!!! 向本科朋友借了几台机子跑, 这要是没上过大学, 研究生还不能毕业了呢!!!目录1. 简单任务介绍2. 本机的配置3. 跳板机的配置4.内网服务器的配置5. 怎么样不需要密码访问嘤嘤嘤~~1. 简单的任务介绍:我现在用的电脑叫做A, 然后借了一台服务器叫做C..._vscode 连接堡垒机

【实战】python-docx---每页表格固定显示行数_python docx设置每页行数-程序员宅基地

文章浏览阅读1.1k次。例如100行数据填入word,每页固定展示5行,需要20页_python docx设置每页行数

OpenEmu:一个让你在Mac上爽快体验任天堂的模拟器_openemu苹果电脑-程序员宅基地

文章浏览阅读7k次。目录前言OpenEmu简介OpenEmu实际体验最后前言 这次的文章和以往不太一样, 不谈技术, 来谈谈情怀. 记得那是我小学二三年级的时候吧, 我妈给我买了一个Game Boy, 价格记不清了, 反正不贵, 而且是黑白的那种, 应该就是初代Game Boy吧. 然后还配了一张口袋妖怪青的卡. 在那个魔域啊, 传奇啊, 或者什么类似网游盛..._openemu苹果电脑

随便推点

Shell编程之case循环、for循环及while循环理论与实例_scl case循环执行-程序员宅基地

文章浏览阅读493次。文章目录前言:一、case多分支语句1.1 case语句的结构1.2 case语句应用示例二、for循环语句2.1 for循环语句结构2.2 for语句应用示例三、while 循环语句3.1 while 语句结构3.2 while 语句应用示例总结:前言:除了我们之前讲述的if条件语句外,作为一种脚本编程语言,Shell同样包含循环、分支等其他程序结构,从而能够轻松完成更加复杂的工作,本篇博客..._scl case循环执行

TCP中的RST标志(Reset)详解_tcp的rst-程序员宅基地

文章浏览阅读2.9k次,点赞2次,收藏7次。TCP中的RST标志(Reset)详解_a_tu_的专栏-程序员宅基地_rst tcp在谈RST攻击前,必须先了解TCP:如何通过三次握手建立TCP连接、四次握手怎样把全双工的连接关闭掉、滑动窗口是怎么传输数据的、TCP的flag标志位里RST在哪些情况下出现。下面我会画一些尽量简化的图来表达清楚上述几点,之后再了解下RST攻击是怎么回事。1、TCP是什么?TCP是在IP网络层之上的传输层协议,用于提供port到port面向连接的可靠的字节流传输。我来用土语解释下上面的几个关键字:por_tcp的rst

日常问题集锦_el-input 居中-程序员宅基地

文章浏览阅读2.1k次,点赞2次,收藏5次。【①】运行项目时出现的端口被占用问题:【②】JS中获取当前时间:【③】一些常用的校验规则:【④】vue 表格中数据默认全部选中:【⑤】vue 获取表格中选中行的数据:【⑥】vue 强制更新数据:【⑦】vue 想要el-input框内文字居中显示:【⑧】vue 清除表单内的内容或者清除表单验证:【⑨】vue 表格分页的两种格式:【⑩】vue 中防止按钮重复点击提交的方法:_el-input 居中

Android11及以上 文件读写权限申请_android 11 write_external_storage-程序员宅基地

文章浏览阅读2.1w次,点赞9次,收藏50次。Android11及以上 文件读写权限申请_android 11 write_external_storage

yii2 在线教育系统,开办托管班需要什么手续?如何经营托管班?-程序员宅基地

文章浏览阅读330次,点赞8次,收藏6次。托管班店铺的装修风格,一定要结合学生喜欢的设计风格,颜色搭配一定要看起来舒适,让学生一进来就有学习的范围,不由自主就想学习的动力,所以装修的风格,是要符合学生学习的环境氛围,让学生一进来想呆着不想走,所以设计方面,一定一定多参考同行的装修风格。想要运营好一家托管班,那么需要有一个正规化的管理流程,那么就要为校区制定各种规章制度,按照规章制度来走,避免后期因为没有任何提示,导致出现一系列的问题,所以要出针对教师,学生,家长的不同规章制度。保护孩子的安全,预防托管机构火灾事故的发生,是托管班一项重要指标。

外观模式实例-智能手机一键备份_某软件公司为新开发的智能手机控制与管理软件提供了一键备份功能,通过该功能可以-程序员宅基地

文章浏览阅读2.2k次,点赞7次,收藏40次。外观模式实例问题描述结构图编程实现需要交互的类Facade类客户端问题描述某软件公司为新开发的智能手机控制与管理软件提供一键备份功能,通过该功能可以将原本存储在手机中的通讯录、短信、照片、音乐等资料一次性拷贝到移动存储介质中(例如:SD卡)中。在实现过程中需要与多个已有的类进行交互,如通讯录管理类、短信管理类。结构图编程实现需要交互的类public class ContactsManager { private String contacts; public Contacts_某软件公司为新开发的智能手机控制与管理软件提供了一键备份功能,通过该功能可以

推荐文章

热门文章

相关标签