二维码介绍
二维码是一种能够存储信息并可以被电子设备扫描和解码的图形。通常是由黑色和白色的方块或点组成,以正方形或矩形的方式排列,用于存储网址、文本、电子邮件地址、电话号码、社交媒体链接等各种信息。
二维码的普及主要得益于智能手机的广泛使用和移动互联网的快速发展。二维码已经广泛应用于各种场景:
- 商业营销:商家可以将产品信息、优惠活动等信息编码到二维码中,顾客扫描后可以直接访问相关网页或下载应用。
- 社交媒体:个人可以将自己的社交媒体账号或联系方式编码到二维码中,方便他人快速添加。
- 支付:移动支付应用支持二维码支付,用户只需扫描商家的二维码即可完成支付。
- 票务:电影票、演唱会票等通过二维码进行验证和入场。
- 物流:物流公司将包裹信息编码到二维码中,方便收件人查询物流状态。
Version
Version是指二维码的规格或矩阵大小,决定了二维码可以存储的数据量。从Version 1到Version 40共有40种不同的规格。
- 「版本编号」:Version 1是起始版本,Version 40是最高版本。
- 「矩阵大小」:每个版本的二维码都由一个正方形的矩阵构成。Version 1的矩阵是21x21的,每增加一个版本,矩阵的每一边都增加4个模块。Version 2是25x25的矩阵,Version 3是29x29的矩阵,以此类推。最高版本Version 40的矩阵大小是177x177。
- 「计算公式」:矩阵大小的计算公式 (V-1) * 4 + 21,V是版本号。例如,Version 40的矩阵大小就是 (40-1) * 4 + 21 = 177。
- 「存储容量」:随着版本号的增加,二维码可以存储的数据量也相应增加。版本越高,二维码的容量越大,即可承载的内容越多。具体来说,Version 1的二维码最多可以储存25个字符或41个数字,Version 40的二维码最多可以储存4296个字符或7089个数字。
- 「实际应用」:在实际应用中,二维码的尺寸制定要看二维码包含多少内容以及是什么内容。如果是文字信息,就需要尺寸大一些,二维码生成的时候字符越多,二维码点阵就越密,当二维码密度比较大的时候需要印大一些才不会影响扫描效果。
定位图案
定位图案主要用于确定二维码的方向和位置,确保扫描设备能够准确识别和解码二维码。帮助扫描设备快速找到二维码的边界,并确定数据区域的位置。
「定位图案的组成」
- 「位置探测图形(Position Detection Pattern)」:最主要的定位图案,由三个黑色正方形组成,分别位于二维码的左上角、右上角和左下角。三个正方形内部还有一些白色的小正方形,与定位正方形的边缘之间有一些白色间隔(Separators for Position Detection Patterns)。三个定位图案足够标识一个矩形,因此不需要第四个。
- 「定位符(Timing Patterns)」:用于定位的线条,位于二维码的顶部和底部,以及左侧和右侧。由于二维码有多种尺寸(从Version 1到Version 40共有40种不同的规格),当尺寸变大时,需要这些定位符作为标准线,以确保扫描时不会偏移。
- 「对齐图案(Alignment Patterns)」:从Version 2开始才出现的定位图案。通常出现在二维码的中间部分,同样是为了辅助定位。随着版本号的增加,对齐图案的数量也会增加。
「定位图案的识别」在扫描二维码时,扫描设备首先会寻找并识别这些定位图案。一旦定位图案被识别,扫描设备就可以确定二维码的边界,并进一步解析数据区域中的信息。
功能性数据
功能性数据主要包括格式信息(Format Information),存在于所有尺寸二维码中的关键数据部分。
「格式信息(Format Information)」
- 「作用」:格式信息用于存放一些格式化数据,告诉扫描设备如何解码二维码中的信息。
- 「位置」:格式信息位于二维码的三个位置探测图形(Position Detection Pattern)附近(二维码的左上角、右上角和左下角)。
- 「内容」:格式信息包含二维码的纠错级别(Error Correction Level)和掩码(Mask)等信息。纠错级别决定了二维码在受损时能被正确识别的能力,掩码则是用于优化二维码的视觉效果和数据密度的。
「其他功能性数据」
二维码还包含其他功能性数据,如版本信息(Version Information,仅当二维码的版本大于1时才存在)、数据和纠错码字等。这些数据都按照特定的格式和规则进行编码和排列,确保二维码的准确性和可读性。
「数据编码方式」
二维码使用了一种称为“二进制位流”的方式来编码信息。使用了一系列的0和1(即二进制位)来表示不同的字符、数字或指令。通过特定的算法进行编码和排列,形成了看到的黑白相间的方块图案。
数据码和纠错码
数据码和纠错码是编码结构中的重要组成部分,共同确保了二维码信息的准确性和可靠性。
「数据码(Data Code)」
数据码是二维码中存储的实际数据,数据可以是文本、网址、数字、电子邮件地址等。在二维码的编码过程中,数据首先被转换成二进制位流,然后按照特定的规则进行编码和排列,形成二维码中的黑白方块图案。
- 数值模式(Numeric Mode):用于编码0-9的数字,每个数字由4位二进制数表示。
- 字母数字模式(Alphanumeric Mode):用于编码数字、大写字母(A-Z)、部分小写字母(a, e, i, o, u)以及一些符号(如$、%、*、+、-、.、/、:),每个字符由6位二进制数表示。
图片
- 字节模式(Byte Mode):用于编码ASCII字符集中的所有字符,每个字符由8位二进制数表示。
- 汉字模式(Kanji Mode):用于编码汉字,每个汉字由13位二进制数表示。
图片
「纠错码(Error Correction Code)」
纠错码是用于检测和纠正错误的冗余数据。在二维码的编码过程中,纠错码是通过特定的算法生成的,被添加到数据码之后。当扫描设备读取二维码时,首先会检查纠错码,以检测是否存在错误。如果检测到错误,扫描设备会使用纠错码来纠正这些错误,确保读取到的数据是准确的。
二维码的纠错能力可以通过设置不同的纠错级别来调整:
- L级(Low):能够纠正约7%的数据码错误。
- M级(Medium):能够纠正约15%的数据码错误。
- Q级(Quartile):能够纠正约25%的数据码错误。
- H级(High):能够纠正约30%的数据码错误。
图片
纠错码的生成和纠错过程使用Reed-Solomon算法来实现。通过向数据码中添加冗余信息(即纠错码)来创建一种数学关系,在数据码出现错误时,通过这种数学关系来恢复原始数据。
绘制二维码
「确定要编码的信息」:
选择要编码的信息,如文字、网址、电话号码等。信息将被转换为二维码的数据码。
「选择合适的编码模式」:
根据信息的类型选择合适的编码模式,如数字模式、字母数字模式、字节模式或汉字模式等。不同的模式有不同的编码效率和字符范围。
将要编码的信息根据所选的编码模式转换为二进制位流。例如,数字模式下每个数字用4位二进制表示,字母数字模式下每个字符用6位二进制表示。
根据编码后的数据量和所选的纠错级别,确定二维码的版本(Version)。版本决定了二维码的矩阵大小,从Version 1(21x21)到Version 40(177x177)不等。
根据版本和纠错级别,确定二维码的具体尺寸和结构,包括行数、列数、数据区域、校验区域等。
在二维码的左上角、右上角和左下角添加三个定位图案(位置探测图形),用于帮助扫描设备识别二维码的方向和位置(无论Version如何,这个图案的尺寸就是这么大)。
图片
「添加对齐图案」:
从Version 2开始,在二维码的中间部分添加对齐图案(Alignment Patterns),有助于在扫描过程中校正二维码的形状和大小。
图片
「添加格式信息和版本信息」:
在二维码的特定位置添加格式信息(Format Information),包含了纠错级别和掩码模式等关键信息。
如果二维码的版本大于1,还需要添加版本信息(Version Information)。
图片
Formation Information是一个15个bits的信息,每一个bit的位置如下图所示(注意图中的Dark Module,那是永远出现的)。
图片
Version Information一共是18个bits,其中包括6个bits的版本号以及12个bits的纠错码。
图片
「添加纠错码」:
根据所选的纠错级别,为数据区域添加纠错码(Error Correction Code)。纠错码用于检测和纠正扫描时可能产生的错误。
「掩模处理」:
对数据区域进行掩模处理,通过改变数据区域中某些模块的颜色来打破图案中可能出现的规律性的过程,以提高扫描的可靠性。
图片
下面是Mask后的一些样子,我们可以看到被某些Mask XOR了的数据变得比较零散了。
图片
「生成二维码图案」:
将经过掩模处理的数据区域和所有功能区域(如定位图案、对齐图案、格式信息和版本信息等)组合在一起,生成最终的二维码图案。
图片
「保存和使用二维码」:
保存生成的二维码图案,并可以根据需要将其打印出来或发布到网站、社交媒体等平台上供用户扫描使用。
代码生成二维码
使用zxing生成二维码
- 引入Zxing库 在项目中引入Zxing库,在build.gradle中添加相应的依赖:
dependencies {
implementation 'com.google.zxing:core:3.5.3'
}
- 创建QRCodeWriter对象 QRCodeWriter是Zxing库中的一个类,用于生成二维码。
QRCodeWriter qrCodeWriter = new QRCodeWriter();
- 设置二维码参数 设置二维码的尺寸、纠错级别等参数。
int width = 350; // 二维码宽度
int height = 350; // 二维码高度
ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.M; // 纠错级别
HashMap<EncodeHintType, ErrorCorrectionLevel> hints = new HashMap<>();
hints.put(EncodeHintType.ERROR_CORRECTION, errorCorrectionLevel);
- 调用QRCodeWriter的encode方法生成BitMatrix
将需要编码的字符串和参数传递给QRCodeWriter的encode方法,生成一个BitMatrix对象。
String text = "沐雨花飞蝶";
BitMatrix bitMatrix = qrCodeWriter.encode(text, BarcodeFormat.QR_CODE, width, height, hints);
- 将BitMatrix转换为图片
BarcodeEncoder barcodeEncoder = new BarcodeEncoder();
Bitmap bitmap = barcodeEncoder.createBitmap(bitMatrix);
- 显示或保存二维码
将生成的bitmap其设置给ImageView在屏幕上显示,或者保存到文件系统中。
使用第三方zxing-lite生成二维码
ZXingLite for Android 是ZXing的精简极速版,基于ZXing库优化扫码和生成二维码/条形码功能,扫码界面完全支持自定义;使用ZXingLite可快速实现扫码识别相关功能。
- 引入zxing-lite库 在项目中引入zxing-lite库,在build.gradle中添加相应的依赖:
dependencies {
// AndroidX 版本
implementation 'com.github.jenly1314:zxing-lite:3.1.1'
}
- 利用CodeUtils可以生成一个二维码的bitmap
private fun generateQRcode(content: String, ratio: Float): Bitmap {
val icon = BitmapFactory.decodeResource(resources, R.drawable.ic_default_profile)
val qrCode: Bitmap = CodeUtils.createQRCode(content, 600, icon, ratio)
return qrCode
}
CodeUtil代码实现:
/**
* 生成二维码
* @param content 二维码的内容
* @param heightPix 二维码的高
* @param logo 二维码中间的logo
* @param ratio logo所占比例 因为二维码的最大容错率为30%,所以建议ratio的范围小于0.3
* @return
*/
public static Bitmap createQRCode(String content, int heightPix, Bitmap logo,@FloatRange(from = 0.0f,to = 1.0f)float ratio) {
//配置参数
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put( EncodeHintType.CHARACTER_SET, "utf-8");
//容错级别
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
//设置空白边距的宽度
hints.put(EncodeHintType.MARGIN, 1); //default is 4
return createQRCode(content,heightPix,logo,ratio,hints);
}
public static Bitmap createQRCode(String content, int heightPix, Bitmap logo,@FloatRange(from = 0.0f,to = 1.0f)float ratio,Map<EncodeHintType,?> hints,int codeColor) {
try {
// 图像数据转换,使用了矩阵转换
BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, heightPix, heightPix, hints);
int[] pixels = new int[heightPix * heightPix];
// 下面这里按照二维码的算法,逐个生成二维码的图片,
// 两个for循环是图片横列扫描的结果
for (int y = 0; y < heightPix; y++) {
for (int x = 0; x < heightPix; x++) {
if (bitMatrix.get(x, y)) {
pixels[y * heightPix + x] = codeColor;
} else {
pixels[y * heightPix + x] = Color.WHITE;
}
}
}
// 生成二维码图片的格式
Bitmap bitmap = Bitmap.createBitmap(heightPix, heightPix, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, heightPix, 0, 0, heightPix, heightPix);
if (logo != null) {
bitmap = addLogo(bitmap, logo,ratio);
}
return bitmap;
} catch (WriterException e) {
Log.w(CaptureHelper.TAG,e.getMessage());
}
return null;
}
二维码生成最核心的代码:
BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, heightPix, heightPix, hints);
@Override
public BitMatrix encode(String contents,
BarcodeFormat format,
int width,
int height,
Map<EncodeHintType,?> hints) throws WriterException {
if (contents.isEmpty()) {
throw new IllegalArgumentException("Found empty contents");
}
if (format != BarcodeFormat.QR_CODE) {
throw new IllegalArgumentException("Can only encode QR_CODE, but got " + format);
}
if (width < 0 || height < 0) {
throw new IllegalArgumentException("Requested dimensions are too small: " + width + 'x' +
height);
}
ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;
int quietZone = QUIET_ZONE_SIZE;
if (hints != null) {
if (hints.containsKey(EncodeHintType.ERROR_CORRECTION)) {
errorCorrectionLevel = ErrorCorrectionLevel.valueOf(hints.get(EncodeHintType.ERROR_CORRECTION).toString());
}
if (hints.containsKey(EncodeHintType.MARGIN)) {
quietZone = Integer.parseInt(hints.get(EncodeHintType.MARGIN).toString());
}
}
QRCode code = Encoder.encode(contents, errorCorrectionLevel, hints);
return renderResult(code, width, height, quietZone);
}
public enum EncodeHintType {
/**
* Specifies what degree of error correction to use, for example in QR Codes.
* Type depends on the encoder. For example for QR codes it's type
* {@link com.google.zxing.qrcode.decoder.ErrorCorrectionLevel ErrorCorrectionLevel}.
* For Aztec it is of type {@link Integer}, representing the minimal percentage of error correction words.
* For PDF417 it is of type {@link Integer}, valid values being 0 to 8.
* In all cases, it can also be a {@link String} representation of the desired value as well.
* Note: an Aztec symbol should have a minimum of 25% EC words.
*/
ERROR_CORRECTION,
/**
* Specifies what character encoding to use where applicable (type {@link String})
*/
CHARACTER_SET,
/**
* Specifies the matrix shape for Data Matrix (type {@link com.google.zxing.datamatrix.encoder.SymbolShapeHint})
*/
DATA_MATRIX_SHAPE,
/**
* Specifies a minimum barcode size (type {@link Dimension}). Only applicable to Data Matrix now.
*
* @deprecated use width/height params in
* {@link com.google.zxing.datamatrix.DataMatrixWriter#encode(String, BarcodeFormat, int, int)}
*/
@Deprecated
MIN_SIZE,
/**
* Specifies a maximum barcode size (type {@link Dimension}). Only applicable to Data Matrix now.
*
* @deprecated without replacement
*/
@Deprecated
MAX_SIZE,
/**
* Specifies margin, in pixels, to use when generating the barcode. The meaning can vary
* by format; for example it controls margin before and after the barcode horizontally for
* most 1D formats. (Type {@link Integer}, or {@link String} representation of the integer value).
*/
MARGIN,
/**
* Specifies whether to use compact mode for PDF417 (type {@link Boolean}, or "true" or "false"
* {@link String} value).
*/
PDF417_COMPACT,
/**
* Specifies what compaction mode to use for PDF417 (type
* {@link com.google.zxing.pdf417.encoder.Compaction Compaction} or {@link String} value of one of its
* enum values).
*/
PDF417_COMPACTION,
/**
* Specifies the minimum and maximum number of rows and columns for PDF417 (type
* {@link com.google.zxing.pdf417.encoder.Dimensions Dimensions}).
*/
PDF417_DIMENSIONS,
/**
* Specifies the required number of layers for an Aztec code.
* A negative number (-1, -2, -3, -4) specifies a compact Aztec code.
* 0 indicates to use the minimum number of layers (the default).
* A positive number (1, 2, .. 32) specifies a normal (non-compact) Aztec code.
* (Type {@link Integer}, or {@link String} representation of the integer value).
*/
AZTEC_LAYERS,
/**
* Specifies the exact version of QR code to be encoded.
* (Type {@link Integer}, or {@link String} representation of the integer value).
*/
QR_VERSION,
/**
* Specifies whether the data should be encoded to the GS1 standard (type {@link Boolean}, or "true" or "false"
* {@link String } value).
*/
GS1_FORMAT,
}
EncodeHintType 代表二维码的一些格式化参数,可以指定二维码的纠错级别、字符集、外边框(白色边框)的宽度、二维码版本(QR_VERSION)等。