现在二维码应用越来越广泛,把一个url转换为二维码图片,让后去扫描,应用太多了。现在 zxing:core 包也用的相当广泛,android studio 使用也很方便,在 gradle 中加入配置, compile 'com.google.zxing:core:3.2.1'
然后调用方法
public static Bitmap createQRImage(String content, int widthPix, int heightPix) {
if (!TextUtils.isEmpty(content)) {
try {
//配置参数
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); //啊啊啊
// 图像数据转换,使用了矩阵转换
BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, widthPix, heightPix, hints);
int[] pixels = new int[widthPix * heightPix];
// 下面这里按照二维码的算法,逐个生成二维码的图片,
// 两个for循环是图片横列扫描的结果
for (int y = 0; y < heightPix; y++) {
for (int x = 0; x < widthPix; x++) {
if (bitMatrix.get(x, y)) {
pixels[y * widthPix + x] = 0xff000000;
} else {
pixels[y * widthPix + x] = 0xffffffff;
}
}
}
// 生成二维码图片的格式,使用ARGB_8888
Bitmap bitmap = Bitmap.createBitmap(widthPix, heightPix, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, widthPix, 0, 0, widthPix, heightPix);
return bitmap;
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
content 是 http的url, widthPix 和 heightPix 是要生成bitmap 的大小。但是记住,二维码是正方形的,也就说,如果宽和高不一致时,会以小的为边长。如果要在二维码中间加入其它图片,则
/**
* 在二维码中间添加Logo图案
*/
public 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;
}
//logo大小为二维码整体大小的1/5
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.ALL_SAVE_FLAG);
canvas.restore();
} catch (Exception e) {
bitmap = null;
e.getStackTrace();
}
return bitmap;
}
把两个图片合成一张图片。
以上代码是去年项目中的代码,用了一年了,结果来了新需求了,就是生成二维码时四周有白色的边框,要把边框给去掉。然后就找方法吧,很快,百度出了答案,注意createQRImage()方法中注释的地方 啊啊啊 的地方,就是在这个地方,设置为0时,就可以了。运行后,然并卵,没鸟用。继续找新的方法,看了BitMatrix类似矩阵的类,发现有几个方法 getTopLeftOnBit() 和 getBottomRightOnBit() , 对外暴露了 生成二维码 的左上角和右下角的方法,既然这样,就有办法了,生成bitmap后根据坐标去截取新的图片。
/**
* 判断是否有白边,如果有,就把二维码给截取出来
*/
private static Bitmap createCenterBitmap(BitMatrix bitMatrix, Bitmap bitmap) {
try {
int[] topLeftOnBit = bitMatrix.getTopLeftOnBit();
int[] bottomRightOnBit = bitMatrix.getBottomRightOnBit();
int left = topLeftOnBit[0];
int top = topLeftOnBit[1];
int right = bottomRightOnBit[0];
int bottom = bottomRightOnBit[1];
if( left > 0 && top > 0 && left < right && top < bottom){
int width = right - left;
int height = bottom - top;
return Bitmap.createBitmap(bitmap, left, top, width, height);
}
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
在createQRImage()方法中最后一步获取到bitmap后,把bitmap和 bitMatrix 传进去,就可以去裁剪bitmap了,把二维码中的黑白点裁剪出来。嗯,可以了,完成新需求;但,很快,发现两个小坑。一个是 getBottomRightOnBit() 这个方法返回右下角的坐标,但坑爹的是,如果右下角恰好是白点而不是黑点的话,右下角坐标就不准了,宽度会变小,会把白色的角那一点给减去,导致拿到的right 值小了,所以截取出来的二维码右边少了一丢丢,然后扫描时怎么也扫描不出来。这个右下角是黑点还是白点是根据url值决定了,不是必现,所以这个坑有掉隐蔽,或者说用错了方法。补救措施有两种,一个是拿到 int width = right - left; int height = bottom - top; 在这里对 width 和 height 做个判断,取大值,这样就行了
/**
* 判断是否有白边,如果有,就把二维码给截取出来
*/
private static Bitmap createCenterBitmap(BitMatrix bitMatrix, Bitmap bitmap) {
try {
int[] topLeftOnBit = bitMatrix.getTopLeftOnBit();
int[] bottomRightOnBit = bitMatrix.getBottomRightOnBit();
int left = topLeftOnBit[0];
int top = topLeftOnBit[1];
int right = bottomRightOnBit[0];
int bottom = bottomRightOnBit[1];
if( left > 0 && top > 0 && left < right && top < bottom){
int width = right - left;
int height = bottom - top;
int wh = Math.max(width, height);
return Bitmap.createBitmap(bitmap, left, top, wh, wh);
}
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
另外一种方法是,BitMatrix类中getEnclosingRectangle()这个方法,能拿到左上角和宽高,既然这样,就不用自己计算了,直接用这个方法。
private static Bitmap createCenterBitmap2(BitMatrix bitMatrix, Bitmap bitmap) {
try {
int[] rec = bitMatrix.getEnclosingRectangle();
int left = rec[0];
int top = rec[1];
int width = rec[2];
int height = rec[3];
return Bitmap.createBitmap(bitmap, left, top, width, height);
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
后来,在网上也找到一个靠谱的方法,通过缩减矩阵的大小
/**
* 删除白边
* */
private static BitMatrix deleteWhite(BitMatrix matrix) {
int[] rec = matrix.getEnclosingRectangle();
int resWidth = rec[2] ;
int resHeight = rec[3] ;
BitMatrix resMatrix = new BitMatrix(resWidth, resHeight);
resMatrix.clear();
for (int i = 0; i < resWidth; i++) {
for (int j = 0; j < resHeight; j++) {
if (matrix.get(i + rec[0], j + rec[1]))
resMatrix.set(i, j);
}
}
return resMatrix;
}
这个方法需要在 createQRImage()方法中,生成 BitMatrix bitMatrix 时调用,
public static Bitmap createQRImage2(String content, int widthPix, int heightPix) {
if (!TextUtils.isEmpty(content)) {
try {
//配置参数
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
//容错级别
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
// 图像数据转换,使用了矩阵转换
BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, widthPix, heightPix, hints);
bitMatrix = deleteWhite(bitMatrix);
widthPix = bitMatrix.getWidth();
heightPix = bitMatrix.getHeight();
int[] pixels = new int[widthPix * heightPix];
// 下面这里按照二维码的算法,逐个生成二维码的图片,
// 两个for循环是图片横列扫描的结果
for (int y = 0; y < heightPix; y++) {
for (int x = 0; x < widthPix; x++) {
if (bitMatrix.get(x, y)) {
pixels[y * widthPix + x] = 0xff000000;
} else {
pixels[y * widthPix + x] = 0xffffffff;
}
}
}
// 生成二维码图片的格式,使用ARGB_8888
Bitmap bitmap = Bitmap.createBitmap(widthPix, heightPix, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, widthPix, 0, 0, widthPix, heightPix);
return bitmap;
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
基本结束,但还有个小问题,就是截取图片或缩减矩阵后,bitmap比着原先传进来的值变小了一部分,这个也好解决,算出新bitmap的图的宽和原图的宽,算个比例,然后扩大,或者截取时就把图给拉伸扩大到原图大小。
private static Bitmap createCenterBitmap2(BitMatrix bitMatrix, Bitmap bitmap) {
try {
int[] rec = bitMatrix.getEnclosingRectangle();
int left = rec[0];
int top = rec[1];
int width = rec[2];
int height = rec[3];
Matrix matrix = new Matrix();
float scaleValue = 1.0f * bitmap.getWidth() / width ;
matrix.postScale(scaleValue, scaleValue);// 使用后乘
return Bitmap.createBitmap(bitmap, left, top, width, height, matrix, false);
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
关键是try catch 里最后四行代码,算出比例,然后拉伸。
bitmap的缩放,在createQRImage2()方法最后调用,用局部变量记录传进来的宽和高,得到二维码bitmap后,调用下面方法,把三个值穿进去即可。
private static Bitmap scaleBitmap(Bitmap origin, int newWidth, int newHeight) {
if (origin == null) {
return null;
}
int height = origin.getHeight();
int width = origin.getWidth();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);// 使用后乘
Bitmap newBM = Bitmap.createBitmap(origin, 0, 0, width, height, matrix, false);
if (!origin.isRecycled()) {
origin.recycle();
}
return newBM;
}
二维码这一块,关键还是多看看,多想想。
然后调用方法
public static Bitmap createQRImage(String content, int widthPix, int heightPix) {
if (!TextUtils.isEmpty(content)) {
try {
//配置参数
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); //啊啊啊
// 图像数据转换,使用了矩阵转换
BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, widthPix, heightPix, hints);
int[] pixels = new int[widthPix * heightPix];
// 下面这里按照二维码的算法,逐个生成二维码的图片,
// 两个for循环是图片横列扫描的结果
for (int y = 0; y < heightPix; y++) {
for (int x = 0; x < widthPix; x++) {
if (bitMatrix.get(x, y)) {
pixels[y * widthPix + x] = 0xff000000;
} else {
pixels[y * widthPix + x] = 0xffffffff;
}
}
}
// 生成二维码图片的格式,使用ARGB_8888
Bitmap bitmap = Bitmap.createBitmap(widthPix, heightPix, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, widthPix, 0, 0, widthPix, heightPix);
return bitmap;
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
content 是 http的url, widthPix 和 heightPix 是要生成bitmap 的大小。但是记住,二维码是正方形的,也就说,如果宽和高不一致时,会以小的为边长。如果要在二维码中间加入其它图片,则
/**
* 在二维码中间添加Logo图案
*/
public 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;
}
//logo大小为二维码整体大小的1/5
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.ALL_SAVE_FLAG);
canvas.restore();
} catch (Exception e) {
bitmap = null;
e.getStackTrace();
}
return bitmap;
}
把两个图片合成一张图片。
以上代码是去年项目中的代码,用了一年了,结果来了新需求了,就是生成二维码时四周有白色的边框,要把边框给去掉。然后就找方法吧,很快,百度出了答案,注意createQRImage()方法中注释的地方 啊啊啊 的地方,就是在这个地方,设置为0时,就可以了。运行后,然并卵,没鸟用。继续找新的方法,看了BitMatrix类似矩阵的类,发现有几个方法 getTopLeftOnBit() 和 getBottomRightOnBit() , 对外暴露了 生成二维码 的左上角和右下角的方法,既然这样,就有办法了,生成bitmap后根据坐标去截取新的图片。
/**
* 判断是否有白边,如果有,就把二维码给截取出来
*/
private static Bitmap createCenterBitmap(BitMatrix bitMatrix, Bitmap bitmap) {
try {
int[] topLeftOnBit = bitMatrix.getTopLeftOnBit();
int[] bottomRightOnBit = bitMatrix.getBottomRightOnBit();
int left = topLeftOnBit[0];
int top = topLeftOnBit[1];
int right = bottomRightOnBit[0];
int bottom = bottomRightOnBit[1];
if( left > 0 && top > 0 && left < right && top < bottom){
int width = right - left;
int height = bottom - top;
return Bitmap.createBitmap(bitmap, left, top, width, height);
}
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
在createQRImage()方法中最后一步获取到bitmap后,把bitmap和 bitMatrix 传进去,就可以去裁剪bitmap了,把二维码中的黑白点裁剪出来。嗯,可以了,完成新需求;但,很快,发现两个小坑。一个是 getBottomRightOnBit() 这个方法返回右下角的坐标,但坑爹的是,如果右下角恰好是白点而不是黑点的话,右下角坐标就不准了,宽度会变小,会把白色的角那一点给减去,导致拿到的right 值小了,所以截取出来的二维码右边少了一丢丢,然后扫描时怎么也扫描不出来。这个右下角是黑点还是白点是根据url值决定了,不是必现,所以这个坑有掉隐蔽,或者说用错了方法。补救措施有两种,一个是拿到 int width = right - left; int height = bottom - top; 在这里对 width 和 height 做个判断,取大值,这样就行了
/**
* 判断是否有白边,如果有,就把二维码给截取出来
*/
private static Bitmap createCenterBitmap(BitMatrix bitMatrix, Bitmap bitmap) {
try {
int[] topLeftOnBit = bitMatrix.getTopLeftOnBit();
int[] bottomRightOnBit = bitMatrix.getBottomRightOnBit();
int left = topLeftOnBit[0];
int top = topLeftOnBit[1];
int right = bottomRightOnBit[0];
int bottom = bottomRightOnBit[1];
if( left > 0 && top > 0 && left < right && top < bottom){
int width = right - left;
int height = bottom - top;
int wh = Math.max(width, height);
return Bitmap.createBitmap(bitmap, left, top, wh, wh);
}
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
另外一种方法是,BitMatrix类中getEnclosingRectangle()这个方法,能拿到左上角和宽高,既然这样,就不用自己计算了,直接用这个方法。
private static Bitmap createCenterBitmap2(BitMatrix bitMatrix, Bitmap bitmap) {
try {
int[] rec = bitMatrix.getEnclosingRectangle();
int left = rec[0];
int top = rec[1];
int width = rec[2];
int height = rec[3];
return Bitmap.createBitmap(bitmap, left, top, width, height);
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
后来,在网上也找到一个靠谱的方法,通过缩减矩阵的大小
/**
* 删除白边
* */
private static BitMatrix deleteWhite(BitMatrix matrix) {
int[] rec = matrix.getEnclosingRectangle();
int resWidth = rec[2] ;
int resHeight = rec[3] ;
BitMatrix resMatrix = new BitMatrix(resWidth, resHeight);
resMatrix.clear();
for (int i = 0; i < resWidth; i++) {
for (int j = 0; j < resHeight; j++) {
if (matrix.get(i + rec[0], j + rec[1]))
resMatrix.set(i, j);
}
}
return resMatrix;
}
这个方法需要在 createQRImage()方法中,生成 BitMatrix bitMatrix 时调用,
public static Bitmap createQRImage2(String content, int widthPix, int heightPix) {
if (!TextUtils.isEmpty(content)) {
try {
//配置参数
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
//容错级别
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
// 图像数据转换,使用了矩阵转换
BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, widthPix, heightPix, hints);
bitMatrix = deleteWhite(bitMatrix);
widthPix = bitMatrix.getWidth();
heightPix = bitMatrix.getHeight();
int[] pixels = new int[widthPix * heightPix];
// 下面这里按照二维码的算法,逐个生成二维码的图片,
// 两个for循环是图片横列扫描的结果
for (int y = 0; y < heightPix; y++) {
for (int x = 0; x < widthPix; x++) {
if (bitMatrix.get(x, y)) {
pixels[y * widthPix + x] = 0xff000000;
} else {
pixels[y * widthPix + x] = 0xffffffff;
}
}
}
// 生成二维码图片的格式,使用ARGB_8888
Bitmap bitmap = Bitmap.createBitmap(widthPix, heightPix, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, widthPix, 0, 0, widthPix, heightPix);
return bitmap;
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
基本结束,但还有个小问题,就是截取图片或缩减矩阵后,bitmap比着原先传进来的值变小了一部分,这个也好解决,算出新bitmap的图的宽和原图的宽,算个比例,然后扩大,或者截取时就把图给拉伸扩大到原图大小。
private static Bitmap createCenterBitmap2(BitMatrix bitMatrix, Bitmap bitmap) {
try {
int[] rec = bitMatrix.getEnclosingRectangle();
int left = rec[0];
int top = rec[1];
int width = rec[2];
int height = rec[3];
Matrix matrix = new Matrix();
float scaleValue = 1.0f * bitmap.getWidth() / width ;
matrix.postScale(scaleValue, scaleValue);// 使用后乘
return Bitmap.createBitmap(bitmap, left, top, width, height, matrix, false);
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
关键是try catch 里最后四行代码,算出比例,然后拉伸。
bitmap的缩放,在createQRImage2()方法最后调用,用局部变量记录传进来的宽和高,得到二维码bitmap后,调用下面方法,把三个值穿进去即可。
private static Bitmap scaleBitmap(Bitmap origin, int newWidth, int newHeight) {
if (origin == null) {
return null;
}
int height = origin.getHeight();
int width = origin.getWidth();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);// 使用后乘
Bitmap newBM = Bitmap.createBitmap(origin, 0, 0, width, height, matrix, false);
if (!origin.isRecycled()) {
origin.recycle();
}
return newBM;
}
二维码这一块,关键还是多看看,多想想。