一 简介
1 ZXing
目前Android扫描二维码,条形码主要用google官方的工具Zxing,支持扫码,相册解码,生成带logo的二维码等功能
Zxing github 示例地址:https://github.com/zxing/zxing
2 ZBar
由于zxing是基于java编写的,扫码速度和解析上可能没那么快,但大部分场合足够用。也有基于c/c++的库zbar,需要编译通过才能用,下面是官网,有兴趣的可以编译试试:
ZBar官网:http://zbar.sourceforge.net/
ZBar GitHub地址:https://github.com/ZBar/ZBar
3 华为ScanKit
目前体验最好的华为统一扫码SDK,基本可以做到秒扫和快速识别,支持多码识别和二维码生成。但该服务必须在华为开发者联盟平台注册应用,配置包名和服务json
https://gitee.com/hms-core/hms-scan-demo华为官方demo示例 gitee地址:https://gitee.com/hms-core/hms-scan-demo
二 Zxing使用
1 依赖远程zxing库
dependencies { //zxing的core库 implementation "com.google.zxing:core:3.5.1" //zxing implementation "com.google.zxing:zxing-parent:3.5.1" //zxing implementation 'com.journeyapps:zxing-android-embedded:4.1.0'}
或直接使用下面库,目前识别比较快的Zxing库
implementation 'com.journeyapps:zxing-android-embedded:4.3.0'
2 添加权限
3 调用SDK的扫码页面并返回结果
private void goScan(){ Intent intent = new Intent(MainActivity.this, CaptureActivity.class); startActivityForResult(intent, REQUEST_CODE_SCAN);}
@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); // 扫描二维码/条码回传 if (requestCode == REQUEST_CODE_SCAN && resultCode == RESULT_OK) { if (data != null) { //返回的文本内容 String content = data.getStringExtra(DECODED_CONTENT_KEY); //返回的BitMap图像 Bitmap bitmap = data.getParcelableExtra(DECODED_BITMAP_KEY); } }}
三 自定义扫码页面
1 效果图
2 activity_scan.xml
3 ScanActivity.java
public class ScanActivity extends BaseActivity { private DecoratedBarcodeView decoratedBarcodeView; @Override protected int getLayoutId() { return R.layout.activity_scan; } @Override public void initData() { decoratedBarcodeView = new DecoratedBarcodeView(this); decoratedBarcodeView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); decoratedBarcodeView.setStatusText(""); decoratedBarcodeView.getViewFinder().setVisibility(View.GONE); decoratedBarcodeView.getCameraSettings().setAutoFocusEnabled(true); mDataBinding.vScanLayout.removeAllViews(); mDataBinding.vScanLayout.addView(decoratedBarcodeView); checkCameraPermission(); } public final int REQUEST_CAMERA_PERMISSION = 1; private String cameraPermission = Manifest.permission.CAMERA; private void checkCameraPermission() { //检查是否有相机权限 if (ContextCompat.checkSelfPermission(this, cameraPermission) != PackageManager.PERMISSION_GRANTED) { //没权限,请求权限 ActivityCompat.requestPermissions(this, new String[]{cameraPermission}, REQUEST_CAMERA_PERMISSION); } else { //有权限 scanLaunch(); } } //权限请求回调 @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { case REQUEST_CAMERA_PERMISSION: if (grantResults != null && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { //用户同意权限 scanLaunch(); } else { // 权限被用户拒绝了,可以提示用户,关闭界面等等。 Toast.makeText(this, "拒绝权限,请去设置里面手动开启权限", Toast.LENGTH_SHORT).show(); } break; } } private void scanLaunch() { Collection formats = Arrays.asList(BarcodeFormat.QR_CODE, BarcodeFormat.CODE_39); decoratedBarcodeView.getBarcodeView().setDecoderFactory(new DefaultDecoderFactory(formats)); decoratedBarcodeView.initializeFromIntent(getIntent()); decoratedBarcodeView.decodeContinuous(callback); startScanAnimal(); } private BarcodeCallback callback = new BarcodeCallback() { @Override public void barcodeResult(BarcodeResult result) { String originalValue = result.getText(); if (TextUtils.isEmpty(originalValue)) { // Prevent duplicate scans return; } toResult(originalValue); } @Override public void possibleResultPoints(List resultPoints) { } }; private void toResult(String originalValue) { Intent intent=new Intent(mContext,ScanResultActivity.class); intent.putExtra("result",originalValue); startActivity(intent); } @Override protected void onResume() { super.onResume(); decoratedBarcodeView.resume(); } @Override protected void onPause() { super.onPause(); decoratedBarcodeView.pause(); } @Override protected void onDestroy() { super.onDestroy(); if (animation != null) { animation.cancel(); } } //扫码线动画 private Animation animation; private void startScanAnimal() { animation = new TranslateAnimation(0, 0, 0, StatuesBarUtils.getScreenWidth(this) - StatuesBarUtils.dp2px(this, 100)); animation.setDuration(2000); animation.setRepeatCount(Animation.INFINITE);//动画的反复次数 animation.setFillAfter(true);//设置为true,动画转化结束后被应用 //animation.setRepeatMode(ObjectAnimator.RESTART);//重复模式 animation.setInterpolator(new LinearInterpolator()); mDataBinding.scanLine.startAnimation(animation);//開始动画 }}
四 相册选区图片解析二维码
1 选择相册图片
private void setPictureScanOperation() { imgBtn = findViewById(R.id.img_btn); imgBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent pickIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); pickIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image public static Bitmap getDecodeAbleBitmap(String picturePath) { try { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(picturePath, options); int sampleSize = options.outHeight / 400; if (sampleSize <= 0) { sampleSize = 1; } options.inSampleSize = sampleSize; options.inJustDecodeBounds = false; return BitmapFactory.decodeFile(picturePath, options); } catch (Exception e) { e.printStackTrace(); return null; } }
3 解析二维码图片
public static String syncDecodeQRCode(Bitmap bitmap) { Result result; RGBLuminanceSource source = null; try { int width = bitmap.getWidth(); int height = bitmap.getHeight(); int[] pixels = new int[width * height]; bitmap.getPixels(pixels, 0, width, 0, 0, width, height); source = new RGBLuminanceSource(width, height, pixels); result = new MultiFormatReader().decode(new BinaryBitmap(new HybridBinarizer(source)), new EnumMap<>(DecodeHintType.class)); return result.getText(); } catch (Exception e) { e.printStackTrace(); if (source != null) { try { result = new MultiFormatReader().decode(new BinaryBitmap(new GlobalHistogramBinarizer(source)), new EnumMap<>(DecodeHintType.class)); return result.getText(); } catch (Throwable e2) { e2.printStackTrace(); } } return null; } }
五 生成二维码
1 支持修改边框颜色大小,二维码颜色大小,背景颜色,logo样式和圆角
2 activity_qrcode_style_xml
5.3 核心生成二维码源码,QRCodeEncoder.java
public class QRCodeEncoder { public static final Map HINTS = new EnumMap<>(EncodeHintType.class); static { HINTS.put(EncodeHintType.CHARACTER_SET, "utf-8"); HINTS.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); HINTS.put(EncodeHintType.MARGIN, 0); } private QRCodeEncoder() { } public static Bitmap syncEncodeQRCode(String content, int size) { return syncEncodeQRCode(content, size, Color.BLACK, Color.WHITE, null); } public static Bitmap syncEncodeQRCode(String content, int size, int foregroundColor) { return syncEncodeQRCode(content, size, foregroundColor, Color.WHITE, null); } public static Bitmap syncEncodeQRCode(String content, int size, int foregroundColor, Bitmap logo) { return syncEncodeQRCode(content, size, foregroundColor, Color.WHITE, logo); } public static Bitmap syncEncodeQRCode(String content, int size, int foregroundColor, int backgroundColor, Bitmap logo) { try { BitMatrix matrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, size, size, HINTS); int[] pixels = new int[size * size]; for (int y = 0; y < size; y++) { for (int x = 0; x < size; x++) { if (matrix.get(x, y)) { pixels[y * size + x] = foregroundColor; } else { pixels[y * size + x] = backgroundColor; } } } Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); bitmap.setPixels(pixels, 0, size, 0, 0, size, size); return addLogoToQRCode(bitmap, logo); } catch (Exception e) { e.printStackTrace(); return null; } } public static Bitmap syncEncodeQRCode(String content, int size, int foregroundColor, int backgroundColor, Bitmap logo ,int border , int borderColor) { int borderWidth= (int) dp2px(MyApp.getInstance(),4); size-=borderWidth*2; try { BitMatrix matrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, size, size, HINTS); int[] pixels = new int[size * size]; for (int y = 0; y < size; y++) { for (int x = 0; x < size; x++) { if (matrix.get(x, y)) { pixels[y * size + x] = foregroundColor; } else { pixels[y * size + x] = backgroundColor; } } } Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); bitmap.setPixels(pixels, 0, size, 0, 0, size, size); if(bitmap!=null){ bitmap=addLogoToQRCode(bitmap, logo); } bitmap=addBorderToQRCode(bitmap,backgroundColor ,border,borderColor); return bitmap; } catch (Exception e) { e.printStackTrace(); return null; } } private static Bitmap addBorderToQRCode(Bitmap src, int backgroundColor, int border , int borderColor) { if (src == null) { return src; } if(borderColor==0){ borderColor=Color.parseColor("#63C99B"); } int srcWidth = src.getWidth(); int srcHeight = src.getHeight(); int borderWidth= (int) dp2px(MyApp.getInstance(),4); Bitmap bitmap = Bitmap.createBitmap(srcWidth+borderWidth, srcHeight+borderWidth, Bitmap.Config.ARGB_8888); try { Canvas canvas = new Canvas(bitmap); canvas.drawColor(backgroundColor); if(border!=0){ Paint paintRect = new Paint(); paintRect.setColor(borderColor); paintRect.setStrokeWidth(borderWidth); if(border==1){ paintRect.setPathEffect(new DashPathEffect(new float[]{8, 8}, 0)); // 设置虚线样式 } paintRect.setStyle(Paint.Style.STROKE); canvas.drawRect(0, 0, bitmap.getWidth(), bitmap.getHeight(), paintRect); } canvas.drawBitmap(src, borderWidth/2f, borderWidth/2f, null); canvas.save(); canvas.restore(); } catch (Exception e) { e.printStackTrace(); bitmap = null; } return bitmap; } private static Bitmap addLogoToQRCode(Bitmap src, Bitmap logo) { if (src == null || logo == null) { return src; } int srcWidth = src.getWidth(); int srcHeight = src.getHeight(); int logoWidth = logo.getWidth(); int logoHeight = logo.getHeight(); float scaleFactor = srcWidth * 1.0f / 5 / logoWidth; Bitmap bitmap = Bitmap.createBitmap(srcWidth, srcHeight, Bitmap.Config.ARGB_8888); try { Canvas canvas = new Canvas(bitmap); canvas.drawBitmap(src, 0, 0, null); canvas.scale(scaleFactor, scaleFactor, srcWidth / 2, srcHeight / 2); canvas.drawBitmap(logo, (srcWidth - logoWidth) / 2, (srcHeight - logoHeight) / 2, null); canvas.save(); canvas.restore(); } catch (Exception e) { e.printStackTrace(); bitmap = null; } return bitmap; } public static Bitmap syncEncodeBarcode(String content, int width, int height, int textSize) { if (TextUtils.isEmpty(content)) { return null; } Map hints = new HashMap<>(); hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); hints.put(EncodeHintType.MARGIN, 0); try { BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.CODE_128, width, height, hints); int[] pixels = new int[width * height]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (bitMatrix.get(x, y)) { pixels[y * width + x] = 0xff000000; } else { pixels[y * width + x] = 0xffffffff; } } } Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); bitmap.setPixels(pixels, 0, width, 0, 0, width, height); if (textSize > 0) { bitmap = showContent(bitmap, content, textSize); } return bitmap; } catch (Exception e) { e.printStackTrace(); } return null; } private static Bitmap showContent(Bitmap barcodeBitmap, String content, int textSize) { if (TextUtils.isEmpty(content) || null == barcodeBitmap) { return null; } Paint paint = new Paint(); paint.setColor(Color.BLACK); paint.setAntiAlias(true); paint.setStyle(Paint.Style.FILL); paint.setTextSize(textSize); paint.setTextAlign(Paint.Align.CENTER); int textWidth = (int) paint.measureText(content); Paint.FontMetrics fm = paint.getFontMetrics(); int textHeight = (int) (fm.bottom - fm.top); float scaleRateX = barcodeBitmap.getWidth() * 1.0f / textWidth; if (scaleRateX < 1) { paint.setTextScaleX(scaleRateX); } int baseLine = barcodeBitmap.getHeight() + textHeight; Bitmap bitmap = Bitmap.createBitmap(barcodeBitmap.getWidth(), barcodeBitmap.getHeight() + 2 * textHeight, Bitmap.Config.ARGB_4444); Canvas canvas = new Canvas(); canvas.drawColor(Color.WHITE); canvas.setBitmap(bitmap); canvas.drawBitmap(barcodeBitmap, 0, 0, null); canvas.drawText(content, barcodeBitmap.getWidth() / 2, baseLine, paint); canvas.save(); canvas.restore(); return bitmap; } public static float dp2px(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return dpValue * scale + 0.5f; }}
4 异步调用QRCodeEncoder生成二维码
private void updateQrcode(){ new GenerateQrcodeTask().execute(content); } public class GenerateQrcodeTask extends AsyncTask { @Override protected Bitmap doInBackground(String... strings) { Bitmap logoBitmap=null; if(logo!=0){ logoBitmap = BitmapFactory.decodeResource(getResources(), logo); } int borderColor22=TextUtils.isEmpty(borderColor)?0:Color.parseColor(borderColor); return QRCodeEncoder.syncEncodeQRCode(strings[0], (int) StatuesBarUtils.dp2px(mContext, 300f), Color.parseColor(qianColor),Color.parseColor(beinColor),logoBitmap, border , borderColor22); } @Override protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); qrBitmap=bitmap; if (bitmap != null) { mDataBinding.vImage.setImageBitmap(qrBitmap); } else { Toast.makeText(mContext, "生成維碼失敗", Toast.LENGTH_SHORT).show(); } } }
5 保存二维码,FileUtil.java工具类
public class FileUtil { public static String saveToImage(Bitmap bitmap) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { return MediaStore.Images.Media.insertImage(MyApp.getInstance().getContentResolver(), bitmap, "", ""); } else { try { File dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "qrcode"); if (!dir.exists()) { dir.mkdirs(); } File picFile = new File(dir, System.currentTimeMillis() + ".png"); FileOutputStream fos = new FileOutputStream(picFile); bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos); fos.flush(); fos.close(); return picFile.getAbsolutePath(); } catch (Exception e) { e.printStackTrace(); } } return null; }
异步调用FileUtil,保存二维码
public class SaveBitmapTask extends AsyncTask { @Override protected String doInBackground(Bitmap... bitmaps) { return FileUtil.saveToImage(bitmaps[0]); } @Override protected void onPostExecute(String result) { super.onPostExecute(result); if (result != null) { Intent intent = new Intent(mContext, QrBarcodeEditResultActivity.class); intent.putExtra("filePath", result); startActivity(intent); } else { Toast.makeText(mContext, "保存二維碼失敗", Toast.LENGTH_SHORT).show(); } } }
6 核心源码解析,我们知道bitmap是一个保存所有像素内容的容器,那二维码原理即是把内容解析到每一个像素里面。如下
public static Bitmap syncEncodeQRCode(String content, int size, int foregroundColor, int backgroundColor, Bitmap logo ,int border , int borderColor) {int borderWidth= (int) dp2px(MyApp.getInstance(),4);size-=borderWidth*2;try {//内容解析到字节矩阵BitMatrix matrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, size, size, HINTS);//设置宽高像素数组int[] pixels = new int[size * size];//遍历记录图片每个像素信息for (int y = 0; y < size; y++) {for (int x = 0; x < size; x++) {if (matrix.get(x, y)) {pixels[y * size + x] = foregroundColor;} else {pixels[y * size + x] = backgroundColor;}}}//创建bitmap,设置像素数组Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);bitmap.setPixels(pixels, 0, size, 0, 0, size, size);if(bitmap!=null){bitmap=addLogoToQRCode(bitmap, logo);}//添加logobitmap=addBorderToQRCode(bitmap,backgroundColor ,border,borderColor);return bitmap;} catch (Exception e) {e.printStackTrace();return null;}}
来源地址:https://blog.csdn.net/qq_29848853/article/details/131057600