Android进阶 - 二维码生成

zxing.png

摘要

最近,公司业务上有个生成二维码图片的需求(Android端),之后笔者在网上查阅了一些资料,实现了这个功能。最后,给自己做个笔记,给各位做下分享。

什么是二维码?

百度链接:二维码

二维码生成方案(Android端)

在查找二维码生成方案时,发现很多方案的源头都指向了GitHub的开源库https://github.com/zxing/zxing

1. ZXing简介:

ZXing全称zebra crossing,翻译过来就是『斑马线』的意思。ZXing是一个采用Java实现的、开源的、支持多格式(一维/二维)的条形码图像处理库。

其中,QRCode格式就是我们常说的二维码格式。

注:QRCode(Quick Response Code:快速响应码)是二维条形码中最常用的一种格式,所以很多人直接将QRCode翻译为二维码,而且连百度百科都这样称呼,笔者也暂时就这么称呼了。

2. ZXing库引入

对于开发者来讲,我们需要下载ZXing库的一个jar包(core-x.x.x.jar)或者通过添加依赖的方式引入库文件,具体方法如下:

  • 方法一:ZXing提供了Maven库,让我们可以根据自己的需要选择想要的jar包版本进行下载。Maven库:https://repo1.maven.org/maven2/com/google/zxing/core/

  • 方法二(推荐):对于使用AndroidStudio开发的程序员而言,可能更习惯于在.gradle文件中添加依赖。具体代码如下(3.3.0是笔者使用时的最新版本,想知道最新版本是多少可以去Maven库查):

    dependencies {
        
        ......
        
        compile 'com.google.zxing:core:3.3.0'
    }
    

3. ZXing库使用

在公司的项目中,需要实现这样一个功能:根据传入的“url字符串”生成一张二维码图片。

也就是说,需要笔者写一个工具方法:接收一个传入的字符串,生成一个Bitmap对象并返回。

注:这里笔者进行了扩展,可以传入任意字符串(包含url字符串)。

接下来,先把笔者的二维码生成工具类附上(使用该工具类的前提:你已完成第2步,导入了ZXing的核心库)。

/**
 * @ClassName: QRCodeUtil
 * @Description: 二维码工具类
 * @Author Wangnan
 * @Date 2017/2/8
 */

public class QRCodeUtil {

    /**
     * 创建二维码位图
     *
     * @param content 字符串内容(支持中文)
     * @param width 位图宽度(单位:px)
     * @param height 位图高度(单位:px)
     * @return
     */
    @Nullable
    public static Bitmap createQRCodeBitmap(String content, int width, int height){
        return createQRCodeBitmap(content, width, height, "UTF-8", "H", "2", Color.BLACK, Color.WHITE);
    }

    /**
     * 创建二维码位图 (支持自定义配置和自定义样式)
     *
     * @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); // 空白边距设置
            }
            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;
    }
}

工具类的方法看不懂没关系,先缕清整体流程。之后,笔者会详解二维码生成方法createQRCodeBitmap。

这里,笔者写了两个重载的createQRCodeBitmap方法。其中第一个方法,笔者进行了默认的参数设置,可以满足生成二维码的大部分需求。

  • createQRCodeBitmap(String content, int width, int height):传入任意字符串和你想要的二维码图片的宽、高,生成一个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):完整的二维码生成方法,支持自定义配置和自定义样式。

接下来,给各位来一个实例,实现二维码生成。

4. 二维码生成(实例)

1.在AndroidStudio中新建一个Android工程。

2.导入ZXing的核心库。

dependencies {
    
    ......
    
    compile 'com.google.zxing:core:3.3.0'
}

3.将上面的“QRCodeUtil”工具类添加进工程。

4.在activity_main.xml中放置一个宽、高自适应的ImageView(用于显示二维码图片)。

5.在MainActivity中调用QRCodeUtil工具类中的createQRCodeBitmap方法生成二维码位图,之后将Bitmap对象设置进ImageView中。代码如下:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ImageView mImageView = (ImageView) findViewById(R.id.iv);
        Bitmap mBitmap = QRCodeUtil.createQRCodeBitmap("https://www.baidu.com", 480, 480);
        mImageView.setImageBitmap(mBitmap);
    }
}

这里,我们调用3个参数的createQRCodeBitmap方法:传入了一个url字符串(百度链接),传入了图片的宽、高。

接下来,跑一下程序,效果如下图所示:

QRCode_Demo.png.png

我们看到二维码已经生成,如果你有另一部手机就可以用扫码工具扫描验证了。

如果你只有一部手机,那该如何验证二维码信息的正确性呢?

给各位介绍一个简单的方法:微信

把当前截屏图片发送给自己的“微信小号”或“微信朋友”,利用微信的图片显示工具查看(长按图片,有二维码时会自动识别),如下所示:

arcode_wechat.gif

事实证明,笔者生成的二维码是正确的,微信跳转到了“百度一下”这个链接。

5. createQRCodeBitmap方法细节详解

虽然,笔者在createQRCodeBitmap方法中加了很多注释帮助各位理解二维码创建的流程,但可能各位对createQRCodeBitmap这个方法有些疑惑,这些配置参数有什么作用?ZXing这个库到底帮我们做了什么?接下来,我们详解这几个细节。

1.character_set(字符集/字符转码格式)这个参数有什么用?

笔者在3个参数的重载方法中设置的字符集是"UTF-8"。(代码如下:)

utf-8.png

ZXing源码默认使用的是"ISO-8859-1",而"ISO-8859-1"本身是不支持中文的。如果你的url中包含中文,字符集最好选用"UTF-8"。

接下来,给各位做个关于字符集的小实验。

笔者将字符集改为"ISO-8859-1"。

iso-8859-1.png

我们将“百度链接”换成“中文字符串”,如下所示:

modify_main.png

之后,笔者生成二维码在微信中进行识别,识别结果如下:

decode_error.png

我们看到中文字符都解码失败了。

之后,我们将"ISO-8859-1"改回"UTF-8"。再生成二维码在微信中识别,识别结果如下:

decode_success.png

我们可以看到识别成功了。(我们从这里也可以看出“微信”的使用的解码规则是"UTF-8")

2.error_correction(容错级别)这个参数有什么作用?

讲这个问题前,先给各位解释下什么是"容错"?

不知道各位有没有注意过这些扫码细节:二维码图片有时出现部分缺失(或破损)也能扫描成功;我们近距离扫二维码时,图形还没扫全就提示扫描成功了;有些二维码中间贴了一个小图标,扫码也能成功......这其实都归功于二维码有很强大的容错性

回到代码,笔者3个参数的createQRCodeBitmap方法使用的是"H",代码如下所示:

error_correction_set.png

这个"H"是什么鬼?看下源码,各位就懂了(“红色字体”是笔者加的“截图注释”)。

error_correction_source.png

有兴趣的朋友可以试下,同一个字符串使用不同容错率生成的二维码图形是不一样的,但扫出的信息是相同的。

3.margin(空白边距)这个参数有什么作用?

笔者3个参数的createQRCodeBitmap方法中传入的是"2"(如下图所示)。

margin_set.png

接下来,我们分别传入"0","1","2"看看二维码的图形有什么变化。(如下图所示)

margin_result.png

4.color_black,color_white 这两个参数有什么作用?

其实就是字面意思,黑色和白色。(这里只是建议颜色,笔者代码如下)

black_white.png

这里,我们换几种颜色试试。(效果图如下)

colors_test.png

当然,笔者还是建议使用主流的黑色和白色。因为在实际项目中玩花样,如果导致部分手机扫描不出来,就有点作死了...o(╯□╰)o。

5.ZXing这个库到底帮我们做了什么?

看下图,红线圈出的这两行代码是整个方法的核心,是笔者借助ZXing实现的。

zxing_core.png

BitMatrix是ZXing库中的一个类,我们将配置参数传入到QRCodeWriter的编码方法中,在encode的执行过程中ZXing库进行了相应的计算(计算哪个像素点应该是黑色,哪个像素点是白色),之后会生成一个BitMatrix对象存放最后的计算结果。

之后,如果你想知道矩阵中的哪个像素点是什么颜色可以调用bitMatrix.get(x,y)方法。如果该方法返回true,那么该像素点应该填充黑色,反之,应该填充白色。

细节解释就到这了,如果还有不懂的地方,笔者建议各位去看下ZXing的源码。

题外话

做项目时,笔者导入的ZXing的库大小是541KB,我们项目组的同事是一个“Geek”(做事追求极致的人),它感觉我这种方式太偷懒了。

因为ZXing这个库不仅仅提供了二维码生成的功能,还有比如二维码识别等其他功能。也就是说,有很多功能我们项目是用不到的。换句话说:jar包(或库文件)中的很多类都是用不到的

最后在同事的强烈建议下,我把ZXing生成二维码需要用到的类都提取了出来,重新进行了封装。最后提取了23个类,文件总大小144KB

但是,同事还是觉得库还是有点大(o(>_<)o 这同事一定是“处女座”的)。最后我只能在源码文件中一个类、一个类的找,把没有使用到的方法一一剔除。最后文件大小削减到了130KB

看到这里,我猜你是不是想问:还能不能再削减?

答案:能。还可以把源码中的所有注释全删了。但估计一段时间后,你再看源码就看不懂了。(一开始笔者也想删注释,但想了想还是放弃了)

发表一下自己的感慨:

best_friend.png

最后,附上Demo地址(包含ZXing删减库):https://github.com/sinawangnan7/QRCodeDemo
封装工具类:Android进阶 - 二维码生成(花式效果)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容