欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-07-14 17:34:22
...

前言

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

一般二维码:

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

带Icon的二维码:

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

更简单的方式

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

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

如果只是需要用到二维码的生成,只需要添加对应的依赖,然后直接按照QrCodeScannerView-Android中文说明文档或者文章上方的「如何使用」方式直接使用即可。