摘要
最近,公司业务上有个生成二维码图片的需求(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 <span class="hljs-string">'com.google.zxing:core:3.3.0'</span>
}
3. ZXing库使用
在公司的项目中,需要实现这样一个功能:根据传入的“url字符串”生成一张二维码图片。
也就是说,需要笔者写一个工具方法:接收一个传入的字符串,生成一个Bitmap对象并返回。
注:这里笔者进行了扩展,可以传入任意字符串(包含url字符串)。
接下来,先把笔者的二维码生成工具类附上(使用该工具类的前提:你已完成第2步,导入了ZXing的核心库)。
/**
* @ClassName: QRCodeUtil
* @Description: 二维码工具类
* @Author Wangnan
* @Date 2017/2/8
*/
public class QRCodeUtil {
<span class="hljs-comment">/**
* 创建二维码位图
*
* <span class="hljs-doctag">@param</span> content 字符串内容(支持中文)
* <span class="hljs-doctag">@param</span> width 位图宽度(单位:px)
* <span class="hljs-doctag">@param</span> height 位图高度(单位:px)
* <span class="hljs-doctag">@return</span>
*/</span>
<span class="hljs-meta">@Nullable</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Bitmap <span class="hljs-title">createQRCodeBitmap</span><span class="hljs-params">(String content, <span class="hljs-keyword">int</span> width, <span class="hljs-keyword">int</span> height)</span></span>{
<span class="hljs-keyword">return</span> createQRCodeBitmap(content, width, height, <span class="hljs-string">"UTF-8"</span>, <span class="hljs-string">"H"</span>, <span class="hljs-string">"2"</span>, Color.BLACK, Color.WHITE);
}
<span class="hljs-comment">/**
* 创建二维码位图 (支持自定义配置和自定义样式)
*
* <span class="hljs-doctag">@param</span> content 字符串内容
* <span class="hljs-doctag">@param</span> width 位图宽度,要求>=0(单位:px)
* <span class="hljs-doctag">@param</span> height 位图高度,要求>=0(单位:px)
* <span class="hljs-doctag">@param</span> character_set 字符集/字符转码格式 (支持格式:{<span class="hljs-doctag">@link</span> CharacterSetECI })。传null时,zxing源码默认使用 "ISO-8859-1"
* <span class="hljs-doctag">@param</span> error_correction 容错级别 (支持级别:{<span class="hljs-doctag">@link</span> ErrorCorrectionLevel })。传null时,zxing源码默认使用 "L"
* <span class="hljs-doctag">@param</span> margin 空白边距 (可修改,要求:整型且>=0), 传null时,zxing源码默认使用"4"。
* <span class="hljs-doctag">@param</span> color_black 黑色色块的自定义颜色值
* <span class="hljs-doctag">@param</span> color_white 白色色块的自定义颜色值
* <span class="hljs-doctag">@return</span>
*/</span>
<span class="hljs-meta">@Nullable</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Bitmap <span class="hljs-title">createQRCodeBitmap</span><span class="hljs-params">(String content, <span class="hljs-keyword">int</span> width, <span class="hljs-keyword">int</span> height,
@Nullable String character_set, @Nullable String error_correction, @Nullable String margin,
@ColorInt <span class="hljs-keyword">int</span> color_black, @ColorInt <span class="hljs-keyword">int</span> color_white)</span></span>{
<span class="hljs-comment">/** 1.参数合法性判断 */</span>
<span class="hljs-keyword">if</span>(TextUtils.isEmpty(content)){ <span class="hljs-comment">// 字符串内容判空</span>
<span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
}
<span class="hljs-keyword">if</span>(width < <span class="hljs-number">0</span> || height < <span class="hljs-number">0</span>){ <span class="hljs-comment">// 宽和高都需要>=0</span>
<span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
}
<span class="hljs-keyword">try</span> {
<span class="hljs-comment">/** 2.设置二维码相关配置,生成BitMatrix(位矩阵)对象 */</span>
Hashtable<EncodeHintType, String> hints = <span class="hljs-keyword">new</span> Hashtable<>();
<span class="hljs-keyword">if</span>(!TextUtils.isEmpty(character_set)) {
hints.put(EncodeHintType.CHARACTER_SET, character_set); <span class="hljs-comment">// 字符转码格式设置</span>
}
<span class="hljs-keyword">if</span>(!TextUtils.isEmpty(error_correction)){
hints.put(EncodeHintType.ERROR_CORRECTION, error_correction); <span class="hljs-comment">// 容错级别设置</span>
}
<span class="hljs-keyword">if</span>(!TextUtils.isEmpty(margin)){
hints.put(EncodeHintType.MARGIN, margin); <span class="hljs-comment">// 空白边距设置</span>
}
BitMatrix bitMatrix = <span class="hljs-keyword">new</span> QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
<span class="hljs-comment">/** 3.创建像素数组,并根据BitMatrix(位矩阵)对象为数组元素赋颜色值 */</span>
<span class="hljs-keyword">int</span>[] pixels = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[width * height];
<span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> y = <span class="hljs-number">0</span>; y < height; y++){
<span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> x = <span class="hljs-number">0</span>; x < width; x++){
<span class="hljs-keyword">if</span>(bitMatrix.get(x, y)){
pixels[y * width + x] = color_black; <span class="hljs-comment">// 黑色色块像素设置</span>
} <span class="hljs-keyword">else</span> {
pixels[y * width + x] = color_white; <span class="hljs-comment">// 白色色块像素设置</span>
}
}
}
<span class="hljs-comment">/** 4.创建Bitmap对象,根据像素数组设置Bitmap每个像素点的颜色值,之后返回Bitmap对象 */</span>
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, <span class="hljs-number">0</span>, width, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, width, height);
<span class="hljs-keyword">return</span> bitmap;
} <span class="hljs-keyword">catch</span> (WriterException e) {
e.printStackTrace();
}
<span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
}
}
工具类的方法看不懂没关系,先缕清整体流程。之后,笔者会详解二维码生成方法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 <span class="hljs-string">'com.google.zxing:core:3.3.0'</span>
}
3.将上面的“QRCodeUtil”工具类添加进工程。
4.在activity_main.xml中放置一个宽、高自适应的ImageView(用于显示二维码图片)。
5.在MainActivity中调用QRCodeUtil工具类中的createQRCodeBitmap方法生成二维码位图,之后将Bitmap对象设置进ImageView中。代码如下:
public class MainActivity extends AppCompatActivity {
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCreate</span><span class="hljs-params">(Bundle savedInstanceState)</span> </span>{
<span class="hljs-keyword">super</span>.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView mImageView = (ImageView) findViewById(R.id.iv);
Bitmap mBitmap = QRCodeUtil.createQRCodeBitmap(<span class="hljs-string">"https://www.baidu.com"</span>, <span class="hljs-number">480</span>, <span class="hljs-number">480</span>);
mImageView.setImageBitmap(mBitmap);
}
}
这里,我们调用3个参数的createQRCodeBitmap方法:传入了一个url字符串(百度链接),传入了图片的宽、高。
接下来,跑一下程序,效果如下图所示:
我们看到二维码已经生成,如果你有另一部手机就可以用扫码工具扫描验证了。
如果你只有一部手机,那该如何验证二维码信息的正确性呢?
给各位介绍一个简单的方法:微信
把当前截屏图片发送给自己的“微信小号”或“微信朋友”,利用微信的图片显示工具查看(长按图片,有二维码时会自动识别),如下所示:
事实证明,笔者生成的二维码是正确的,微信跳转到了“百度一下”这个链接。
5. createQRCodeBitmap方法细节详解
虽然,笔者在createQRCodeBitmap方法中加了很多注释帮助各位理解二维码创建的流程,但可能各位对createQRCodeBitmap这个方法有些疑惑,这些配置参数有什么作用?ZXing这个库到底帮我们做了什么?接下来,我们详解这几个细节。
1.character_set(字符集/字符转码格式)这个参数有什么用?
笔者在3个参数的重载方法中设置的字符集是"UTF-8"。(代码如下:)
ZXing源码默认使用的是"ISO-8859-1",而"ISO-8859-1"本身是不支持中文的。如果你的url中包含中文,字符集最好选用"UTF-8"。
接下来,给各位做个关于字符集的小实验。
笔者将字符集改为"ISO-8859-1"。
我们将“百度链接”换成“中文字符串”,如下所示:
之后,笔者生成二维码在微信中进行识别,识别结果如下:
我们看到中文字符都解码失败了。
之后,我们将"ISO-8859-1"改回"UTF-8"。再生成二维码在微信中识别,识别结果如下:
我们可以看到识别成功了。(我们从这里也可以看出“微信”的使用的解码规则是"UTF-8")
2.error_correction(容错级别)这个参数有什么作用?
讲这个问题前,先给各位解释下什么是"容错"?
不知道各位有没有注意过这些扫码细节:二维码图片有时出现部分缺失(或破损)也能扫描成功;我们近距离扫二维码时,图形还没扫全就提示扫描成功了;有些二维码中间贴了一个小图标,扫码也能成功......这其实都归功于二维码有很强大的容错性。
回到代码,笔者3个参数的createQRCodeBitmap方法使用的是"H",代码如下所示:
这个"H"是什么鬼?看下源码,各位就懂了(“红色字体”是笔者加的“截图注释”)。
有兴趣的朋友可以试下,同一个字符串使用不同容错率生成的二维码图形是不一样的,但扫出的信息是相同的。
3.margin(空白边距)这个参数有什么作用?
笔者3个参数的createQRCodeBitmap方法中传入的是"2"(如下图所示)。
接下来,我们分别传入"0","1","2"看看二维码的图形有什么变化。(如下图所示)
4.color_black,color_white 这两个参数有什么作用?
其实就是字面意思,黑色和白色。(这里只是建议颜色,笔者代码如下)
这里,我们换几种颜色试试。(效果图如下)
当然,笔者还是建议使用主流的黑色和白色。因为在实际项目中玩花样,如果导致部分手机扫描不出来,就有点作死了...o(╯□╰)o。
5.ZXing这个库到底帮我们做了什么?
看下图,红线圈出的这两行代码是整个方法的核心,是笔者借助ZXing实现的。
BitMatrix是ZXing库中的一个类,我们将配置参数传入到QRCodeWriter的编码方法中,在encode的执行过程中ZXing库进行了相应的计算(计算哪个像素点应该是黑色,哪个像素点是白色),之后会生成一个BitMatrix对象存放最后的计算结果。
之后,如果你想知道矩阵中的哪个像素点是什么颜色可以调用bitMatrix.get(x,y)方法。如果该方法返回true,那么该像素点应该填充黑色,反之,应该填充白色。
细节解释就到这了,如果还有不懂的地方,笔者建议各位去看下ZXing的源码。
题外话
做项目时,笔者导入的ZXing的库大小是541KB,我们项目组的同事是一个“Geek”(做事追求极致的人),它感觉我这种方式太偷懒了。
因为ZXing这个库不仅仅提供了二维码生成的功能,还有比如二维码识别等其他功能。也就是说,有很多功能我们项目是用不到的。换句话说:jar包(或库文件)中的很多类都是用不到的。
最后在同事的强烈建议下,我把ZXing生成二维码需要用到的类都提取了出来,重新进行了封装。最后提取了23个类,文件总大小144KB。
但是,同事还是觉得库还是有点大(o(>_<)o 这同事一定是“处女座”的)。最后我只能在源码文件中一个类、一个类的找,把没有使用到的方法一一剔除。最后文件大小削减到了130KB。
看到这里,我猜你是不是想问:还能不能再削减?
答案:能。还可以把源码中的所有注释全删了。但估计一段时间后,你再看源码就看不懂了。(一开始笔者也想删注释,但想了想还是放弃了)
发表一下自己的感慨:
最后,附上Demo地址(包含ZXing删减库):https://github.com/sinawangnan7/QRCodeDemo
封装工具类:Android进阶 - 二维码生成(花式效果)