美文网首页安卓集中营精选案例Android开发实战总结
Android二维码(仿微信,轻量Zxing)

Android二维码(仿微信,轻量Zxing)

作者: 美猴王丶 | 来源:发表于2016-11-27 23:45 被阅读8367次

前言#

要做一个功能,二维码识别。网上找一堆相关的Demo,但是总不是想要的效果,或者都是多年前的版本,权衡考虑之后,决定亲自操刀。不纠结直接选中Zxing框架,https://github.com/zxing/zxing 在网站上直接clone下来,运行,然后就发现问题了...

选Zxing存在的问题#

  • 为什么是横屏,调成竖屏,居然有问题
  • 这个包居然有好多用不着的代码
  • 默认识别的界面不是想要的效果
  • 加个Title在顶部之后识别框居然不居中

发现问题,那么本文的优点就来了,且听一一道来#

  • 集成速度快,相关核心功能都已经再次封装好
  • 最新的V3.30的工程,识别速度快,基本见图秒识别
  • 解决横竖屏的问题,通过设置Activity的android:screenOrientation="portrait" 方式设置,就可以自适应横屏竖屏
  • 去掉工程中无用的代码,留下最核心的代码,实现最最最轻量级
  • 自定义AutoScannerView控件,实现微信识别区域的效果
  • 解决工程之间只以屏幕为居中的问题,目前可以根据设置宽度高度自适应居中
  • 具体示例参考:https://github.com/yangxixi88/ZxingLite

光说不练假把式,上动图#

模仿微信的效果,真机上效果更好,录屏的将就着看
默认效果

集成方式#

1、导入zxinglite工程,Andorid Studio通过Import Module方式导入
2、导入目标工程之后,如果有存在R等资源文件未找到,可以在菜单栏Build->Make Module zxinglite 即可
3、添加两个权限

<uses-permission android:name="android.permission.CAMERA" /> 
<uses-permission android:name="android.permission.VIBRATE" />

4、集成BaseCaptureActivity,实现getSurfaceView()和dealDecode()等方法
5、布局样式,仿微信效果则用AutoScannerView,默认效果使用com.google.zxing.client.android.ViewfinderView

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_wechat_capture"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="yangxixi.zxinglib.WeChatCaptureActivity">

    <SurfaceView
        android:id="@+id/preview_view"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />

    <com.google.zxing.client.android.AutoScannerView
        android:id="@+id/autoscanner_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

6、仿微信效果的Activity

/**
 * 模仿微信的扫描界面
 */
public class WeChatCaptureActivity extends BaseCaptureActivity {

    private static final String TAG = WeChatCaptureActivity.class.getSimpleName();

    private SurfaceView surfaceView;
    private AutoScannerView autoScannerView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_wechat_capture);
        surfaceView = (SurfaceView) findViewById(R.id.preview_view);
        autoScannerView = (AutoScannerView) findViewById(R.id.autoscanner_view);
    }

    @Override
    protected void onResume() {
        super.onResume();
        autoScannerView.setCameraManager(cameraManager);
    }

    @Override
    public SurfaceView getSurfaceView() {
        return (surfaceView == null) ? (SurfaceView) findViewById(R.id.preview_view) : surfaceView;
    }

    @Override
    public void dealDecode(Result rawResult, Bitmap barcode, float scaleFactor) {
        Log.i(TAG, "dealDecode ~~~~~ " + rawResult.getText() + " " + barcode + " " + scaleFactor);
        playBeepSoundAndVibrate(true, false);
        Toast.makeText(this, rawResult.getText(), Toast.LENGTH_LONG).show();
//        对此次扫描结果不满意可以调用
//        reScan();
    }
}

默认效果Activity

/**
 * 默认的扫描界面
 */
public class DefaultCaptureActivity extends BaseCaptureActivity {

    private static final String TAG = DefaultCaptureActivity.class.getSimpleName();

    private SurfaceView surfaceView;
    private ViewfinderView viewfinderView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_capture);
        surfaceView = (SurfaceView) findViewById(R.id.preview_view);
        viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);
    }

    @Override
    public SurfaceView getSurfaceView() {
        return (surfaceView == null) ? (SurfaceView) findViewById(R.id.preview_view) : surfaceView;
    }

    @Override
    public ViewfinderView getViewfinderHolder() {
        return (viewfinderView == null) ? (ViewfinderView) findViewById(R.id.viewfinder_view) : viewfinderView;
    }

    @Override
    public void dealDecode(Result rawResult, Bitmap barcode, float scaleFactor) {
        Log.i(TAG, "dealDecode ~~~~~ " + rawResult.getText() + " " + barcode + " " + scaleFactor);
        playBeepSoundAndVibrate();
        Toast.makeText(this, rawResult.getText(), Toast.LENGTH_LONG).show();
//        对此次扫描结果不满意可以调用
//        reScan();
    }
}

自定义AutoScannerView的实现

/**
 * Created by yangxixi on 16/11/22.
 *
 * 自动上下扫描
 */

public class AutoScannerView extends View {

    private static final String TAG = AutoScannerView.class.getSimpleName();

    private Paint maskPaint;
    private Paint linePaint;
    private Paint traAnglePaint;
    private Paint textPaint;
    private CameraManager cameraManager;

    private final int maskColor = Color.parseColor("#60000000");                          //蒙在摄像头上面区域的半透明颜色
    private final int triAngleColor = Color.parseColor("#76EE00");                        //边角的颜色
    private final int lineColor = Color.parseColor("#FF0000");                            //中间线的颜色
    private final int textColor = Color.parseColor("#CCCCCC");                            //文字的颜色
    private final int triAngleLength = dp2px(20);                                         //每个角的点距离
    private final int triAngleWidth = dp2px(4);                                           //每个角的点宽度
    private final int textMarinTop = dp2px(30);                                           //文字距离识别框的距离
    private int lineOffsetCount = 0;

    public AutoScannerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        maskPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        maskPaint.setColor(maskColor);

        traAnglePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        traAnglePaint.setColor(triAngleColor);
        traAnglePaint.setStrokeWidth(triAngleWidth);
        traAnglePaint.setStyle(Paint.Style.STROKE);

        linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        linePaint.setColor(lineColor);

        textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        textPaint.setColor(textColor);
        textPaint.setTextSize(dp2px(14));
    }

    public void setCameraManager(CameraManager cameraManager) {
        this.cameraManager = cameraManager;
        invalidate();//重新进入可能不刷新,所以调用一次。
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (cameraManager == null)
            return;
        Rect frame = cameraManager.getFramingRect();
        Rect previewFrame = cameraManager.getFramingRectInPreview();
        if (frame == null || previewFrame == null) {
            return;
        }

        int width = canvas.getWidth();
        int height = canvas.getHeight();

        // 除了中间的识别区域,其他区域都将蒙上一层半透明的图层
        canvas.drawRect(0, 0, width, frame.top, maskPaint);
        canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, maskPaint);
        canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, maskPaint);
        canvas.drawRect(0, frame.bottom + 1, width, height, maskPaint);

        String text = "将二维码放入框内,即可自动扫描";
        canvas.drawText(text, (width - textPaint.measureText(text)) / 2, frame.bottom + textMarinTop, textPaint);

        // 四个角落的三角
        Path leftTopPath = new Path();
        leftTopPath.moveTo(frame.left + triAngleLength, frame.top + triAngleWidth / 2);
        leftTopPath.lineTo(frame.left + triAngleWidth / 2, frame.top + triAngleWidth / 2);
        leftTopPath.lineTo(frame.left + triAngleWidth / 2, frame.top + triAngleLength);
        canvas.drawPath(leftTopPath, traAnglePaint);

        Path rightTopPath = new Path();
        rightTopPath.moveTo(frame.right - triAngleLength, frame.top + triAngleWidth / 2);
        rightTopPath.lineTo(frame.right - triAngleWidth / 2, frame.top + triAngleWidth / 2);
        rightTopPath.lineTo(frame.right - triAngleWidth / 2, frame.top + triAngleLength);
        canvas.drawPath(rightTopPath, traAnglePaint);

        Path leftBottomPath = new Path();
        leftBottomPath.moveTo(frame.left + triAngleWidth / 2, frame.bottom - triAngleLength);
        leftBottomPath.lineTo(frame.left + triAngleWidth / 2, frame.bottom - triAngleWidth / 2);
        leftBottomPath.lineTo(frame.left + triAngleLength, frame.bottom - triAngleWidth / 2);
        canvas.drawPath(leftBottomPath, traAnglePaint);

        Path rightBottomPath = new Path();
        rightBottomPath.moveTo(frame.right - triAngleLength, frame.bottom - triAngleWidth / 2);
        rightBottomPath.lineTo(frame.right - triAngleWidth / 2, frame.bottom - triAngleWidth / 2);
        rightBottomPath.lineTo(frame.right - triAngleWidth / 2, frame.bottom - triAngleLength);
        canvas.drawPath(rightBottomPath, traAnglePaint);

        //循环划线,从上到下
        if (lineOffsetCount > frame.bottom - frame.top - dp2px(10)) {
            lineOffsetCount = 0;
        } else {
            lineOffsetCount = lineOffsetCount + 6;
//            canvas.drawLine(frame.left, frame.top + lineOffsetCount, frame.right, frame.top + lineOffsetCount, linePaint);    //画一条红色的线
            Rect lineRect = new Rect();
            lineRect.left = frame.left;
            lineRect.top = frame.top + lineOffsetCount;
            lineRect.right = frame.right;
            lineRect.bottom = frame.top + dp2px(10) + lineOffsetCount;
            canvas.drawBitmap(((BitmapDrawable)(getResources().getDrawable(R.drawable.scanline))).getBitmap(), null, lineRect, linePaint);
        }
        postInvalidateDelayed(10L, frame.left, frame.top, frame.right, frame.bottom);
    }

    private int dp2px(int dp) {
        float density = getContext().getResources().getDisplayMetrics().density;
        return (int) (dp * density + 0.5f);
    }
}

集成之后,基本就可以,如果需要设置横屏竖屏直接设置screenOrientation就好了,里面的效果适配效果都已经实现;直接在dealDecode中处理扫描之后的结果,playBeepSoundAndVibrate可以设置响铃,振动或者同时都可以。想设置微信识别框的参数可以再AutoScannerView中修改,这里就不精细去实现哒

结语#

到这里,大致流程就介绍完了。有什么不足的,欢迎指出。
欢迎在下方评论和喜欢,谢谢,转载请说明出处。

具体示例,请跳转Github查看,地址:https://github.com/yangxixi88/ZxingLite 欢迎StarStarStar啊~~~

读完思考#

自定义AutoScannerView是匀速从上到下,而细细观察微信的效果,是两头慢,中间快,如果所以实现微信那种效果,需要从哪些方面改进呢,欢迎讨论~~

相关文章

网友评论

  • Wezarp:老哥,最近抽时间跑了你demo,发现解析效率还是挺高的,但是发现在一些带边边角角的定制二维码面前还是比较无奈的,这个问题我已经解决了,但是我水平有限,没法合进你的项目,你的项目还在维护吗?我想给你提点意见
  • 大表哥007:怎么回显数据
  • d0f801cf222d:为什么我的viov扫描的时候是黑色的
  • 0832b1ebfbc6:您好,我集成您的库之后,触发扫码操作,扫码框出来了,但是页面每次得有一秒左右的延迟才能进去,请问您遇到过这种问题吗?
  • 猪爸爸Hulk:谢谢大神分享,我试了试扫描页面被拉伸了,请问怎么解决?
  • 寻行路人:垃圾demo 扫描二维码出现拉升现象,扫描 带图片的二维码半小时都扫描不出来,大家千万别用
    彼岸青园:别人是开源的,你这样说有意思吗?你有能力你自己写一个啊!

    这种把开源免费当做理所当然的人真让人看不下去
  • 6ac72b57d077:你好 使用这个库后我发现画面的宽似乎被拉长了一些 有解决方案吗
  • 清枫浦上:使用魅蓝X手机扫描时会出现二维码拉伸的情况
  • 小小橙123:噢,能把二维码生成的也抽取出来么?
    小小橙123:@Gentleman1995 花点时间看看代码,你就会弄了
    猪爸爸Hulk:@小小橙123 有生成二维码的代码吗?求指教
    小小橙123:后来仔细看了一下,里面有生成二维码的代码
  • 小小橙123:写的不错,多谢楼主。
  • 段浅浅儿:运行demo后发现,第一次扫描解析成功,之后都失败了
    段浅浅儿:哦哦 抱歉,是我的问题,忘记调用rescan()了,哈哈哈
  • AndroidXiaoKY:而且也没有报错
  • AndroidXiaoKY:作者你好,下载你的demo运行后,发现横屏无法扫描,会直接强退
  • code小生:问下你的录屏工具是啥?
    code小生:@code小生 这样太麻烦,我就想看看有没有直接可以录制手机屏幕为gif格式的工具
    美猴王丶:@code小生 手机上用的易录屏,再把录的视频用mac上的软件转换成gif。其实还有更好的方案的,我是觉得用真机录比较流畅。
  • sendtion:支持下

本文标题:Android二维码(仿微信,轻量Zxing)

本文链接:https://www.haomeiwen.com/subject/vsbnpttx.html