一 前言

接下来就让我们来看看条形码的生成方式。在正式开始之前先来了来了解一个东西,因为在绘制条形码的内容的时候会用到,那就是drawText绘制,小的时候我们都适用过四线格写过英语单词,其实android 系统在进行文本绘制的时候就是这种格式,其格式如下:



四线三格写spring 四线三格写英语单词_图层



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



四线三格写spring 四线三格写英语单词_图层_02



它们的意义分别是:


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


这样以来就比刚刚的图像好看多了。


效果图如下:

四线三格写spring 四线三格写英语单词_条形码_03



/**
     * 绘制条形码
     * @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测量绘制文本