Android二维码开发(二)优化

[TOC]

前言

上一篇,扫码的基本功能已经实现,不过还存在一些问题

  • 扫码界面不是我们常见的二维码扫描界面
  • 方法调用过于繁琐,有大量的重复代码

本篇博文将介绍如何对第三方库进行二次开发,自定义我们想要的扫码界面以及对方法进行封装方便调用

自定义扫码界面

获取第三方库源码

  1. 下载源码

因为本篇是基于 zxing-android-embedded进行开发,因此首先获取该库的源码,打开该库的github地址,最新版本是3.5.0,,将项目clone或者download下来,下载后的项目结构入下图所示:

tag.png

其中红框所示的zxing-android-embedded库就是我们正在使用的库,其目录结构如下所示

tag2.png
  1. 导入源码到项目中:

这个库的结构和常见的android studio创建的model不太一样,为了方便开发,我们选择重新创建一个android-library的model并将该库的代码导入。

第一步:

重新创建一个android-library的model,这里取名为zxingcore

tag3.png

第二步:

zxing-android-embedded目录下的src里面的所有java代码拷贝到zxingcore中java目录下;将res和res-orig两个目录下的文件全部拷贝到zxingcore的res目录下

tag4.png

将AndroidManifest.xml里面的内容拷贝到zxingcore中的AndroidManifest.xml。这里主要是权限的申请和扫码界面activity的注册,对于android 6.0及以上版本,需要再实际项目中进行动态权限的申请。

tag5.png

因为这个库需要引用到谷歌zxing库中的core代码,所以需要再build.gradle中加入对该库的依赖

 compile 'com.google.zxing:core:3.2.1'

到此这个第三方库的导入已经完成,我们上一篇sample中通过gradle依赖zxing-android-embedded可以更改为依赖我们自己导入的zxingcore

// compile 'com.journeyapps:zxing-android-embedded:3.4.0'
// compile 'com.google.zxing:core:3.2.1'

  compile project(':zxingcore')

自定义扫码界面

在上一篇中我们通过以下代码开启了扫码界面

 new IntentIntegrator(this)
                .setOrientationLocked(false)
                .setDesiredBarcodeFormats(IntentIntegrator.ALL_CODE_TYPES)
                .setPrompt("将二维码/条码放入框内,即可自动扫描")
                .initiateScan(); // 初始化扫描

这个开启的界面就是CaptureActivity这个activity,这当然不是我们想要的界面,需要调整的地方有三个

  • 方向调整,默认是横向的
  • 扫码框大小调整
  • 扫码框样式调整
  1. 调整方向

调整方向很简单,把manifest中activity声明的下面代码去掉就好

 android:screenOrientation="sensorLandscape"//去掉这段代码
  1. 扫码框大小调整

打开activity布局文件的zxing_capture.xml,代码很简单,只有一个DecoratedBarcodeView

<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <!--
    This Activity is typically full-screen. Therefore we can safely use centerCrop scaling with
    a SurfaceView, without fear of weird artifacts. -->
    <com.journeyapps.barcodescanner.DecoratedBarcodeView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/zxing_barcode_scanner"
        app:zxing_preview_scaling_strategy="centerCrop"
        app:zxing_use_texture_view="false"/>

</merge>

在DecoratedBarcodeView中加入两行代码 大小可以自行设置

 app:zxing_framing_rect_height="150dp"//扫码框高
 app:zxing_framing_rect_width="200dp"//扫码框宽

重新build项目并运行我们的sample可以看到界面已经好看很多了,扫码框也变成了正常的大小

tag6.png

优化扫码框

扫码界面虽然变了,不过这还不是我们想要的,先看一下微信的扫码框

weixin.png

可以看到,正常扫码框都有一个滑动的条条和四个边框,接下来就来添加这部分东西。

从上面的步骤可以看出整个扫码界面其实就是一个DecoratedBarcodeView,打开DecoratedBarcodeView的代码可以看到它引用的布局就是layout文件中的zxing_barcode_scanner.xml,打开该布局文件

<merge xmlns:android="http://schemas.android.com/apk/res/android">
    
    //封装了摄像头的一个类,用来获取拍摄画面
    <com.journeyapps.barcodescanner.BarcodeView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/zxing_barcode_surface"/>
    
    // 类似遮罩的作用覆盖在BarcodeView上
    <com.journeyapps.barcodescanner.ViewfinderView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/zxing_viewfinder_view"/>
    
    //提示语
    <TextView android:id="@+id/zxing_status_view"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_gravity="bottom|center_horizontal"
              android:background="@color/zxing_transparent"
              android:text="@string/zxing_msg_default_status"
              android:textColor="@color/zxing_status_text"/>
</merge>

三个view我都做了注释,想要改变看到的扫码界面和扫码框,需要重写的就是ViewfinderView这个类

首先明确一下我们需要加入的东西

  • 取消原有红色条,加入滑动的扫码条
  • 四个边框
  • 提示文字移动到扫码框下方

首先定义需要的属性和方法,在修改源码的时候我习惯将自己写的代码都放在最后面,并且用一条分割线分割,

  1. 定义属性
 /*-----------------------自定义方法和属性--------------------------*/
    //画边框相关属性
    private Paint mLinePaint;//边框画笔
    private final int mLineColor = Color.BLUE;//边框的颜色

    //滑动条相关属性
    private Bitmap mLineBm;//滑动条图片
    private RectF mLineReact;//滑动条区域
    private final int mStepSize = 12;//滑动条每次滑动的速度
    private final int mLineHeight = 30;//滑动条的高度
    private boolean isBottom = false;//滑动条是否滑动到扫码框底部

    //文字相关属性
    private Paint mTextPaint;//画提示语的画笔
    private String mPromptText;//扫码的提示语
    private int mTextMargin;//提示语距离扫描框的大小
  1. 定义方法

有了属性自然需要有初始化的方法和操作的逻辑代码 创建2个新方法,初始化一般放在构造方法中,操作的代码是在onDraw中调用的

 /**
     * 改方法在构造方法中调用用来初始化属性
     *
     * @param context
     */
    private void customInit(Context context) {
        //初始化滑动线的画笔
        mLinePaint = new Paint();
        mLinePaint.setStyle(Paint.Style.FILL);
        mLinePaint.setStrokeWidth(20);
        mLinePaint.setColor(mLineColor);
        //初始化滑动条
        mLineBm = BitmapFactory.decodeResource(getResources(), R.drawable.lan);
        //初始化提示语的画笔
        mTextPaint = new Paint();
        mTextPaint.setColor(Color.WHITE);
        mTextPaint.setTextSize(sp2px(14));
        mTextPaint.setTextAlign(Paint.Align.CENTER);
        mTextMargin = sp2px(20);
    }



    /**
     * 该方法在onDraw中调用,放在  
     * Rect frame = framingRect;
     * Rect previewFrame = previewFramingRect;
     * 两段代码之后
     *
     * @param frame
     * @param canvas
     */
    private void customDraw(Rect frame, Canvas canvas) {
        drawSlipLine(frame, canvas);//画滑动的线
        drawEdge(frame, canvas);//画边框
        drawPromptText(frame, canvas);//画提示语
    }



  1. 画滑动的线 drawSlipLine(frame, canvas)

首先要去掉原先的红色线,注释掉onDraw()方法中下图所示代码 ,还可以注释掉下面那一部分画跳动小点的代码,取消跳动小点,看个人需求。滑动的线一般是一张图片,这类我准备了一张图放在drawable目录下 lan.png 图片可以自行替换

tag8.png

实现drawSlipLine()方法

/**
     * 画移动的短线
     *
     * @param frame
     * @param canvas
     */
    private void drawSlipLine(Rect frame, Canvas canvas) {
       if (mLineReact == null) {
            mLineReact = new RectF(frame.left + 5, frame.top, frame.right - 5, frame.top + mLineHeight);
        }

        if (isBottom) {
            mLineReact.set(frame.left + 5, frame.top, frame.right - 5, frame.top + mLineHeight);
        }
        mLineReact.offset(0, mStepSize);
        canvas.drawBitmap(mLineBm, null, mLineReact, null);

        isBottom = mLineReact.bottom + mStepSize > frame.bottom;
    }

效果

tag9.png
  1. 画边框 drawEdge(frame, canvas)

边框的颜色,在边界内还是边界外可以自行调整位置,这里面传入的fram是扫描框所在的长方形Rect

 /**
     * 画框边的四个角
     *
     * @param frame
     * @param canvas
     */
    private void drawEdge(Rect frame, Canvas canvas) {
        canvas.drawRect(frame.left - 10, frame.top, frame.left, frame.top + 50, mLinePaint);
        canvas.drawRect(frame.left - 10, frame.top - 10, frame.left + 50, frame.top, mLinePaint);
        canvas.drawRect(frame.right - 50, frame.top - 10, frame.right + 10, frame.top, mLinePaint);
        canvas.drawRect(frame.right, frame.top, frame.right + 10, frame.top + 50, mLinePaint);

        canvas.drawRect(frame.left - 10, frame.bottom - 50, frame.left, frame.bottom, mLinePaint);
        canvas.drawRect(frame.left - 10, frame.bottom, frame.left + 50, frame.bottom + 10, mLinePaint);
        canvas.drawRect(frame.right - 50, frame.bottom, frame.right, frame.bottom + 10, mLinePaint);
        canvas.drawRect(frame.right, frame.bottom - 50, frame.right + 10, frame.bottom + 10, mLinePaint);

    }

效果

tag10.png
  1. 画提示语

从上面DecoratedBarcodeView的布局可以看出,提示语是一个textview,位置是bottom,这不符合我们的需要,通过调整textview距离底边框的margin也可以调整位置,不过我这边采用的是在ViewfinderView遮罩层画这个提示语,取消textview

第一步,注释掉zxing_barcode_scanner.xml里面的textview

<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <com.journeyapps.barcodescanner.BarcodeView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/zxing_barcode_surface"/>

    <com.journeyapps.barcodescanner.ViewfinderView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/zxing_viewfinder_view"/>

    <!--<TextView android:id="@+id/zxing_status_view"-->
              <!--android:layout_width="wrap_content"-->
              <!--android:layout_height="wrap_content"-->
              <!--android:layout_gravity="bottom|center_horizontal"-->
              <!--android:background="@color/zxing_transparent"-->
              <!--android:text="@string/zxing_msg_default_status"-->
              <!--android:textColor="@color/zxing_status_text"/>-->
</merge>

第二步,需要将提示语传递到ViewfinderView内部去,还记得提示语是在哪里设置的吗?重新看一下我们打开扫码页面的代码,

  new IntentIntegrator(this)
                .setOrientationLocked(false)
                .setDesiredBarcodeFormats(IntentIntegrator.ALL_CODE_TYPES)
                .setPrompt("将二维码/条码放入框内,即可自动扫描")
                .initiateScan(); // 初始化扫描

通过setPrompt()方法传递,跟踪这个方法可以发现所有设置的信息最后都会放到一个intent并传递给CaptureActivity,重新打开CaptureActivity,在onCreate()中将设置的参数传递给一个CaptureManager

  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        barcodeScannerView = initializeContent();
        //初始化配置扫码界面
        capture = new CaptureManager(this, barcodeScannerView);
        //intent中携带了通过IntentIntegrator设置的参数
        capture.initializeFromIntent(getIntent(), savedInstanceState);
        capture.decode();
    }

CaptureManager内部再次把intent传递给DecoratedBarcodeView的initializeFromIntent(Intent intent)方法,看这个方法中有这么一句

  String customPromptMessage = intent.getStringExtra(Intents.Scan.PROMPT_MESSAGE);
        if (customPromptMessage != null) {
            setStatusText(customPromptMessage);
        }
    /**
    
 /**
     * 设置提示语
     * @param text
     */
    public void setStatusText(String text) {
        // statusView is optional when using a custom layout
        if(statusView != null) {
            statusView.setText(text);
        }
    }        
        

可以看出在这个方法里面将提示语设置给了statusView(即我们上面注释掉的textview)

理清了这个再进行修改就容易了

  • 首先去掉DecoratedBarcodeView中statusView变量和与它相关的代码,注释掉就好了。
  • 在ViewfinderView中添加一个方法,mPromptText属性的set()方法,这个属性我们一开始已经定义过了。在DecoratedBarcodeView的setStatusText中调用
  • 完善之前在ViewfinderView中定义的drawPromptText(frame, canvas)方法将文字画到扫描界面
 /**
     * 传入提示语
     *
     * @param text
     */
    public void setPromptText(String text) {
        this.mPromptText = text;
    }

修改DecoratedBarcodeView的setStatusText()方法

   public void setStatusText(String text) {
        // statusView is optional when using a custom layout
//        if(statusView != null) {
//            statusView.setText(text);
//        }
        //viewFinder就是DecoratedBarcodeView持有的ViewfinderView
        viewFinder.setPromptText(text);
    }

这样我们已经把提示语传递到了ViewfinderView中,接下来就是将它画到扫描框的下方,具体想要画的位置可以自行调整。

实现drawPromptText(frame, canvas)的代码, mTextPaint的初始化代码customInit()中,文字样式可以自行调整

 /**
     * 画提示语
     *
     * @param frame
     * @param canvas
     */
    private void drawPromptText(Rect frame, Canvas canvas) {
        int startX = frame.left + frame.width() / 2;
        int startY = frame.bottom + mTextMargin;
        if (!TextUtils.isEmpty(mPromptText)) {
            canvas.drawText(mPromptText, startX, startY, mTextPaint);
        }
    }

效果

tag11.png

扫码框位置调整

默认扫码框是在整个屏幕的中间,如果想要调整扫码框的位置,比如上移或者下一的话,找到CameraPreview中的calculateFramingRect方法,添加如下代码 intersection.offset(0,-150); 具体调整根据需求而定。

 protected Rect calculateFramingRect(Rect container, Rect surface) {
        // intersection is the part of the container that is used for the preview
        Rect intersection = new Rect(container);
        boolean intersects = intersection.intersect(surface);

        if(framingRectSize != null) {
            // Specific size is specified. Make sure it's not larger than the container or surface.
            int horizontalMargin = Math.max(0, (intersection.width() - framingRectSize.width) / 2);
            int verticalMargin = Math.max(0, (intersection.height() - framingRectSize.height) / 2);
            intersection.inset(horizontalMargin, verticalMargin);
            
            /**将默认的扫码框位置上调150px*/
            intersection.offset(0,-150);
            
            return intersection;
        }
        // margin as 10% (default) of the smaller of width, height
        int margin = (int)Math.min(intersection.width() * marginFraction, intersection.height() * marginFraction);
        intersection.inset(margin, margin);
        if (intersection.height() > intersection.width()) {
            // We don't want a frame that is taller than wide.
            intersection.inset(0, (intersection.height() - intersection.width()) / 2);
        }
        return intersection;
    }

总结

通过对源码的二次开发,优化了扫码界面,初步实现了常见的扫码框效果,下一篇将介绍对API方法的封装。如有不对的地方还请指正,感谢。

demo下载

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,369评论 25 707
  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    阳明先生x阅读 15,967评论 3 119
  • 一滴阳光阅读 530评论 0 0
  • 朋友就是已经很了解你过去沧桑不堪的经历,也知道你满身是处的缺点,更清楚你艰难险阻的未来。但依然没有离你远去,没有弃...
    心静自燃美阅读 298评论 0 1
  • 好久不见,你还好吗我亲爱的那个陌生人时间繁如沙,只是我傻半个年轮独自一人描摹伤疤你换朋友我没有说话你我本就是陌生人...
    Mr橘子阅读 222评论 1 4