Android 二维码生成框架Zxing

一:定义
ZXing是一个开源的,用Java实现的多种格局的1D/2D条码图像处理库,它蕴含了分割到其余语言的端口。zxing能够实现应用手机的内置的摄像头实现条形码的扫描及解码。
Android Studio下增加依赖

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

增加权限
因为扫描二维码须要摄像头权限,把图片保留到本地须要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是为了把应用程序所依赖的软硬件个性告知应用程序之外的对象。 -->    <uses-feature android:name="android.hardware.camera" />    <uses-feature android:name="android.hardware.camera.autofocus" />

这里咱们看到 <uses-feature/>标签

<uses-feature   android:name="string"   android:required=["true" | "false"]   android:glEsVersion="integer" /> 

申明一项应用程序须要用到的软、硬件个性。
申明一项 <uses-feature> 的目标,是为了把应用程序所依赖的软硬件个性告知应用程序之外的对象。 本元素给出了一个 required 属性, 用于指定应用程序是否必须该项个性,也即不申明该项个性的话就无奈失常运行; 或者最好是提供该项个性,但没有的话也能运行。 因为每种 Android 设施提供的个性各不相同, <uses-feature> 元素施展着重要作用, 应用程序能够用它来形容其用到的各种设施个性。
假如应用程序须要应用蓝牙和摄像头设施,则应申明两个元素:

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

<uses-feature android:name="android.hardware.camera" />
通常,应该确保为应用程序须要的所有个性均申明了 <uses-feature> 元素。
<uses-feature> 元素的申明仅仅是告知性质的,这意味着 Android 零碎自身不会在安装程序前查看设施是否反对这些个性。 不过,其余服务(如 Google Play )或者其余应用程序能够查看 <uses-feature> 申明来进行相应解决或与本应用程序进行交互。 因而,对须要用到的所有个性都进行申明(如下表所示)是十分重要的。

有些设施个性可能会存在一些非凡的属性,用于定义该个性的版本,比方 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);

批改一下图片命名能够解决
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<EncodeHintType, Object> qrParam = new Hashtable<EncodeHintType, Object>();        // 设置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<EncodeHintType, Object> 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<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图片压缩)    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:藏好本人,做好清理