Android 二维码生成框架Zxing

Android 二维码生成框架Zxing

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

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

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

 
    
    
    
    

    
    

这里我们看到 标签

 

声明一项应用程序需要用到的软、硬件特性。
声明一项 的目的,是为了把应用程序所依赖的软硬件特性告知应用程序之外的对象。 本元素给出了一个 required 属性, 用于指定应用程序是否必需该项特性,也即不声明该项特性的话就无法正常运行; 或者最好是提供该项特性,但没有的话也能运行。 由于每种 Android 设备提供的特性各不相同, 元素发挥着重要作用, 应用程序可以用它来描述其用到的各种设备特性。
假设应用程序需要使用蓝牙和摄像头设备,则应声明两个元素:


通常,应该确保为应用程序需要的所有特性均声明了 元素。
元素的声明仅仅是告知性质的,这意味着 Android 系统本身不会在安装程序前检查设备是否支持这些特性。 不过,其他服务(如 Google Play )或者其他应用程序可以检查 声明来进行相应处理或与本应用程序进行交互。 因此,对需要用到的所有特性都进行声明(如下表所示)是非常重要的。

有些设备特性可能会存在一些特殊的属性,用于定义该特性的版本,比如 Open GL 版本(用 glEsVersion声明)。 其他的一些与硬件是否就绪有关的特性,比如摄像头,则通过 name 属性进行声明。

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

public class EightTeenActivity extends AppCompatActivity implements View.OnClickListener {
    private EditText etInput;
    private Bitmap qrCodeBitmap;
    private ImageView ivQrImage;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_eight_teen);
        etInput = findViewById(R.id.et_input);
        ivQrImage = findViewById(R.id.iv_qr_image);
        findViewById(R.id.btn_scanning).setOnClickListener(this::onClick);//扫描点击事件
        findViewById(R.id.btn_select).setOnClickListener(this::onClick);//选择图库点击事件
        findViewById(R.id.generate_qr_code).setOnClickListener(this::onClick);//生成二维码点击事件
        findViewById(R.id.btn_long_press).setOnClickListener(this::onClick);//

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_scanning://扫描
                break;
            case R.id.btn_select:
                //激活系统库选择一张图片
                break;
            case R.id.generate_qr_code:
                //生成二维码
                String contentString =etInput.getText().toString().trim();//获取输入框中内容
                if (TextUtils.isEmpty(contentString)){
                    showToast("请输入二维码内容");
                    return;

                }
                Log.i("rocky","输入的内容:"+contentString);
                //加载资源文件的图片生成Bitmap
                  //高版本,这里会出现一个问题:找不到资源图片为null
                Bitmap portrait= BitmapFactory.decodeResource(getResources(),R.mipmap.login_icon);
                //生成二维码工具类CreateQRBitmp,两个方法,一个不传大小,使用默认
              
                qrCodeBitmap=CreateQRBitmp.createQRCodeBitmap(contentString,portrait);//生成BitMap图片
                //imageView上设置图片
                ivQrImage.setImageBitmap(qrCodeBitmap);


                break;
            case R.id.btn_long_press:
                break;
        }

    }

    private void showToast(String str){
        Toast.makeText(EightTeenActivity.this,str,Toast.LENGTH_LONG).show();
    }
}

高版本Sdk,加载资源图片logo时候,获取不到图片问题
Bitmap portrait= BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher);
Android 二维码生成框架Zxing_第1张图片
修改一下图片命名可以解决
CreateQRBitmp创建一个二维码图片

public class CreateQRBitmp {

    /**
     * 生成二维码图片大小
     */
    private static int QRCODE_SIZE = 300;
    /**
     * 头像图片大小
     */
    private static int PORTRAIT_SIZE = 55;
    /**
     * 头像图片
     */
    private Bitmap portrait;

    /**
     * 功能:创建QR二维码图片
     * 可设置图片大小和头像图片大小
     *
     * @param portrait 头像bitmap
     * @param content  生成二维码内容数据
     */
    public static Bitmap createQRCodeBitmap(String content, Bitmap portrait, int widthAndHeight, int portraitSize) {
        QRCODE_SIZE = widthAndHeight;
        PORTRAIT_SIZE = portraitSize;
        // 用于设置QR二维码参数
        Hashtable 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;
    }

    /**
     * 功能:创建QR二维码图片
     * 头像图片大小默认
     *
     * @param portrait 头像bitmap
     * @param content  生成二维码内容数据
     */
    public static Bitmap createQRCodeBitmap(String content, Bitmap portrait) {
        //用于设置QR二维码参数
        Hashtable qrParam = new Hashtable<>();//使用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){//添加最中间的logo
                createQRCodeBitmapWithPortrait(bitmap,initProtrait(portrait));
            }
            return bitmap;

        } catch (WriterException e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 初始化头像图片
     */
    public static Bitmap initProtrait(Bitmap portrait) {
        // 对原有图片压缩显示大小
        Matrix mMatrix = new Matrix();
        float width = portrait.getWidth();
        float height = portrait.getHeight();
        mMatrix.setScale(PORTRAIT_SIZE / width, PORTRAIT_SIZE / height);
        return Bitmap.createBitmap(portrait, 0, 0, (int) width,
                (int) height, mMatrix, true);
    }

    /**
     * 在二维码上绘制头像
     */
    public static void createQRCodeBitmapWithPortrait(Bitmap qr, Bitmap portrait) {
        // 头像图片的大小
        int portrait_W = portrait.getWidth();
        int portrait_H = portrait.getHeight();

        // 设置头像要显示的位置,即居中显示
        int left = (QRCODE_SIZE - portrait_W) / 2;
        int top = (QRCODE_SIZE - portrait_H) / 2;
        int right = left + portrait_W;
        int bottom = top + portrait_H;
        Rect rect1 = new Rect(left, top, right, bottom);

        // 取得qr二维码图片上的画笔,即要在二维码图片上绘制我们的头像
        Canvas canvas = new Canvas(qr);

        // 设置我们要绘制的范围大小,也就是头像的大小范围
        Rect rect2 = new Rect(0, 0, portrait_W, portrait_H);
        // 开始绘制
        canvas.drawBitmap(portrait, rect2, rect1, null);
    }
}

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

private void longPress() {
        if (qrCodeBitmap==null){
            showToast("请先生成二维码图片");
            return ;
        }
        //这个一个自定义Dialog弹窗
        ImageOptDialog imageOptDialog=new ImageOptDialog(EightTeenActivity.this);
        imageOptDialog.setCallback(new ImageOptDialog.ImageOptCallback() {
            //识别二维码
            @Override
            public void onIdentifyQrClick() {
                View view = getWindow().getDecorView().getRootView();//找到当前页面的根布局
                view.setDrawingCacheEnabled(true);//禁用绘图缓存
                view.buildDrawingCache();

                Bitmap temBitmap = view.getDrawingCache();
                //String result=BitmapUtil.parseQRcode(temBitmap);
              //  showToast("长按识别二维码结果:"+result);

                //禁用DrawingCahce否则会影响性能 ,而且不禁止会导致每次截图到保存的是缓存的位图
                view.setDrawingCacheEnabled(false);//识别完成之后开启绘图缓存
            }
            //保存图片到本地

            @Override
            public void onSaveImageClick() {
                View view = getWindow().getDecorView().getRootView();//找到当前页面的根布局
                view.setDrawingCacheEnabled(true);//禁用绘图缓存
                view.buildDrawingCache();

                Bitmap temBitmap = view.getDrawingCache();
                ImageUtil.savePicToLocal(temBitmap,EightTeenActivity.this);
                //禁用DrawingCahce否则会影响性能 ,而且不禁止会导致每次截图到保存的是缓存的位图
                view.setDrawingCacheEnabled(false);//识别完成之后开启绘图缓存

                showToast("保存图片到本地成功");
            }
        });
        imageOptDialog.show();

    }

图片保存成功后路径:rocky: filePath:/storage/emulated/0/screen/1627982791618.png

保存图片的方法:

public class ImageUtil {
    public static void savePicToLocal(Bitmap bitmap, Context context) {
        //路径是sdcard 获取sd卡目录即:sdcard/screen/时间.png
        String filePath= Environment.getExternalStorageDirectory().getAbsolutePath()+"/screen"+ File.separator+System.currentTimeMillis()+".png";
        if (bitmap!=null){

            try {
                // 图片文件路径
                Log.i("rocky", "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) {
                e.printStackTrace();
            }

        }
    }
}

存储需要动态添加权限:

//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);
}

四:从相册选择二维码图片进行识别
首先启动系统相册,从相册中选择一张图片。

  //激活系统图库,选择一张图片
                    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();
        }/*else if (requestCode == SCAN_REQUEST_CODE && resultCode == RESULT_OK) {
            String input = intent.getStringExtra(ScanActivity.INTENT_EXTRA_RESULT);
            showToast("扫描结果:"+input);
        }*/
    }

BitmapUtil解析二维码图片

public class BitmapUtil {
    /**
     * 解析二维码图片
     * @param bitmapPath 文件路径
     * @return
     */
    public static String parseQRcode(String bitmapPath){
        Bitmap bitmap = BitmapFactory.decodeFile(bitmapPath, null);
        String result=parseQRcode(bitmap);
        return result;
    }

    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 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图片压缩)
    private static Bitmap comp(Bitmap image) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
        if( baos.toByteArray().length / 1024>1024) {//判断如果图片大于1M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出
            baos.reset();//重置baos即清空baos
            image.compress(Bitmap.CompressFormat.JPEG, 50, baos);//这里压缩50%,把压缩后的数据存放到baos中
        }
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
        BitmapFactory.Options newOpts = new BitmapFactory.Options();
        //开始读入图片,此时把options.inJustDecodeBounds 设回true了
        newOpts.inJustDecodeBounds = true;
        Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);
        newOpts.inJustDecodeBounds = false;
        int w = newOpts.outWidth;
        int h = newOpts.outHeight;
        //现在主流手机比较多是800*480分辨率,所以高和宽我们设置为
        float hh = 400f;//这里设置高度为800f
        float ww = 400f;//这里设置宽度为480f
        //缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
        int be = 1;//be=1表示不缩放
        if (w > h && w > ww) {//如果宽度大的话根据宽度固定大小缩放
            be = (int) (newOpts.outWidth / ww);
        } else if (w < h && h > hh) {//如果高度高的话根据宽度固定大小缩放
            be = (int) (newOpts.outHeight / hh);
        }
        if (be <= 0)
            be = 1;
        newOpts.inSampleSize = be;//设置缩放比例
        //重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
        isBm = new ByteArrayInputStream(baos.toByteArray());
        bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);
        return compressImage(bitmap);//压缩好比例大小后再进行质量压缩
    }

    //质量压缩方法
    private static Bitmap compressImage(Bitmap image) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
        int options = 100;
        while (baos.toByteArray().length/1024>100) { //循环判断如果压缩后图片是否大于100kb,大于继续压缩
            baos.reset();//重置baos即清空baos
            image.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中
            options -= 10;//每次都减少10
        }
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中
        Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream数据生成图片
        return bitmap;
    }
}

五:扫描二维码

Intent intent = new Intent(EightTeenActivity.this,ScanActivity.class);
                startActivityForResult(intent,SCAN_REQUEST_CODE);

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

 if (requestCode == SCAN_REQUEST_CODE && resultCode == RESULT_OK) {
            String input = intent.getStringExtra(ScanActivity.INTENT_EXTRA_RESULT);
            showToast("扫描结果:"+input);
        }

ScanActivity类代码比较多
具体可以参考:https://github.com/ansen666/Z...

END:藏好自己,做好清理

你可能感兴趣的:(androidjava)