29
2019
01

zxing使用详情

如果你的项目中有模块跟二维码相关的话,那你一定听过或者用过大名鼎鼎的zxing开源库。

什么是zxing?

ZXing是一个开源的,用Java实现的多种格式的1D/2D条码图像处理库,它包含了联系到其他语言的端口。zxing可以实现使用手机的内置的摄像头完成条形码的扫描及解码。

本篇文章就来学习zxing的基本使用,学习了以下几个内容就能满足大部分项目中的二维码相关需求:

通过摄像头扫描二维码图片,读取图片内容

从相册中选取二维码图片,读取图片内容

自己输入字符串内容,生成二维码图片

长按识别自己生成的二维码图片

如何依赖zxing到项目中?

如果你还在使用zxing的jar包、或者你是把zxing的代码复制到项目中,使用这两种方式依赖的话那就out了,现在Android Studio可支持zxing在线依赖,目前最新版本是3.3.3。在线依赖的好处我就不多说了,相信大家都懂。

新建项目,在app/build.gradle文件中在线依赖:

implementation 'com.google.zxing:core:3.3.3'

加入权限

因为扫描二维码需要摄像头权限,把图片保存到本地需要sdcard权限,所以需要在AndroidManifest.xml中加入相应的权限

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

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

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

<uses-feature android:name="android.hardware.camera" />

<uses-feature android:name="android.hardware.camera.autofocus" />

当然现在市面上的手机大部分都是6.0以上的操作系统了,所以还得在MainActivity的onCreate方法中动态申请以上这两个权限。

 //6.0版本或以上需请求权限

String[] permissions=new String[]{Manifest.permission.

        WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA};

if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {

    requestPermissions(permissions,PERMS_REQUEST_CODE);

}

扫描二维码

项目依赖进来了,权限也有了,开始用代码实现第一个功能。点击扫描二维码按钮,开启一个ScanActivity,这个Activity是我之前封装好的,里面处理了扫描二维码的整个流程,扫描成功后会把扫描结果返回。ScanActivity类的代码有点多,就不贴出来了,有兴趣的自己看源码。

Intent intent = new Intent(MainActivity.this,ScanActivity.class);

startActivityForResult(intent,SCAN_REQUEST_CODE);

重写onActivityResult方法,监听扫描结果。

@Override

protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent intent) {

    super.onActivityResult(requestCode, resultCode, intent);

    if (requestCode == SCAN_REQUEST_CODE && resultCode == RESULT_OK) {

        String input = intent.getStringExtra(ScanActivity.INTENT_EXTRA_RESULT);

        showToast("扫描结果:"+input);

    }

}

从相册中选择二维码图片进行识别

首先启动系统相册,从相册中选择一张图片。

Intent innerIntent = new Intent(Intent.ACTION_PICK,

MediaStore.Images.Media.EXTERNAL_CONTENT_URI);

Intent wrapperIntent = Intent.createChooser(innerIntent, "选择二维码图片");

startActivityForResult(wrapperIntent, SELECT_IMAGE_REQUEST_CODE);

然后在onActivityResult中获取选择图片路径,调用BitmapUtil.parseQRcode方法解析二维码图片。

@Override

protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent intent) {

    super.onActivityResult(requestCode, resultCode, intent);

    if(requestCode==SELECT_IMAGE_REQUEST_CODE){//从图库选择图片

        String[] proj = {MediaStore.Images.Media.DATA};

        // 获取选中图片的路径

        Cursor cursor = this.getContentResolver().query(intent.getData(),proj, null, null, null);

        if (cursor.moveToFirst()) {

            int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);

            String photoPath = cursor.getString(columnIndex);

            String result= BitmapUtil.parseQRcode(photoPath);

            if (!TextUtils.isEmpty(result)) {

                showToast("从图库选择的图片识别结果:"+result);

            } else {

                showToast("从图库选择的图片不是二维码图片");

            }

        }

        cursor.close();

    }

}

接下来看parseQRcode方法,

/**

 * 解析二维码图片

 * @param bitmapPath 文件路径

 * @return

 */

public static String parseQRcode(String bitmapPath){

    Bitmap bitmap = BitmapFactory.decodeFile(bitmapPath, null);

    String result=parseQRcode(bitmap);

    return result;

}

从上面的方法中看到直接把文件路径读取成Bitmap,继续调用parseQRcode方法把Bitmap对象传进去,这里用到了方法重载。

public static String parseQRcode(Bitmap bmp){

    bmp=comp(bmp);//bitmap压缩  如果不压缩的话在低配置的手机上解码很慢

    int width = bmp.getWidth();

    int height = bmp.getHeight();

    int[] pixels = new int[width * height];

    bmp.getPixels(pixels, 0, width, 0, 0, width, height);

    QRCodeReader reader = new QRCodeReader();

    Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);

    hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);//优化精度

    hints.put(DecodeHintType.CHARACTER_SET,"utf-8");//解码设置编码方式为:utf-8

    try {

        Result result = reader.decode(new BinaryBitmap(

        new HybridBinarizer(new RGBLuminanceSource(width, height, pixels))), hints);

        return result.getText();

    } catch (NotFoundException e) {

        Log.i("ansen",""+e.toString());

        e.printStackTrace();

    } catch (ChecksumException e) {

        e.printStackTrace();

    } catch (FormatException e) {

        e.printStackTrace();

    }

    return null;

}

如果传入的是一个Bitmap对象,先调用comp方法对Bitmap进行压缩(压缩代码这里不贴出),获取图片宽高,把图像的每个像素颜色转为int值,存入pixels数组。

然后初始化QRCodeReader对象,调用decode方法进行解码,这个方法有两个参数,参数1是一个BinaryBitmap对象,第二个参数是一个Map类型,key的值是DecodeHintType枚举类型,这里我们put了两个值,优化精度跟设置编码方式为。这个方法还会返回一个Result对象,最后调用result.getText()方法获取二维码内容。

生成二维码图片

生成二维码图片调用CreateQRBitmp.createQRCodeBitmap方法生成,这个方法是我们自己封装的,需要传入两个参数,参数1:图片内容、参数2:二维码图片最中间显示的logo(Bitmap对象)。

String contentString = etInput.getText().toString().trim();

if(TextUtils.isEmpty(contentString)){

    showToast("请输入二维码内容");

    return ;

}

Log.i("ansen","输入的内容:"+contentString);

Bitmap portrait = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher);

//两个方法,一个不传大小,使用默认

qrCodeBitmap = CreateQRBitmp.createQRCodeBitmap(contentString, portrait);

ivQrImage.setImageBitmap(qrCodeBitmap);

createQRCodeBitmap源码如下:

public static Bitmap createQRCodeBitmap(String content,Bitmap portrait) {

    // 用于设置QR二维码参数

    Hashtable<EncodeHintType, Object> qrParam = new Hashtable<>();

    // 设置QR二维码的纠错级别——这里选择最高H级别

    qrParam.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);

    // 设置编码方式

    qrParam.put(EncodeHintType.CHARACTER_SET, "UTF-8");

    // 生成QR二维码数据——这里只是得到一个由true和false组成的数组

    // 参数顺序分别为:编码内容,编码类型,生成图片宽度,生成图片高度,设置参数

    try {

        BitMatrix bitMatrix = new MultiFormatWriter().encode(content,

                BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, qrParam);

        // 开始利用二维码数据创建Bitmap图片,分别设为黑白两色

        int w = bitMatrix.getWidth();

        int h = bitMatrix.getHeight();

        int[] data = new int[w * h];

        for (int y = 0; y < h; y++) {

            for (int x = 0; x < w; x++) {

                if (bitMatrix.get(x, y))

                    data[y * w + x] = 0xff000000;// 黑色

                else

                    data[y * w + x] = 0x00ffffff;// -1 相当于0xffffffff 白色

            }

        }

        // 创建一张bitmap图片,采用最高的图片效果ARGB_8888

        Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);

        // 将上面的二维码颜色数组传入,生成图片颜色

        bitmap.setPixels(data, 0, w, 0, 0, w, h);

        if(portrait!=null){

            createQRCodeBitmapWithPortrait(bitmap,initProtrait(portrait));

        }

        return bitmap;

    } catch (WriterException e) {

        e.printStackTrace();

    }

    return null;

}

大部分代码都有注释,首先就是调用MultiFormatWriter对象的encode方法生成BitMatrix对象,这里我们传入5个参数,参数1:内容、参数2:二维码格式、参数3:图片宽、参数4:图片高、参数5:二维码生成的参数(例如编码方法以及纠错级别)。

拿到BitMatrix对象后开始利用二维码数据创建Bitmap图片,分别设为黑白两色,创建一个宽高一样的Bitmap对象,调用setPixels方法把上面的二维码颜色数组传入,生成图片颜色。如果中间需要添加logo调用createQRCodeBitmapWithPortrait方法。最后把Bitmap对象返回。

长按识别二维码以及保存图片

识别二维码跟从相册中选择图片进行识别功能上很相似,所以就不在做重复介绍了,就介绍一下保存图片功能。

从下面源码中看到,首先获取rootView,从rootView中获取根布局的Bitmap,然后调用ImageUtil.savePicToLocal方法保存图片。

View view = getWindow().getDecorView().getRootView();//找到当前页面的根布局

view.setDrawingCacheEnabled(true);//禁用绘图缓存

view.buildDrawingCache();

Bitmap temBitmap = view.getDrawingCache();

ImageUtil.savePicToLocal(temBitmap,MainActivity.this);

//禁用DrawingCahce否则会影响性能 ,而且不禁止会导致每次截图到保存的是缓存的位图

view.setDrawingCacheEnabled(false);//识别完成之后开启绘图缓存

showToast("保存图片到本地成功");

ImageUtil.savePicToLocal方法也比较简单,就是把一个Bitmap保存到本地Sdcard上。需要注意的是记得发送一个广播,不然需要重启手机才能在系统相册中看到这个图片。

public static void savePicToLocal(Bitmap bitmap, Context context) {

    String filePath=Environment.getExternalStorageDirectory()

    .getAbsolutePath() + "/screen"+File.separator + System.currentTimeMillis() + ".png";

    if (bitmap != null) {

        try {

            // 图片文件路径

            Log.i("ansen", "filePath:" + filePath);

            File file = new File(filePath);

            if (!file.getParentFile().exists()) {

                file.getParentFile().mkdirs();

            }

            FileOutputStream os = new FileOutputStream(file);

            bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);

            Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);

            Uri uri = Uri.fromFile(new File(filePath));

            intent.setData(uri);

            context.sendBroadcast(intent);

            os.flush();

            os.close();

        } catch (Exception e) {

        }

    }

}

代码终于写完了,接下来看看效果,由于模拟器没有摄像头,而真机又不能录制Gif图片,所以摄像头扫描二维码就不演示啦,大家自己下载源码运行查看效果。

当然少不了源码,下载地址如下:

https://github.com/ansen666/ZxingTest

原文链接:https://www.qiquanji.com/post/8466.html

本站声明:网站内容来源于网络,如有侵权,请联系我们,我们将及时处理。

gzh

微信扫码关注

更新实时通知

« 上一篇 下一篇 »

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。