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:藏好本人,做好清理