一 简介

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"

//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.2 添加权限

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

xmlns:my_view="http://schemas.android.com/apk/res-auto"

android:layout_width="match_parent"

android:layout_height="match_parent">

android:id="@+id/rim"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:background="#C0C0C0">

android:id="@+id/surfaceView"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:layout_gravity="center"/>

android:layout_width="match_parent"

android:layout_height="56dp"

android:gravity="center_vertical">

android:layout_marginStart="10sp"

android:layout_toEndOf="@+id/back_img"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:gravity="center_vertical"

android:text="扫码"

android:textAllCaps="false"

android:textColor="#FFFFFF"

android:textSize="20sp"

android:textStyle="bold" />

android:id="@+id/back_img"

android:layout_width="48dp"

android:layout_height="48dp"

android:padding="12dp"

android:layout_alignParentStart="true"

android:layout_marginStart="12dp"

android:layout_marginTop="4dp"

android:gravity="center"

android:src="@drawable/back" />

android:id="@+id/tv_decode"

android:layout_width="wrap_content"

android:layout_height="match_parent"

android:gravity="center_vertical"

android:text="解码器"

android:textAllCaps="false"

android:textColor="#FFFFFF"

android:textSize="20sp"

android:textStyle="bold"

android:layout_alignParentEnd="true"

android:layout_marginEnd="12dp"

android:layout_marginTop="4dp" />

android:id="@+id/img_btn"

android:layout_width="48dp"

android:layout_height="48dp"

android:padding="12dp"

android:layout_alignParentEnd="true"

android:layout_marginEnd="12dp"

android:layout_marginTop="4dp"

android:gravity="center"

android:visibility="gone"

android:src="@drawable/photo" />

3.3 ScanActivity.java

public class ScanActivity extends Activity {

public static final int REQUEST_CODE_PHOTO = 0X1113;

private static final String TAG = "ScanActivity";

private Camera camera = null;

private Camera.Parameters parameters = null;

private boolean isPreview = false;

private FrameCallback frameCallback = new FrameCallback();

private int width = 1920;

private int height = 1080;

private double defaultZoom = 1.0;

private SurfaceHolder surfaceHolder;

private SurfaceCallBack surfaceCallBack;

private CommonHandler handler;

private boolean isShow;

private FrameLayout rim;

private SurfaceView surfaceView;

private ImageView backImg;

private TextView tvDecode;

private ImageView imgBtn;

private int shortSize;

private int longSize;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

Window window = getWindow();

window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

requestWindowFeature(Window.FEATURE_NO_TITLE);

setContentView(R.layout.activity_common);

rim = (FrameLayout) findViewById(R.id.rim);

surfaceView = (SurfaceView) findViewById(R.id.surfaceView);

backImg = (ImageView) findViewById(R.id.back_img);

tvDecode = (TextView) findViewById(R.id.tv_decode);

imgBtn = (ImageView) findViewById(R.id.img_btn);

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[]{Manifest.permission.CAMERA},

REQUEST_CAMERA_PERMISSION);

} else {

//有权限

createSurfaceView();

}

}

//权限请求回调

@Override

public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {

switch (requestCode) {

case REQUEST_CAMERA_PERMISSION:

if (grantResults != null && grantResults.length > 0

&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {

//用户同意权限

createSurfaceView();

} else {

// 权限被用户拒绝了,可以提示用户,关闭界面等等。

Toast.makeText(this, "拒绝权限,请去设置里面手动开启权限", Toast.LENGTH_SHORT).show();

}

break;

}

}

/**

* 创建预览

*/

private void createSurfaceView() {

surfaceCallBack = new SurfaceCallBack();

adjustSurface(surfaceView);

surfaceHolder = surfaceView.getHolder();

isShow = false;

setBackOperation();

setPictureScanOperation();

setDecodeSelectOperation();

}

private void adjustSurface(SurfaceView cameraPreview) {

FrameLayout.LayoutParams paramSurface = (FrameLayout.LayoutParams) cameraPreview.getLayoutParams();

if (getSystemService(Context.WINDOW_SERVICE) != null) {

WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);

Display defaultDisplay = windowManager.getDefaultDisplay();

Point outPoint = new Point();

defaultDisplay.getRealSize(outPoint);

int sceenWidth = outPoint.x;

int sceenHeight = outPoint.y;

shortSize = Math.min(sceenWidth, sceenHeight);

longSize = shortSize;

//横屏

paramSurface.width = shortSize;

paramSurface.height = shortSize;

// float rate;

// if (sceenWidth / (float) 1080 > sceenHeight / (float) 1920) {

// rate = sceenWidth / (float) 1080;

// int targetHeight = (int) (1920 * rate);

// paramSurface.width = FrameLayout.LayoutParams.MATCH_PARENT;

// paramSurface.height = targetHeight;

// int topMargin = (int) (-(targetHeight - sceenHeight) / 2);

// if (topMargin < 0) {

// paramSurface.topMargin = topMargin;

// }

// } else {

// rate = sceenHeight / (float) 1920;

// int targetWidth = (int) (1080 * rate);

// paramSurface.width = targetWidth;

// paramSurface.height = FrameLayout.LayoutParams.MATCH_PARENT;

// int leftMargin = (int) (-(targetWidth - sceenWidth) / 2);

// if (leftMargin < 0) {

// paramSurface.leftMargin = leftMargin;

// }

// }

}

}

private void setBackOperation() {

backImg.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

finish();

}

});

}

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/*");

ScanActivity.this.startActivityForResult(pickIntent, REQUEST_CODE_PHOTO);

}

});

}

private int decodeType = 1;

private void setDecodeSelectOperation() {

tvDecode.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

if (decodeType == 1) {

decodeType = 2;

tvDecode.setText("Sunmin");

} else if (decodeType == 2) {

decodeType = 3;

tvDecode.setText("HuaweiHms");

} else if (decodeType == 3) {

decodeType = 1;

tvDecode.setText("Zxing");

}

}

});

tvDecode.setText("Zxing");

}

@Override

protected void onResume() {

super.onResume();

if (isShow) {

initCamera();

} else {

surfaceHolder.addCallback(surfaceCallBack);

}

}

@Override

protected void onPause() {

if (handler != null) {

handler.quit();

handler = null;

}

close();

if (!isShow) {

surfaceHolder.removeCallback(surfaceCallBack);

}

super.onPause();

}

@Override

protected void onDestroy() {

super.onDestroy();

}

private void initCamera() {

open(surfaceHolder);

if (handler == null) {

handler = new CommonHandler();

} else {

startPreview();

}

}

@Override

protected void onActivityResult(int requestCode, int resultCode, Intent data) {

super.onActivityResult(requestCode, resultCode, data);

// if (resultCode != RESULT_OK || data == null || requestCode != REQUEST_CODE_PHOTO) {

// return;

// }

// try {

// decodeMultiSyn(MediaStore.Images.Media.getBitmap(this.getContentResolver(), data.getData()));

// } catch (Exception e) {

// Log.e(TAG, Objects.requireNonNull(e.getMessage()));

// }

}

class SurfaceCallBack implements SurfaceHolder.Callback {

@Override

public void surfaceCreated(SurfaceHolder holder) {

if (!isShow) {

isShow = true;

initCamera();

}

}

@Override

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

}

@Override

public void surfaceDestroyed(SurfaceHolder holder) {

isShow = false;

}

}

///

/**

* Open up the camera.

*/

public synchronized void open(SurfaceHolder holder) {

try {

camera = Camera.open(0);

parameters = camera.getParameters();

//获取合适的预览尺寸,保证不变形

Camera.Size bestSize = getBestSize(parameters);

//设置预览大小

parameters.setPreviewSize(bestSize.width, bestSize.height);

parameters.setPictureSize(width, height);

parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);

parameters.setPictureFormat(ImageFormat.NV21);

camera.setPreviewDisplay(holder);

camera.setDisplayOrientation(90);

//camera.setDisplayOrientation(270);

camera.setParameters(parameters);

} catch (Exception e) {

e.printStackTrace();

}

}

/**

* 获取预览最佳尺寸

*/

private Camera.Size getBestSize(Camera.Parameters parameters) {

List sizes = parameters.getSupportedPreviewSizes();

Camera.Size bestSize = null;

float uiRatio = (float) longSize / shortSize;

float minRatio = uiRatio;

for (Camera.Size previewSize : sizes) {

float cameraRatio = (float) previewSize.width / previewSize.height;

//如果找不到比例相同的,找一个最近的,防止预览变形

float offset = Math.abs(cameraRatio - minRatio);

if (offset < minRatio) {

minRatio = offset;

bestSize = previewSize;

}

//比例相同

if (uiRatio == cameraRatio) {

bestSize = previewSize;

break;

}

}

return bestSize;

}

public synchronized void close() {

if (camera != null) {

camera.stopPreview();

camera.release();

camera = null;

}

}

public synchronized void startPreview() {

if (camera != null && !isPreview) {

camera.startPreview();

isPreview = true;

}

}

public synchronized void stopPreview() {

if (camera != null && isPreview) {

camera.stopPreview();

frameCallback.setProperties(null);

isPreview = false;

}

}

public synchronized void callbackFrame(Handler handler, double zoomValue) {

if (camera != null && isPreview) {

frameCallback.setProperties(handler);

if (camera.getParameters().isZoomSupported() && zoomValue != defaultZoom) {

//Auto zoom.

//parameters.setZoom(convertZoomInt(zoomValue));

camera.setParameters(parameters);

}

camera.setOneShotPreviewCallback(frameCallback);

}

}

public int convertZoomInt(double zoomValue) {

List allZoomRatios = parameters.getZoomRatios();

float maxZoom = Math.round(allZoomRatios.get(allZoomRatios.size() - 1) / 100f);

if (zoomValue >= maxZoom) {

return allZoomRatios.size() - 1;

}

for (int i = 1; i < allZoomRatios.size(); i++) {

if (allZoomRatios.get(i) >= (zoomValue * 100) && allZoomRatios.get(i - 1) <= (zoomValue * 100)) {

return i;

}

}

return -1;

}

class FrameCallback implements Camera.PreviewCallback {

private Handler handler;

public void setProperties(Handler handler) {

this.handler = handler;

}

@Override

public void onPreviewFrame(byte[] data, Camera camera) {

if (handler != null) {

Message message = handler.obtainMessage(0, camera.getParameters().getPreviewSize().width,

camera.getParameters().getPreviewSize().height, data);

message.sendToTarget();

handler = null;

}

}

}

//

class CommonHandler extends Handler {

private static final String TAG = "MainHandler";

private static final double DEFAULT_ZOOM = 1.0;

private HandlerThread decodeThread;

private Handler decodeHandle;

private BeepManager beepManager;

private long preTime;

public CommonHandler() {

beepManager = new BeepManager(ScanActivity.this);

decodeThread = new HandlerThread("DecodeThread");

decodeThread.start();

decodeHandle = new Handler(decodeThread.getLooper()) {

@Override

public void handleMessage(Message msg) {

if (msg == null) {

return;

}

long startTime=System.currentTimeMillis();

String result = decodeSyn(msg.arg1, msg.arg2, (byte[]) msg.obj);

long endTime=System.currentTimeMillis();

if (result == null) {

restart(DEFAULT_ZOOM);

} else {

Message message = new Message();

message.what = msg.what;

message.obj = result;

message.arg1= (int) (endTime-startTime);

CommonHandler.this.sendMessage(message);

}

}

};

startPreview();

restart(DEFAULT_ZOOM);

}

@Override

public void handleMessage(Message message) {

if (message.what == 0) {

String result = (String) message.obj;

Log.e(TAG, result);

long currentTime = System.currentTimeMillis();

if (currentTime - preTime > 200) {

preTime = currentTime;

beepManager.playBeepSoundAndVibrate();

restart(DEFAULT_ZOOM);

stopPreview();

Intent intent = new Intent(ScanActivity.this, ResultActivity.class);

intent.putExtra("result", result);

intent.putExtra("time", message.arg1);

startActivity(intent);

}

}

}

public void quit() {

try {

stopPreview();

decodeHandle.getLooper().quit();

decodeThread.join(500);

} catch (InterruptedException e) {

Log.w(TAG, e);

}

}

public void restart(double zoomValue) {

callbackFrame(decodeHandle, zoomValue);

}

/**

* Call the MultiProcessor API in synchronous mode.

*/

private String decodeSyn(int width, int height, byte[] data) {

String result="";

switch (decodeType){

case 1://Zxing

result = ZxingUtils.decode(width, height, data);

break;

case 2://商米

result = SunmiUtils.decode(width, height, data);

break;

case 3://华为

result = HuaweiHmsUtils.decode(width, height, data, ScanActivity.this);

break;

}

return result;

}

}

}

3.4  解析工具类

public class ZxingUtils {

public static String decode(int width, int height, byte[] data) {

Map hints = new EnumMap<>(DecodeHintType.class);

hints.put(DecodeHintType.CHARACTER_SET, "utf-8");

Result rawResult = null;

PlanarYUVLuminanceSource source = buildLuminanceSource(data, width, height);

MultiFormatReader multiFormatReader = null;

if (source != null) {

BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));

try {

multiFormatReader = new MultiFormatReader();

multiFormatReader.setHints(hints);

rawResult = multiFormatReader.decodeWithState(bitmap);

} catch (ReaderException re) {

// continue

} finally {

multiFormatReader.reset();

}

}

if(rawResult==null){

return null;

}else {

return rawResult.getText();

}

}

/**

* A factory method to build the appropriate LuminanceSource object based on the format

* of the preview buffers, as described by Camera.Parameters.

*

* @param data A preview frame.

* @param width The width of the image.

* @param height The height of the image.

* @return A PlanarYUVLuminanceSource instance.

*/

public static PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {

Rect rect = new Rect(0,0,width,height);

if (rect == null) {

return null;

}

// Go ahead and assume it's YUV rather than die.

return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,

rect.width(), rect.height(), false);

}

}

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

4.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/*");

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

android:layout_width="match_parent"

android:layout_height="match_parent">

android: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" />

 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() {

}

/**

* 同步创建黑色前景色、白色背景色的二维码图片。该方法是耗时操作,请在子线程中调用。

*

* @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 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 {

@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.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();

}

}

}

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

}

//添加logo

bitmap=addBorderToQRCode(bitmap,backgroundColor ,border,borderColor);

return bitmap;

} catch (Exception e) {

e.printStackTrace();

return null;

}

}

推荐阅读

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。