Android生成二维码使用的是zxing。
1.加入依赖,或者自己选择zxing版本:Releases · zxing/zxing · GitHub
dependencies {
...
implementation 'com.google.zxing:core:3.5.0'
}
2.生成普通二维码:
- 生成二维码
/**
* 创建二维码位图 (支持自定义配置和自定义样式)
*
* @param content 字符串内容
* @param width 位图宽度,要求>=0(单位:px)
* @param height 位图高度,要求>=0(单位:px)
* @param character_set 字符集/字符转码格式 (支持格式:{@link CharacterSetECI })。传null时,zxing源码默认使用 "ISO-8859-1"
* @param error_correction 容错级别 (支持级别:{@link ErrorCorrectionLevel })。传null时,zxing源码默认使用 "L"
* @param margin 空白边距 (可修改,要求:整型且>=0), 传null时,zxing源码默认使用"4"。
* @param color_black 黑色色块的自定义颜色值
* @param color_white 白色色块的自定义颜色值
* @return
*/
@Nullable
public static Bitmap createQRCodeBitmap(String content, int width, int height,
@Nullable String character_set, @Nullable String error_correction, @Nullable String margin,
@ColorInt int color_black, @ColorInt int color_white) {
/** 1.参数合法性判断 */
if (TextUtils.isEmpty(content)) { // 字符串内容判空
return null;
}
if (width < 0 || height < 0) { // 宽和高都需要>=0
return null;
}
try {
/** 2.设置二维码相关配置,生成BitMatrix(位矩阵)对象 */
Hashtable<EncodeHintType, String> hints = new Hashtable<>();
if (!TextUtils.isEmpty(character_set)) {
hints.put(EncodeHintType.CHARACTER_SET, character_set); // 字符转码格式设置
}
if (!TextUtils.isEmpty(error_correction)) {
hints.put(EncodeHintType.ERROR_CORRECTION, error_correction); // 容错级别设置
}
if (!TextUtils.isEmpty(margin)) {
hints.put(EncodeHintType.MARGIN, margin); // 空白边距设置
}
// hints.put(EncodeHintType.QR_VERSION,"4");
BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
/** 3.创建像素数组,并根据BitMatrix(位矩阵)对象为数组元素赋颜色值 */
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] = color_black; // 黑色色块像素设置
} else {
pixels[y * width + x] = color_white; // 白色色块像素设置
}
}
}
/** 4.创建Bitmap对象,根据像素数组设置Bitmap每个像素点的颜色值,之后返回Bitmap对象 */
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
return bitmap;
} catch (WriterException e) {
e.printStackTrace();
}
return null;
}
- 添加二维码logo
/**
* 在二维码中间添加Logo图案
*/
private static Bitmap addLogo(Bitmap src, Bitmap logo) {
if (src == null) {
return null;
}
if (logo == null) {
return src;
}
//获取图片的宽高
int srcWidth = src.getWidth();
int srcHeight = src.getHeight();
int logoWidth = logo.getWidth();
int logoHeight = logo.getHeight();
if (srcWidth == 0 || srcHeight == 0) {
return null;
}
if (logoWidth == 0 || logoHeight == 0) {
return src;
}
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) {
bitmap = null;
e.getStackTrace();
}
return bitmap;
}
但是在使用过程中,我们有时候会需要用到圆形二维码或者自定义码点大小的情况。
3.生成自定义二维码
二维码主要分为两个部分:定位点和内容区域;二维码的复杂度和二维码版本有关,在生成二维码的时候,如果不设置参数EncodeHintType.QR_VERSION,zxing是根据数据大小自动选择版本,数据越大,版本越高,像素点越多;定位点是一个7x7的方形;
这里生成自定义形状的二维码依旧使用的zxing的库获取点位数据
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
hints.put(EncodeHintType.MARGIN, 0);
QRCode qrCode = Encoder.encode(content, ErrorCorrectionLevel.H, hints);
ByteMatrix matrix = qrCode.getMatrix();//获取点位数据
int originalWidth = matrix.getWidth();//获取宽度
int originalHeight = matrix.getHeight();//获取高度
得到matrix对象之后,根据需要的图片大小和边距获取每个点的大小,再通过matrix对象为数组元素赋值和生成对应想要的图案形状,并绘制出来。
完整代码如下:
- 生成自定义二维码
/**
* 自定义二维码
*
* @param content:二维码内容
* @param psRandom:生成圆形码点时参与随机半径大小生成
* @param sizes:二维码尺寸大小
* @param colors:二维码颜色,第一位为点位颜色,第二位为背景颜色
* @param padding:边距
* @param anchortype:定位点类型:0:方形矩形;1:圆点矩形;2:圆形;3.4.5.6:多边形矩形
* @param contenttype:码点类型:0:方形码点;1:圆形码点;其他为多边形码点
* @param ratio:定位点的大小比例
* @param contentratiod:码点的缩放比例
* @return
*/
public static Bitmap CreateQRCode(String content, float psRandom, int[] sizes, int[] colors, int padding, int anchortype, int contenttype, float ratio, float contentratiod) throws Exception {
int outputWidth = sizes[0], outputHeight = sizes[1];
int qrColor = colors[0], bgColor = colors[1];
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
hints.put(EncodeHintType.MARGIN, 0);
QRCode qrCode = Encoder.encode(content, ErrorCorrectionLevel.H, hints);
ByteMatrix matrix = qrCode.getMatrix();
int originalWidth = matrix.getWidth();//矩阵的列数
int originalHeight = matrix.getHeight();//矩阵的行数
outputWidth = Math.max(originalWidth, outputWidth);
outputHeight = Math.max(originalHeight, outputHeight);
int originalPadding = Math.min(outputWidth / (originalWidth + 2), outputHeight / (originalHeight + 2));
padding = Math.max(padding, originalPadding);
int cellWidth = Math.min((outputWidth - padding * 2) / originalWidth, (outputHeight - padding * 2) / originalHeight);//每一个点位的宽高
int outputLeft = (outputWidth - cellWidth * originalWidth) / 2;
int outputTop = (outputHeight - cellWidth * originalHeight) / 2;
float cellMid = (float) (cellWidth * 1.0 / 2);
double randomRange = 0.25 * psRandom;
Paint paint = new Paint();
paint.setColor(qrColor);
paint.setStyle(Paint.Style.FILL);
paint.setAntiAlias(true);
Bitmap bitmap = Bitmap.createBitmap(outputWidth, outputHeight, Bitmap.Config.ARGB_8888);
bitmap.eraseColor(bgColor);
Canvas canvas = new Canvas(bitmap);
BitMatrix ignoreMatrix = new BitMatrix(originalWidth, originalHeight);
if (anchortype == 2) {
drawCircle2( outputWidth, outputHeight, originalWidth, originalHeight, (float) (cellWidth * 1.0 / 2), paint, canvas, qrColor, ignoreMatrix, ratio);
}
int cellFour = cellWidth / 4;
//圆点可调整
for (int y = 0; y < originalHeight; y++) {
for (int x = 0; x < originalWidth; x++) {
float outputY;
float outputX;
if (matrix.get(x, y) == 1) {
float r;
if ((x < 7 && y < 7) || (x >= originalWidth - 7 && y < 7) || (x < 7 && y >= originalHeight - 7)) {//定位点数据
drawAnchor2(anchortype, outputTop, outputLeft, cellWidth, paint, canvas, x, y, contentratiod);
} else {
if (contenttype == 1) {//圆形点位
outputY = outputTop + y * cellWidth + cellMid;//画圆点因为是要确定圆心的位置,而不是圆的左边界,所以要多加半径cellMid,来确认圆心
outputX = outputLeft + x * cellWidth + cellMid;
if (randomRange == 0) {
r = cellMid * contentratiod;
} else {
r = (int) ((1 + (Math.random() - 0.5) * randomRange * 2) * cellMid) * contentratiod;//生成的圆点会有随机的大小之分
}
canvas.drawCircle(outputX, outputY, r, paint);
} else if (contenttype == 0) {//方形点位
outputY = outputTop + y * cellWidth + (1.0f - contentratiod) * cellWidth / 2;
outputX = outputLeft + x * cellWidth + (1.0f - contentratiod) * cellWidth / 2;
canvas.drawRect(outputX, outputY, outputX + cellWidth * contentratiod, outputY + cellWidth * contentratiod, paint);
} else {//多边形
outputY = outputTop + y * cellWidth + (1.0f - contentratiod) * cellWidth / 2;
outputX = outputLeft + x * cellWidth + (1.0f - contentratiod) * cellWidth / 2;
double pr = 2 * Math.PI / contenttype;
double pr90 = Math.PI / 2;
Path path = new Path();
double r1;
double r2 = pr / 2 - pr90;
path.moveTo(outputX + cellMid, outputY);
path.lineTo((int) (outputX + cellMid + Math.cos(r2) * cellFour), (int) (outputY + cellMid + Math.sin(r2) * cellFour));
for (int indexL = 1; indexL < contenttype; indexL++) {
r1 = indexL * pr - pr90;
path.lineTo((int) (outputX + cellMid + Math.cos(r1) * cellMid* contentratiod), (int) (outputY + cellMid + Math.sin(r1) * cellMid* contentratiod));
r2 = indexL * pr + pr / 2 - pr90;
path.lineTo((int) (outputX + cellMid + Math.cos(r2) * cellFour), (int) (outputY + cellMid + Math.sin(r2) * cellFour));
}
path.close();
canvas.drawPath(path, paint);
}
}
}
}
}
return bitmap;
}
- 二维码的圆形定位点绘制
/*绘制圆形定位点
* cellMid:一个格子的一半大小
* */
private static void drawCircle2( int outputWidth, int outputHeight, int originalWidth, int originalHeight, float cellMid, Paint paint, Canvas canvas, int qrColor, BitMatrix ignoreMatrix, float ratio) {
float rightWidth = cellMid * 2 * originalWidth;
float rightHeight = cellMid * 2 * originalHeight;
float outputLeft = (outputWidth - rightWidth) / 2;
float outputTop = (outputHeight - rightHeight) / 2;
float outputRight = outputLeft + rightWidth;
float outputBottom = outputTop + rightHeight;
float circleP = 7 * cellMid;//半径,cellMid用的是一个点位的一半大小,定位点占据了7x7的点位,
float circleSmR = (float) (3 * cellMid * ratio);
Paint bigCirclePaint = new Paint();
bigCirclePaint.setColor(qrColor);
bigCirclePaint.setStyle(Paint.Style.STROKE);
bigCirclePaint.setAntiAlias(true);
bigCirclePaint.setStrokeWidth((float) (cellMid * 2 * ratio));
canvas.drawCircle(outputLeft + circleP, outputTop + circleP, circleSmR, paint);
canvas.drawCircle(outputRight - circleP, outputTop + circleP, circleSmR, paint);
canvas.drawCircle(outputLeft + circleP, outputBottom - circleP, circleSmR, paint);
float circleR = 6 * cellMid;
canvas.drawCircle(outputLeft + circleP, outputTop + circleP, circleR, bigCirclePaint);
canvas.drawCircle(outputRight - circleP, outputTop + circleP, circleR, bigCirclePaint);
canvas.drawCircle(outputLeft + circleP, outputBottom - circleP, circleR, bigCirclePaint);
ignoreMatrix.setRegion(0, 0, 7, 7);
ignoreMatrix.setRegion(originalWidth - 7, 0, 7, 7);
ignoreMatrix.setRegion(0, originalHeight - 7, 7, 7);
}
- 二维码的方形定位点绘制
/*绘制非圆形定位点*/
private static void drawAnchor2(int anchortype, float outputTop, float outputLeft, int cellWidth, Paint paint, Canvas canvas, int x, int y, float remark3) {
float outputY = 0;
float outputX = 0;
switch (anchortype) {
case 0://普通矩形
outputY = outputTop + y * cellWidth + (1.0f - remark3) * cellWidth / 2;
outputX = outputLeft + x * cellWidth + (1.0f - remark3) * cellWidth / 2;
canvas.drawRect(outputX, outputY, outputX + cellWidth * remark3, outputY + cellWidth * remark3, paint);
break;
case 1://圆形矩形
outputY = outputTop + y * cellWidth + (float) (cellWidth * 1.0 / 2);
outputX = outputLeft + x * cellWidth + (float) (cellWidth * 1.0 / 2);
canvas.drawCircle(outputX, outputY, (float) (cellWidth * remark3 * 1.0 / 2), paint);
break;
case 3:
case 4:
case 5:
case 6:
outputY = outputTop + y * cellWidth;
outputX = outputLeft + x * cellWidth;
double pr = 2 * Math.PI / anchortype;
double pr90 = Math.PI / 2;
Path path = new Path();
double r1;
double r2 = pr / 2 - pr90;
int cellMid = cellWidth / 2;
int cellFour = cellWidth / 4;
path.moveTo(outputX + cellMid, outputY);
path.lineTo((int) (outputX + cellMid + Math.cos(r2) * cellFour), (int) (outputY + cellMid + Math.sin(r2) * cellFour));
for (int indexL = 1; indexL < anchortype; indexL++) {
r1 = indexL * pr - pr90;
path.lineTo((int) (outputX + cellMid + Math.cos(r1) * cellMid), (int) (outputY + cellMid + Math.sin(r1) * cellMid));
r2 = indexL * pr + pr / 2 - pr90;
path.lineTo((int) (outputX + cellMid + Math.cos(r2) * cellFour), (int) (outputY + cellMid + Math.sin(r2) * cellFour));
}
path.close();
canvas.drawPath(path, paint);
break;
}
}
- 方法调用
int[] sizes = new int[]{600, 600};
int[] colors = {Color.BLACK, Color.WHITE};
Bitmap bitmap1 = CreateQRCode("你好123456", 1.0f, sizes, colors,
0, 0, 0, 1.0f, 1.0f);
- 正常尺寸图形
- 定位点和码点缩小之后
注:这里主要使用了方形,圆点和多边形,还可以替换成表情等码点,这里点的大小变化都是基于了中心点不变,如果需要将码点显示为正常大小的一半,比如只显示左侧或者上半部分,同样可以在数组的循环方法中绘制想要的样式,不过这种时候需要麻烦公司的大佬重编解析库。