Android之 Zxing二维码详解

一 简介

1.1 ZXing

目前Android扫描二维码,条形码主要用google官方的工具Zxing,支持扫码,相册解码,生成带logo的二维码等功能 

Zxing github 示例地址:https://github.com/zxing/zxing

1.2 ZBar

由于zxing是基于java编写的,扫码速度和解析上可能没那么快,但大部分场合足够用。也有基于c/c++的库zbar,需要编译通过才能用,下面是官网,有兴趣的可以编译试试:

ZBar官网:http://zbar.sourceforge.net/
ZBar GitHub地址:https://github.com/ZBar/ZBar

1.3  华为ScanKit

目前体验最好的华为统一扫码SDK,基本可以做到秒扫和快速识别,支持多码识别和二维码生成。但该服务必须在华为开发者联盟平台注册应用,配置包名和服务json

https://gitee.com/hms-core/hms-scan-demo华为官方demo示例 gitee地址:https://gitee.com/hms-core/hms-scan-demo

二 Zxing使用

2.1 依赖远程zxing库

dependencies {//zxing的core库implementation "com.google.zxing:core:3.5.1"//zxingimplementation "com.google.zxing:zxing-parent:3.5.1"//zxingimplementation 'com.journeyapps:zxing-android-embedded:4.1.0'}

或直接使用下面库,目前识别比较快的Zxing库

implementation 'com.journeyapps:zxing-android-embedded:4.3.0'

2.2 添加权限

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

2.3 调用SDK的扫码页面并返回结果

/*** 跳转到扫码界面扫码*/
private void goScan(){Intent intent = new Intent(MainActivity.this, CaptureActivity.class);startActivityForResult(intent, REQUEST_CODE_SCAN);
}
@Override
protected 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);}}
}

三 自定义扫码页面

3.1 效果图

3.2 activity_scan.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"><FrameLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/black"><FrameLayoutandroid:id="@+id/v_scan_layout"android:layout_width="match_parent"android:layout_height="match_parent" /><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginLeft="50dp"android:layout_marginRight="50dp"><FrameLayoutandroid:layout_width="0dp"android:layout_height="0dp"android:scaleType="fitXY"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintDimensionRatio="1:1"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent"><Viewandroid:layout_width="15dp"android:layout_height="5dp"android:background="@color/them" /><Viewandroid:layout_width="5dp"android:layout_height="15dp"android:background="@color/them" /><Viewandroid:layout_width="15dp"android:layout_height="5dp"android:layout_gravity="right"android:background="@color/them" /><Viewandroid:layout_width="5dp"android:layout_height="15dp"android:layout_gravity="right"android:background="@color/them" /><Viewandroid:layout_width="15dp"android:layout_height="5dp"android:layout_gravity="right|bottom"android:background="@color/them" /><Viewandroid:layout_width="5dp"android:layout_height="15dp"android:layout_gravity="right|bottom"android:background="@color/them" /><Viewandroid:layout_width="15dp"android:layout_height="5dp"android:layout_gravity="bottom"android:background="@color/them" /><Viewandroid:layout_width="5dp"android:layout_height="15dp"android:layout_gravity="bottom"android:background="@color/them" /><ImageViewandroid:id="@+id/scan_line"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerHorizontal="true"android:background="@mipmap/scan_line" /></FrameLayout></androidx.constraintlayout.widget.ConstraintLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><Viewandroid:id="@+id/view_statues"android:layout_width="match_parent"android:layout_height="0dp" /><RelativeLayoutandroid:id="@+id/v_title_layout"android:layout_width="match_parent"android:layout_height="44dp"><ImageViewandroid:id="@+id/v_back"android:layout_width="wrap_content"android:layout_height="match_parent"android:paddingLeft="15dp"android:paddingRight="15dp"android:src="@mipmap/ic_back_white" /><TextViewandroid:id="@+id/v_head_title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:gravity="center"android:includeFontPadding="false"android:singleLine="true"android:text="掃一掃"android:textColor="@color/white"android:textSize="20sp" /></RelativeLayout></LinearLayout> </FrameLayout>
</layout>

3.3 ScanActivity.java

public class ScanActivity extends BaseActivity<ActivityScanBinding> {private DecoratedBarcodeView decoratedBarcodeView;@Overrideprotected int getLayoutId() {return R.layout.activity_scan;}@Overridepublic 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();}}//权限请求回调@Overridepublic 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<BarcodeFormat> 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() {@Overridepublic void barcodeResult(BarcodeResult result) {String originalValue = result.getText();if (TextUtils.isEmpty(originalValue)) {// Prevent duplicate scansreturn;}toResult(originalValue);}@Overridepublic void possibleResultPoints(List<ResultPoint> resultPoints) {}};private void toResult(String originalValue) {Intent intent=new Intent(mContext,ScanResultActivity.class);intent.putExtra("result",originalValue);startActivity(intent);}@Overrideprotected void onResume() {super.onResume();decoratedBarcodeView.resume();}@Overrideprotected void onPause() {super.onPause();decoratedBarcodeView.pause();}@Overrideprotected 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);//開始动画}
}

四 相册选区图片解析二维码

4.1 选择相册图片

private void setPictureScanOperation() {imgBtn = findViewById(R.id.img_btn);imgBtn.setOnClickListener(new View.OnClickListener() {@Overridepublic 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/*");ZxingActivity.this.startActivityForResult(pickIntent, REQUEST_CODE_PHOTO);}});}

4.2 本地图片文件转换成可解码二维码的 Bitmap

/*** 将本地图片文件转换成可解码二维码的 Bitmap。为了避免图片太大,这里对图片进行了压缩。** @param picturePath 本地图片文件路径*/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;}}

4.3  解析二维码图片

 /*** 同步解析bitmap二维码。该方法是耗时操作,请在子线程中调用。** @param bitmap 要解析的二维码图片* @return 返回二维码图片里的内容 或 null*/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;}}

五 生成二维码

5.1 效果图,支持修改边框颜色大小,二维码颜色大小,背景颜色,logo样式和圆角

5.2 activity_qrcode_style_xml

<FrameLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><ImageViewandroid:id="@+id/v_image"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginLeft="40dp"android:layout_marginRight="40dp" />
</FrameLayout>

 5.3 核心生成二维码源码,QRCodeEncoder.java

public class QRCodeEncoder {public static final Map<EncodeHintType, Object> 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() {}/*** 同步创建黑色前景色、白色背景色的二维码图片。该方法是耗时操作,请在子线程中调用。** @param content 要生成的二维码图片内容* @param size    图片宽高,单位为px*/public static Bitmap syncEncodeQRCode(String content, int size) {return syncEncodeQRCode(content, size, Color.BLACK, Color.WHITE, null);}/*** 同步创建指定前景色、白色背景色的二维码图片。该方法是耗时操作,请在子线程中调用。** @param content         要生成的二维码图片内容* @param size            图片宽高,单位为px* @param foregroundColor 二维码图片的前景色*/public static Bitmap syncEncodeQRCode(String content, int size, int foregroundColor) {return syncEncodeQRCode(content, size, foregroundColor, Color.WHITE, null);}/*** 同步创建指定前景色、白色背景色、带logo的二维码图片。该方法是耗时操作,请在子线程中调用。** @param content         要生成的二维码图片内容* @param size            图片宽高,单位为px* @param foregroundColor 二维码图片的前景色* @param logo            二维码图片的logo*/public static Bitmap syncEncodeQRCode(String content, int size, int foregroundColor, Bitmap logo) {return syncEncodeQRCode(content, size, foregroundColor, Color.WHITE, logo);}/*** 同步创建指定前景色、指定背景色、带logo的二维码图片。该方法是耗时操作,请在子线程中调用。** @param content         要生成的二维码图片内容* @param size            图片宽高,单位为px* @param foregroundColor 二维码图片的前景色* @param backgroundColor 二维码图片的背景色* @param logo            二维码图片的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;}}/*** 同步创建指定前景色、指定背景色、带logo的二维码图片。该方法是耗时操作,请在子线程中调用。** @param content         要生成的二维码图片内容* @param size            图片宽高,单位为px* @param foregroundColor 二维码图片的前景色* @param backgroundColor 二维码图片的背景色* @param logo            二维码图片的logo* @param borderColor     边框颜色*/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;}}/*** 添加logo到二维码图片上*/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;}/*** 添加logo到二维码图片上*/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;}/*** 同步创建条形码图片** @param content  要生成条形码包含的内容* @param width    条形码的宽度,单位px* @param height   条形码的高度,单位px* @param textSize 字体大小,单位px,如果等于0则不在底部绘制文字* @return 返回生成条形的位图*/public static Bitmap syncEncodeBarcode(String content, int width, int height, int textSize) {if (TextUtils.isEmpty(content)) {return null;}Map<EncodeHintType, Object> 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;}/*** 显示条形的内容** @param barcodeBitmap 已生成的条形码的位图* @param content       条形码包含的内容* @param textSize      字体大小,单位px* @return 返回生成的新条形码位图*/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;}
}

5.4 异步调用QRCodeEncoder生成二维码

 private void updateQrcode(){new GenerateQrcodeTask().execute(content);}public class GenerateQrcodeTask extends AsyncTask<String, Void, Bitmap> {@Overrideprotected 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);}@Overrideprotected 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.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<Bitmap, Void, String> {@Overrideprotected String doInBackground(Bitmap... bitmaps) {return FileUtil.saveToImage(bitmaps[0]);}@Overrideprotected 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();}}}

5.6 核心源码解析,我们知道bitmap是一个保存所有像素内容的容器,那二维码原理即是把内容解析到每一个像素里面。如下

  /**** @param content         要生成的二维码图片内容* @param size            图片宽高,单位为px* @param foregroundColor 二维码图片的前景色* @param backgroundColor 二维码图片的背景色* @param logo            二维码图片的logo* @param borderColor     边框颜色*/
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://my.lmcjl.com/post/1957.html

展开阅读全文

4 评论

留下您的评论.