Android自定义View实现两种二维码扫描效果

不问前路如何,我自稳步向前!

背景

最近在开发新项目时,使用了扫描二维码的功能,一般扫描二维码的效果是一条横线从上到下循环移动,这次却换成了网格图片。网上的大多数第三方库实现类似效果时 网格图片被拉伸变形。为了实现效果,只能动手写,话不多说,先看效果。(片尾附有代码地址)

横线效果

网格效果

基础属性

这里自定义了一些常见属性:

scan_image 扫描图片资源
scan_duration 扫描一次时间 ms
scan_width 正方形扫描框宽度
scan_bg_color 除正方形扫描框之外的背景颜色
scan_rect_width 正方形扫描框边框宽度
scan_rect_color 正方形扫描框边框颜色
scan_border_width 扫描框四个边角线的宽度
scan_border_length 扫描框四个边角线的长度
scan_border_color 扫描框四个边角线的颜色

绘制背景色

首先定义正方形扫描框矩形的位置,这么默认使用屏幕中心的位置

kotlin 复制代码
 private fun createRect() {
        val leftOffset = (width - mScanWidth) / 2f
        val topOffset = (height - mScanWidth) / 2f
        mRectFrameRect =
            RectF(leftOffset, topOffset, leftOffset + mScanWidth, topOffset + mScanWidth)

        val scaleHeight = mScanWidth.toFloat() / mLineBitmap!!.width * mLineBitmap!!.height
        mLineBitmap =
            Bitmap.createScaledBitmap(mLineBitmap!!, mScanWidth, scaleHeight.toInt(), true)


    }

绘制背景色

less 复制代码
/**
     * 绘制背景色
     */
    private fun drawScanBackground(canvas: Canvas?) {
        mPaint?.style = Paint.Style.FILL
        mPaint?.color = mBackgroundColor
        val canvasWidth = canvas?.width
        val canvasHeight = canvas?.height
        mPaint?.let {
            canvas?.drawRect(0f, 0f, canvasWidth!!.toFloat(), mRectFrameRect!!.top, it)
            canvas?.drawRect(
                0f,
                mRectFrameRect!!.top - mBorderWidth / 2,
                mRectFrameRect!!.left,
                mRectFrameRect!!.bottom + mBorderWidth / 2,
                it
            )
            canvas?.drawRect(
                mRectFrameRect!!.right,
                mRectFrameRect!!.top - mBorderWidth / 2,
                canvasWidth!!.toFloat(),
                mRectFrameRect!!.bottom,
                it
            )
            canvas?.drawRect(
                0f,
                mRectFrameRect!!.bottom - mBorderWidth / 2,
                canvasWidth!!.toFloat(),
                canvasHeight!!.toFloat(),
                it
            )
        }

    }

将阴影部分分为四块,使用canvas.drawRect分别绘制。

绘制边框线

kotlin 复制代码
/**
     * 画边框线
     */
    private fun drawBorderLine(canvas: Canvas?) {
        mPaint?.color = mRectColor
        mPaint?.style = Paint.Style.STROKE
        mPaint?.strokeWidth = mRectWidth.toFloat()
        mRectFrameRect?.let { mPaint?.let { it1 -> canvas?.drawRect(it, it1) } }
    }

通过上面定义的扫描框矩形,绘制扫描框的边框线。

绘制四个边角线

四个边角线为折线,使用自定义view中的path实现比较简单。

scss 复制代码
    /**
     * 画四个角
     */
    private fun drawBorderCorner(canvas: Canvas?) {
        mPaint?.color = mBorderColor
        mPaint?.style = Paint.Style.STROKE
        val connerWidth = mBorderWidth / 2
        mPaint?.strokeWidth = mBorderWidth.toFloat()

        mPath.reset()
        //左上     
        mPath.moveTo(mRectFrameRect!!.left, mRectFrameRect!!.top - connerWidth + mBorderLength)
        mPath.lineTo(mRectFrameRect!!.left, mRectFrameRect!!.top)
        mPath.lineTo(mRectFrameRect!!.left - connerWidth + mBorderLength, mRectFrameRect!!.top)
         //右上     
        mPath.moveTo(mRectFrameRect!!.right + connerWidth - mBorderLength, mRectFrameRect!!.top)
        mPath.lineTo(mRectFrameRect!!.right, mRectFrameRect!!.top)
        mPath.lineTo(mRectFrameRect!!.right, mRectFrameRect!!.top - mBorderWidth + mBorderLength)
        //左下    
        mPath.moveTo(mRectFrameRect!!.left, mRectFrameRect!!.bottom + mBorderWidth - mBorderLength)
        mPath.lineTo(mRectFrameRect!!.left, mRectFrameRect!!.bottom)
        mPath.lineTo(mRectFrameRect!!.left - mBorderWidth + mBorderLength, mRectFrameRect!!.bottom)
         //右下 
        mPath.moveTo(mRectFrameRect!!.right, mRectFrameRect!!.bottom + mBorderWidth - mBorderLength)
        mPath.lineTo(mRectFrameRect!!.right, mRectFrameRect!!.bottom)
        mPath.lineTo(mRectFrameRect!!.right + mBorderWidth - mBorderLength, mRectFrameRect!!.bottom)

        canvas?.drawPath(mPath, mPaint!!)
    }

扫描线绘制及移动

绘制扫描线使用了canvas.drawBitmap 方法 ,通过裁剪显示位置绘制扫描图片。

scss 复制代码
  /**
     * 绘制扫描线
     */
    private fun drawScanLine(canvas: Canvas?) {
        canvas?.save()
        canvas?.restore()

        val dstGridRectF = RectF(
            mRectFrameRect!!.left,
            mRectFrameRect!!.top,
            mRectFrameRect!!.right,
            mRectFrameRect!!.top + mMoveStep
        )
        val srcRect = Rect(
            0,
            (mLineBitmap!!.height - dstGridRectF.height()).toInt(),
            mLineBitmap!!.width,
            mLineBitmap!!.height
        )
        mLineBitmap?.let {
            canvas?.drawBitmap(it, srcRect, dstGridRectF, mPaint)
        }

        mMoveStep += dp2px(3F)
        if (mMoveStep >= mScanWidth + mLineBitmap!!.height / 2) {
            mMoveStep = 0F
        }
        postInvalidateDelayed(mDelayTimes.toLong())
    }

这里通过调用postInvalidateDelayed 不停延迟绘制图片来实现扫描图的移动效果。

特点

  1. 像zxing 等三方库 直接使用扫描图片来绘制效果,由于扫描框是正方形,如果网格扫描图是长方形图片,则会导致被拉伸为正方形显示,图片变形。为了解决网格图的变形问题,这里对图片进行缩放处理,避免变形。
ini 复制代码
 val scaleHeight = mScanWidth.toFloat() / mLineBitmap!!.width * mLineBitmap!!.height
        mLineBitmap =
            Bitmap.createScaledBitmap(mLineBitmap!!, mScanWidth, scaleHeight.toInt(), true)
  1. 代码简洁,不用区分是线性扫描还是网格扫描,都可以直接使用,使用时,只需传入需要的扫描图片即可。

项目地址:github.com/AndroidYou/...

相关推荐
Flittly3 分钟前
【SpringSecurity新手村系列】(7)基于资源权限码(Authority)的接口权限控制实战
java·spring boot·安全
用户5757303346246 分钟前
拒绝“首屏爆炸”:用 React 哨兵模式与懒加载打造丝滑列表
前端
ECT-OS-JiuHuaShan17 分钟前
哲学的本质,是递归因果
java·开发语言·人工智能·科技·算法·机器学习·数学建模
倾听一世,繁花盛开33 分钟前
Java语言程序设计——篇十三(1)
java·开发语言·ide·eclipse
大腕先生35 分钟前
通用分页超详细介绍(附带源代码解析&页面展示效果)
xml·java·linux·服务器·开发语言·前端·idea
A_aspectJ39 分钟前
如何抓住Java开发岗的市场红利?从需求端反推学习路径
java·开发语言·职场和发展
睿智的海鸥40 分钟前
Markdown 语法大全详解
开发语言·前端·javascript·css·html
XS0301061 小时前
Java 基础(九) IO流
java·开发语言·php
Highcharts.js1 小时前
用Highcharts如何动态向一个序列添加点
前端·javascript·react.js·highcharts
_Evan_Yao1 小时前
缓存金字塔上的红色闪电:Redis 如何借力 CPU 的 L1/L2/L3 与 TLB 飞驰
java·数据库·redis·后端·缓存