ZXing之生成条形码

来源:互联网 发布:js 排序算法 编辑:程序博客网 时间:2024/06/03 00:45

一 前言

在前面学习了二维码的解码及生产,两篇文章总结如下:
  • ZXing之二维码解析图片资源
  • ZXing之生成二维码
接下来就让我们来看看条形码的生成方式。在正式开始之前先来了来了解一个东西,因为在绘制条形码的内容的时候会用到,那就是drawText绘制,小的时候我们都适用过四线格写过英语单词,其实android 系统在进行文本绘制的时候就是这种格式,其格式如下:

Android 系统在绘制文本的时候需要找到一个轴,这个轴就是上面红色的线(基线),也就是四线格的第三条线,在它上下还有四条线分别是top、ascent、decent、bottom ,如下(图片源自于网络) :

它们的意义分别是:
  • ascent: 系统建议的,绘制单个字符时,字符应当的最高高度所在线
  • descent:系统建议的,绘制单个字符时,字符应当的最低高度所在线
  • top: 可绘制的最高高度所在线
  • bottom: 可绘制的最低高度所在线
这个几个参数在Paint内部类FontMetrics中,好接下来进入主题。

二 条形码生成实现

条形码的生成步骤和二维码基本是相同的:
1 设置参数
//配置参数
Map<EncodeHintType,Object> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
// 容错级别 这里选择最高H级别
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
MultiFormatWriter writer = new MultiFormatWriter();
说明,在设置容错级别时最好设置为最好级别,编码格式设置为国际通用的"utf-8"。
2 把数据转换为Matrix
// 图像数据转换,使用了矩阵转换 参数顺序分别为:编码内容,编码类型,生成图片宽度,生成图片高度,设置参数
BitMatrix bitMatrix = writer.encode(content, BarcodeFormat.CODE_128, widthPix, heightPix, hints);
3 根据上面生成的BitMatrix把像素点都为黑白色
            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;// 白色
}
}
}
4 生成位图Bitmap(图片)
Bitmap bitmap = Bitmap.createBitmap(widthPix, heightPix, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, widthPix, 0, 0, widthPix, heightPix);
5 如果允许接下来就是绘制文本
Paint paint = new Paint();
paint.setColor(Color.BLACK);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);//设置填充样式
paint.setTextSize(20);
// paint.setTextAlign(Paint.Align.CENTER);
Paint.FontMetrics fm = paint.getFontMetrics();
//测量字符串的宽度
int textWidth = (int) paint.measureText(content);
//绘制字符串矩形区域的高度
int textHeight = (int) (fm.bottom - fm.top);
// x 轴的缩放比率
float scaleRateX = bCBitmap.getWidth() / textWidth;
paint.setTextScaleX(scaleRateX);
//绘制文本的基线
int baseLine = bCBitmap.getHeight() + textHeight;
//创建一个图层,然后在这个图层上绘制bCBitmap、content
Bitmap bitmap = Bitmap.createBitmap(bCBitmap.getWidth(),bCBitmap.getHeight() + 2 * textHeight,Bitmap.Config.ARGB_4444);
Canvas canvas = new Canvas();
canvas.drawColor(Color.WHITE);
canvas.setBitmap(bitmap);
canvas.drawBitmap(bCBitmap, 0, 0, null);
canvas.drawText(content,0,baseLine,paint);
canvas.save(Canvas.ALL_SAVE_FLAG);
canvas.restore();
        说明,这部分感觉还是比较难,下面就是详细分析下,在得到条形码的Bitmap(即bCBitmap)后,接下来就是把文本和条形码绘制到一起,绘制过程我们可以分为两部分,第一先绘制条形码的Bitmap,第二绘制文本,绘制条形码的Bitmap很简单,在这之前首先要创建一个新的图层,这个图层的宽度就是条形码图形的宽度,通过bCBitmap.getWidth()获取,那么高度呢,首先要有条形码图形的高度,即通过bCBitmap.getHeight()来获取,再加上文本绘制区域的高度,其值可以通过上面所述bottom线 - top线得到,如下:
Paint.FontMetrics fm = paint.getFontMetrics();
//绘制字符串矩形区域的高度
int textHeight = (int) (fm.bottom - fm.top);
那么创建新的图层代码如下 :
//创建一个图层,然后在这个图层上绘制bCBitmap、content
Bitmap bitmap = Bitmap.createBitmap(bCBitmap.getWidth(),bCBitmap.getHeight() +
2 * textHeight,Bitmap.Config.ARGB_4444);
说明下,为了不使文本与条形码图像间距过于紧密,我加上了2倍文本绘制区域的高度。
         如果仅仅这样处理的话,会发现绘制的文本左对齐,右边空了一截没有内容,看起来很不爽,我们知道Paint有个方法setTextScaleX可以
使用文本在x坐标上缩放,那么缩放多少呢?我们可以先测量文本在绘制区域所需的宽度,实现代码如下:
//测量字符串的宽度
int textWidth = (int) paint.measureText(content);
而,条形码图形的宽度,可以通过bCBitmap.getWidth()获取,条形码图形的宽度/文本在绘制区域所需的宽度就是文本在x坐标上缩放比,实现代码如下:
// x 轴的缩放比率
float scaleRateX = bCBitmap.getWidth() / textWidth;
这样以来就比刚刚的图像好看多了。
效果图如下:


/**
* 绘制条形码
* @param content 要生成条形码包含的内容
* @param widthPix 条形码的宽度
* @param heightPix 条形码的高度
* @param isShowContent 否则显示条形码包含的内容
* @return 返回生成条形的位图
*/
public static Bitmap createBarcode( String content, int widthPix, int heightPix, boolean isShowContent) {
if (TextUtils.isEmpty(content)){
return null;
}
//配置参数
Map<EncodeHintType,Object> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
// 容错级别 这里选择最高H级别
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
MultiFormatWriter writer = new MultiFormatWriter();

try {
// 图像数据转换,使用了矩阵转换 参数顺序分别为:编码内容,编码类型,生成图片宽度,生成图片高度,设置参数
BitMatrix bitMatrix = writer.encode(content, BarcodeFormat.CODE_128, 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;// 白色
}
}
}
Bitmap bitmap = Bitmap.createBitmap(widthPix, heightPix, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, widthPix, 0, 0, widthPix, heightPix);
if (isShowContent){
bitmap = showContent(bitmap,content);
}
return bitmap;
} catch (WriterException e) {
e.printStackTrace();
}

return null;
}

/**
* 显示条形的内容
* @param bCBitmap 已生成的条形码的位图
* @param content 条形码包含的内容
* @return 返回生成的新位图,它是 方法{@link #createQRCode(String, int, int, Bitmap)}返回的位图与新绘制文本content的组合
*/
private static Bitmap showContent(Bitmap bCBitmap , String content){
if (TextUtils.isEmpty(content) || null == bCBitmap){
return null;
}
Paint paint = new Paint();
paint.setColor(Color.BLACK);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);//设置填充样式
paint.setTextSize(20);
// paint.setTextAlign(Paint.Align.CENTER);
//测量字符串的宽度
int textWidth = (int) paint.measureText(content);
Paint.FontMetrics fm = paint.getFontMetrics();
//绘制字符串矩形区域的高度
int textHeight = (int) (fm.bottom - fm.top);
// x 轴的缩放比率
float scaleRateX = bCBitmap.getWidth() / textWidth;
paint.setTextScaleX(scaleRateX);
//绘制文本的基线
int baseLine = bCBitmap.getHeight() + textHeight;
//创建一个图层,然后在这个图层上绘制bCBitmap、content
Bitmap bitmap = Bitmap.createBitmap(bCBitmap.getWidth(),bCBitmap.getHeight() + 2 * textHeight,Bitmap.Config.ARGB_4444);
Canvas canvas = new Canvas();
canvas.drawColor(Color.WHITE);
canvas.setBitmap(bitmap);
canvas.drawBitmap(bCBitmap, 0, 0, null);
canvas.drawText(content,bCBitmap.getWidth() / 10,baseLine,paint);
canvas.save(Canvas.ALL_SAVE_FLAG);
canvas.restore();
return bitmap;
}
完整的代码工具类
参考
1 《Paint测量绘制文本
http://blog.csdn.net/zhoumushui/article/details/51008264

原创粉丝点击