您的位置:首页 > 移动开发 > Android开发

Android工具类: 基于Zxing的二维码生成和展示

2017-07-29 18:01 211 查看

前言

Android日常开发中,不可避免遇到二维码开发的需求,本文抛开扫描解析二维码,提供一下生成二维码的思路,和封装好的工具类,很简单实现二维码生成和展示。

注意:本文二维码生成基于著名的二维码库:Zxing v3.2.1

所以你需要先添加Zxing的Jar包 在Gradle中添加对Zxing的依赖

如果你觉得看一篇文章太麻烦,请直接跳到文章末尾,按说明,轻松实现二维码解析&生成

需求基本上有这几种:

生成二维码

生成带图标的二维码

生成二维码放入ImageView中展示(提供给其他设备扫描)

生成二维码保存到本地(以供文件导出,或者保存备用)

接口设计

根据需求设计两种方法:

createQrCode() –> 生成二维码(可以带图标)

createQrCode2ImageView() –> 生成二维码放入ImageView中展示(可以带图标)

简单设计2种接口,大部分都是重载方法:

interface IQrCodeEncoder {
//最基本的方法
Bitmap createQrCode(String content, int widthAndHeight);

void createQrCode2ImageView(String content, ImageView imageView);

//带图标(通过传入R资源/Drawable对象/Bitmap对象的图标)
Bitmap createQrCode(String content, int width, int iconRes);

Bitmap createQrCode(String content, int width, Drawable iconDrawable);

Bitmap createQrCode(String content, int width, Bitmap iconBitmap);

void createQrCode2ImageView(String content, ImageView imageView, int iconRes);

void createQrCode2ImageView(String content, ImageView imageView, Drawable iconDrawable);

void createQrCode2ImageView(String content, ImageView imageView, Bitmap iconBitmap);

//手动设定是否带图标
Bitmap createQrCode(String content, int width, int iconRes, boolean hasIcon);

Bitmap createQrCode(String content, int width, Drawable iconDrawable, boolean hasIcon);

Bitmap createQrCode(String content, int width, Bitmap iconBitmap, boolean hasIcon);

void createQrCode2ImageView(String content, ImageView imageView, int iconRes, boolean hasIcon);

void createQrCode2ImageView(String content, ImageView imageView, Drawable iconDrawable, boolean hasIcon);

void createQrCode2ImageView(String content, ImageView imageView, Bitmap iconBitmap, boolean hasIcon);

//将R资源转为Bitmap对象
Bitmap getBitmapByRes(int resId);
//将Drawable转为Bitmap对象
Bitmap getBitmapByDrawable(Drawable drawable);
//将Icon覆盖到二维码上并返回 带Icon的二维码
Bitmap addIcon2QrCode(Bitmap icon, Bitmap qrCode);
}


实现接口

实现思路很简单,基本上就是:

1.根据提供的内容生成对应的二维码

2.判断是否需要在二维码上添加Icon,若不需要,直接生成Bitmap

2.1 若需要添加Icon,先获取Icon,将不同的资源类型(R资源,Drawable)统统转换为Bitmap,然后通过

addIcon2QrCode(Bitmap icon, Bitmap qrCode)接口获取到带Icon的二维码Bitmap,

2.2 若不需要添加Icon,判断是否需要直接展示在ImageView上,若需要,展示在ImageView上,否则返回Bitmap

3 获得返回的Bitmap后,判断是否需要直接展示在ImageView上,若需要,展示在ImageView上,否则返回Bitmap

看一下代码实现:

public class QRCodeEncoder implements IQrCodeEncoder {

private static final int NO_ICON_RES = 0;

private Activity activity;

private BitmapCompressor bmpConpressor;

public QRCodeEncoder(Activity activity) {
setActivity(activity);
bmpConpressor = new BitmapCompressor();
}

public void setActivity(Activity activity) {
this.activity = activity;
}

@Override
public Bitmap createQrCode(String content, int width) {
return createQrCode(content, width, NO_ICON_RES);
}

@Override
public Bitmap createQrCode(String content, int width, int iconRes) {
return createQrCode(content, width, iconRes, true);
}

@Override
public Bitmap createQrCode(String content, int width, Drawable iconDrawable) {
return createQrCode(content, width, iconDrawable, true);
}

@Override
public Bitmap createQrCode(String content, int width, Bitmap iconBitmap) {
return createQrCode(content, width, iconBitmap, true);
}

@Override
public void createQrCode2ImageView(String content, ImageView imageView) {
this.createQrCode2ImageView(content, imageView, NO_ICON_RES, false);
}

@Override
public void createQrCode2ImageView(String content, ImageView imageView, int iconRes) {
this.createQrCode2ImageView(content, imageView, iconRes, true);
}

@Override
public void createQrCode2ImageView(String content, ImageView imageView, Drawable iconDrawable) {
this.createQrCode2ImageView(content, imageView, iconDrawable, true);
}

@Override
public void createQrCode2ImageView(String content, ImageView imageView, Bitmap iconBitmap) {
this.createQrCode2ImageView(content, imageView, iconBitmap, true);
}

@Override
public void createQrCode2ImageView(String content, ImageView imageView, int iconRes, boolean hasIcon) {
Bitmap iconBitmap = null;
if (iconRes != NO_ICON_RES)
iconBitmap = getBitmapByRes(iconRes);
this.createQrCode2ImageView(content, imageView, iconBitmap, hasIcon);
}

@Override
public void createQrCode2ImageView(String content, ImageView imageView, Drawable iconDrawable, boolean hasIcon) {
Bitmap iconBitmap = null;
if (iconDrawable != null)
iconBitmap = getBitmapByDrawable(iconDrawable);
this.createQrCode2ImageView(content, imageView, iconBitmap, hasIcon);
}

@Override
public void createQrCode2ImageView(String content, ImageView imageView, Bitmap iconBitmap, boolean hasIcon) {
DisplayMetrics dm = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
int mScreenWidth = dm.widthPixels;

Bitmap qrCode = createQrCode(content, mScreenWidth, iconBitmap, hasIcon);
if (qrCode != null) {
imageView.setImageBitmap(qrCode);
}
}

@Override
public Bitmap createQrCode(String content, int width, int iconRes, boolean hasIcon) {
Bitmap iconBitmap = null;
if (iconRes != NO_ICON_RES)
iconBitmap = getBitmapByRes(iconRes);
return createQrCode(content, width, iconBitmap, hasIcon);
}

@Override
public Bitmap createQrCode(String content, int width, Drawable iconDrawable, boolean hasIcon) {
Bitmap iconBitmap = null;
if (iconDrawable != null)
iconBitmap = getBitmapByDrawable(iconDrawable);
return createQrCode(content, width, iconBitmap, hasIcon);
}

/**
* Create QrCode with text content.
* We suggest that developer adjusts the content length, more content text means more difficult for scanning result on the device.
*
* @param content    QrCode content
* @param width      QrCode width&Height
* @param iconBitmap center icon if you want add icon in Bitmap
* @param hasIcon    if false,iconBitmap will not show on QrCode bitmap
* @return QrCode bitmap
*/
@Override
public Bitmap createQrCode(String content, int width, Bitmap iconBitmap, boolean hasIcon) {
try {
//配置参数
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
//容错级别
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
// TODO set blank width, default is 4
// hints.put(EncodeHintType.MARGIN, 2);

BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, width, width, hints);
int[] pixels = new int[width * width];
// 下面这里按照二维码的算法,逐个生成二维码的图片,
// 两个for循环是图片横列扫描的结果
for (int y = 0; y < width; y++) {
for (int x = 0; x < width; x++) {
if (bitMatrix.get(x, y)) {
pixels[y * width + x] = 0xff000000; //black
} else {
pixels[y * width + x] = 0xffffffff; //white
}
}
}

Bitmap bitmap = Bitmap.createBitmap(width, width, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, width);

//Add the Icon to the QrCode bitmap center
if (iconBitmap != null && hasIcon) {
bitmap = addIcon2QrCode(iconBitmap, bitmap);
}

//compress bitmap
return bmpConpressor.compressBitmap(bitmap);
} catch (WriterException e) {
e.printStackTrace();
}
return null;
}

@Override
public Bitmap getBitmapByRes(int resId) {
return BitmapFactory.decodeResource(activity.getResources(), resId);
}

@Override
public Bitmap getBitmapByDrawable(Drawable drawable) {
return Bitmap.createBitmap(
drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight(),
drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
: Bitmap.Config.RGB_565);
}

/**
* Add the Icon to the QrCode bitmap center
*
* @param icon   the icon
* @param qrCode the qrCode
* @return QrCode Bitmap with icon
*/
@Override
public Bitmap addIcon2QrCode(Bitmap icon, Bitmap qrCode) {
if (qrCode == null) {
return null;
}

if (icon == null) {
return qrCode;
}

int srcWidth = qrCode.getWidth();
int srcHeight = qrCode.getHeight();
int iconWidth = icon.getWidth();
int iconHeight = icon.getHeight();

if (srcWidth == 0 || srcHeight == 0) {
return null;
}

if (iconWidth == 0 || iconHeight == 0) {
return qrCode;
}

//Icon size/ QrCode size = 1:5
float scaleFactor = srcWidth * 1.0f / 5 / iconWidth;
Bitmap bitmap = Bitmap.createBitmap(srcWidth, srcHeight, Bitmap.Config.ARGB_8888);
try {
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(qrCode, 0, 0, null);
canvas.scale(scaleFactor, scaleFactor, srcWidth / 2, srcHeight / 2);
canvas.drawBitmap(icon, (srcWidth - iconWidth) / 2, (srcHeight - iconHeight) / 2, null);

canvas.save(Canvas.ALL_SAVE_FLAG);
canvas.restore();
} catch (Exception e) {
bitmap = null;
e.getStackTrace();
}
return bitmap;
}
}


图片压缩

事实上,Bitmap是很吃资源的,所以我们在生成二维码的同时,简单对Bitmap进行压缩再返回:

接口

public interface IBitmapCompressor {

Bitmap compressBitmap(Bitmap bmp);

}


实现

public class BitmapCompressor implements IBitmapCompressor {

private int bmpQuality = 64 * 1024;    //compress bitmap size -> 64k

/**
* set compress bitmap size,with unit KB,default value is 64KB
* @param bmpQuality
*/
public void setBmpQuality(int bmpQuality) {
this.bmpQuality = bmpQuality * 1024;
}

@Override
public Bitmap compressBitmap(Bitmap bmp) {
byte[] bytes = compressBitmap2ByteArray(bmp);
ByteArrayInputStream inputs = new ByteArrayInputStream(bytes);
return BitmapFactory.decodeStream(inputs, null, null);
}

private byte[] compressBitmap2ByteArray(Bitmap bmp) {
ByteArrayOutputStream output = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.JPEG, 100, output);
float zoom = (float) Math.sqrt(32 * 1024 / (float) output.toByteArray().length); //获取缩放比例

// set Rect datas
Matrix matrix = new Matrix();
matrix.setScale(zoom, zoom);

// create new bitmap with Rect datas
Bitmap resultBitmap = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true);

output.reset();

resultBitmap.compress(Bitmap.CompressFormat.JPEG, 100, output);

// if result still > bmpQuality,compress bitmap continue.
while (output.toByteArray().length > bmpQuality) {
matrix.setScale(0.9f, 0.9f);//scale 0.1 every time

resultBitmap = Bitmap.createBitmap(
resultBitmap, 0, 0,
resultBitmap.getWidth(), resultBitmap.getHeight(), matrix, true);

output.reset();
resultBitmap.compress(Bitmap.CompressFormat.JPEG, 100, output);
}
return output.toByteArray();
}
}


这里的bmpQuality 就是要压缩到的大小,一般来说,64K的二维码都足以让绝大多数设备轻松扫描解析了。

当然我也提供了一个接口方便去设置bmpQuality的值:

/**
* set compress bitmap size,with unit KB,default value is 64KB
* @param bmpQuality
*/
public void setBmpQuality(int bmpQuality) {
this.bmpQuality = bmpQuality * 1024;
}


如何使用:

我们只需要在Activity中初始化:

//初始化
qrCodeEncoder = new QRCodeEncoder(this);

//根据内容生成二维码并展示在ImageView上
String textContent = "https://github.com/qingmei2";
qrCodeEncoder.createQrCode2ImageView(textContent, iamgeView);
//或者带Icon的二维码
qrCodeEncoder.createQrCode2ImageView(textContent, iamgeView, R.mipmap.ic_launcher);


结果如下:

内容:”https://github.com/qingmei2

一般二维码:



带Icon的二维码:



更简单的方式

是不是觉得还是有点麻烦,没关系,笔者花了点时间将二维码的生成&解析封装成了library,只需要按照上面的使用文档使用就可以了:

简单易上手的 Android 扫描二维码控件

如果只是需要用到二维码的生成,只需要添加对应的依赖,然后直接按照QrCodeScannerView-Android中文说明文档或者文章上方的「如何使用」方式直接使用即可。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: