二維碼是什么?二維碼有什么用?二維碼繪制過程?二維碼如何生成?
二維碼介紹
二維碼是一種能夠存儲信息并可以被電子設(shè)備掃描和解碼的圖形。通常是由黑色和白色的方塊或點組成,以正方形或矩形的方式排列,用于存儲網(wǎng)址、文本、電子郵件地址、電話號碼、社交媒體鏈接等各種信息。
二維碼的普及主要得益于智能手機的廣泛使用和移動互聯(lián)網(wǎng)的快速發(fā)展。二維碼已經(jīng)廣泛應(yīng)用于各種場景:
- 商業(yè)營銷:商家可以將產(chǎn)品信息、優(yōu)惠活動等信息編碼到二維碼中,顧客掃描后可以直接訪問相關(guān)網(wǎng)頁或下載應(yīng)用。
- 社交媒體:個人可以將自己的社交媒體賬號或聯(lián)系方式編碼到二維碼中,方便他人快速添加。
- 支付:移動支付應(yīng)用支持二維碼支付,用戶只需掃描商家的二維碼即可完成支付。
- 票務(wù):電影票、演唱會票等通過二維碼進行驗證和入場。
- 物流:物流公司將包裹信息編碼到二維碼中,方便收件人查詢物流狀態(tài)。
Version
Version是指二維碼的規(guī)格或矩陣大小,決定了二維碼可以存儲的數(shù)據(jù)量。從Version 1到Version 40共有40種不同的規(guī)格。
- 「版本編號」:Version 1是起始版本,Version 40是最高版本。
- 「矩陣大小」:每個版本的二維碼都由一個正方形的矩陣構(gòu)成。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。
- 「存儲容量」:隨著版本號的增加,二維碼可以存儲的數(shù)據(jù)量也相應(yīng)增加。版本越高,二維碼的容量越大,即可承載的內(nèi)容越多。具體來說,Version 1的二維碼最多可以儲存25個字符或41個數(shù)字,Version 40的二維碼最多可以儲存4296個字符或7089個數(shù)字。
- 「實際應(yīng)用」:在實際應(yīng)用中,二維碼的尺寸制定要看二維碼包含多少內(nèi)容以及是什么內(nèi)容。如果是文字信息,就需要尺寸大一些,二維碼生成的時候字符越多,二維碼點陣就越密,當二維碼密度比較大的時候需要印大一些才不會影響掃描效果。
定位圖案
定位圖案主要用于確定二維碼的方向和位置,確保掃描設(shè)備能夠準確識別和解碼二維碼。幫助掃描設(shè)備快速找到二維碼的邊界,并確定數(shù)據(jù)區(qū)域的位置。
「定位圖案的組成」
- 「位置探測圖形(Position Detection Pattern)」:最主要的定位圖案,由三個黑色正方形組成,分別位于二維碼的左上角、右上角和左下角。三個正方形內(nèi)部還有一些白色的小正方形,與定位正方形的邊緣之間有一些白色間隔(Separators for Position Detection Patterns)。三個定位圖案足夠標識一個矩形,因此不需要第四個。
- 「定位符(Timing Patterns)」:用于定位的線條,位于二維碼的頂部和底部,以及左側(cè)和右側(cè)。由于二維碼有多種尺寸(從Version 1到Version 40共有40種不同的規(guī)格),當尺寸變大時,需要這些定位符作為標準線,以確保掃描時不會偏移。
- 「對齊圖案(Alignment Patterns)」:從Version 2開始才出現(xiàn)的定位圖案。通常出現(xiàn)在二維碼的中間部分,同樣是為了輔助定位。隨著版本號的增加,對齊圖案的數(shù)量也會增加。
「定位圖案的識別」在掃描二維碼時,掃描設(shè)備首先會尋找并識別這些定位圖案。一旦定位圖案被識別,掃描設(shè)備就可以確定二維碼的邊界,并進一步解析數(shù)據(jù)區(qū)域中的信息。
功能性數(shù)據(jù)
功能性數(shù)據(jù)主要包括格式信息(Format Information),存在于所有尺寸二維碼中的關(guān)鍵數(shù)據(jù)部分。
「格式信息(Format Information)」
- 「作用」:格式信息用于存放一些格式化數(shù)據(jù),告訴掃描設(shè)備如何解碼二維碼中的信息。
- 「位置」:格式信息位于二維碼的三個位置探測圖形(Position Detection Pattern)附近(二維碼的左上角、右上角和左下角)。
- 「內(nèi)容」:格式信息包含二維碼的糾錯級別(Error Correction Level)和掩碼(Mask)等信息。糾錯級別決定了二維碼在受損時能被正確識別的能力,掩碼則是用于優(yōu)化二維碼的視覺效果和數(shù)據(jù)密度的。
「其他功能性數(shù)據(jù)」
二維碼還包含其他功能性數(shù)據(jù),如版本信息(Version Information,僅當二維碼的版本大于1時才存在)、數(shù)據(jù)和糾錯碼字等。這些數(shù)據(jù)都按照特定的格式和規(guī)則進行編碼和排列,確保二維碼的準確性和可讀性。
「數(shù)據(jù)編碼方式」
二維碼使用了一種稱為“二進制位流”的方式來編碼信息。使用了一系列的0和1(即二進制位)來表示不同的字符、數(shù)字或指令。通過特定的算法進行編碼和排列,形成了看到的黑白相間的方塊圖案。
數(shù)據(jù)碼和糾錯碼
數(shù)據(jù)碼和糾錯碼是編碼結(jié)構(gòu)中的重要組成部分,共同確保了二維碼信息的準確性和可靠性。
「數(shù)據(jù)碼(Data Code)」
數(shù)據(jù)碼是二維碼中存儲的實際數(shù)據(jù),數(shù)據(jù)可以是文本、網(wǎng)址、數(shù)字、電子郵件地址等。在二維碼的編碼過程中,數(shù)據(jù)首先被轉(zhuǎn)換成二進制位流,然后按照特定的規(guī)則進行編碼和排列,形成二維碼中的黑白方塊圖案。
- 數(shù)值模式(Numeric Mode):用于編碼0-9的數(shù)字,每個數(shù)字由4位二進制數(shù)表示。
- 字母數(shù)字模式(Alphanumeric Mode):用于編碼數(shù)字、大寫字母(A-Z)、部分小寫字母(a, e, i, o, u)以及一些符號(如$、%、*、+、-、.、/、:),每個字符由6位二進制數(shù)表示。
圖片
- 字節(jié)模式(Byte Mode):用于編碼ASCII字符集中的所有字符,每個字符由8位二進制數(shù)表示。
- 漢字模式(Kanji Mode):用于編碼漢字,每個漢字由13位二進制數(shù)表示。
圖片
「糾錯碼(Error Correction Code)」
糾錯碼是用于檢測和糾正錯誤的冗余數(shù)據(jù)。在二維碼的編碼過程中,糾錯碼是通過特定的算法生成的,被添加到數(shù)據(jù)碼之后。當掃描設(shè)備讀取二維碼時,首先會檢查糾錯碼,以檢測是否存在錯誤。如果檢測到錯誤,掃描設(shè)備會使用糾錯碼來糾正這些錯誤,確保讀取到的數(shù)據(jù)是準確的。
二維碼的糾錯能力可以通過設(shè)置不同的糾錯級別來調(diào)整:
- L級(Low):能夠糾正約7%的數(shù)據(jù)碼錯誤。
- M級(Medium):能夠糾正約15%的數(shù)據(jù)碼錯誤。
- Q級(Quartile):能夠糾正約25%的數(shù)據(jù)碼錯誤。
- H級(High):能夠糾正約30%的數(shù)據(jù)碼錯誤。
圖片
糾錯碼的生成和糾錯過程使用Reed-Solomon算法來實現(xiàn)。通過向數(shù)據(jù)碼中添加冗余信息(即糾錯碼)來創(chuàng)建一種數(shù)學(xué)關(guān)系,在數(shù)據(jù)碼出現(xiàn)錯誤時,通過這種數(shù)學(xué)關(guān)系來恢復(fù)原始數(shù)據(jù)。
繪制二維碼
「確定要編碼的信息」:
選擇要編碼的信息,如文字、網(wǎng)址、電話號碼等。信息將被轉(zhuǎn)換為二維碼的數(shù)據(jù)碼。
「選擇合適的編碼模式」:
根據(jù)信息的類型選擇合適的編碼模式,如數(shù)字模式、字母數(shù)字模式、字節(jié)模式或漢字模式等。不同的模式有不同的編碼效率和字符范圍。
將要編碼的信息根據(jù)所選的編碼模式轉(zhuǎn)換為二進制位流。例如,數(shù)字模式下每個數(shù)字用4位二進制表示,字母數(shù)字模式下每個字符用6位二進制表示。
根據(jù)編碼后的數(shù)據(jù)量和所選的糾錯級別,確定二維碼的版本(Version)。版本決定了二維碼的矩陣大小,從Version 1(21x21)到Version 40(177x177)不等。
根據(jù)版本和糾錯級別,確定二維碼的具體尺寸和結(jié)構(gòu),包括行數(shù)、列數(shù)、數(shù)據(jù)區(qū)域、校驗區(qū)域等。
在二維碼的左上角、右上角和左下角添加三個定位圖案(位置探測圖形),用于幫助掃描設(shè)備識別二維碼的方向和位置(無論Version如何,這個圖案的尺寸就是這么大)。
圖片
「添加對齊圖案」:
從Version 2開始,在二維碼的中間部分添加對齊圖案(Alignment Patterns),有助于在掃描過程中校正二維碼的形狀和大小。
圖片
「添加格式信息和版本信息」:
在二維碼的特定位置添加格式信息(Format Information),包含了糾錯級別和掩碼模式等關(guān)鍵信息。
如果二維碼的版本大于1,還需要添加版本信息(Version Information)。
圖片
Formation Information是一個15個bits的信息,每一個bit的位置如下圖所示(注意圖中的Dark Module,那是永遠出現(xiàn)的)。
圖片
Version Information一共是18個bits,其中包括6個bits的版本號以及12個bits的糾錯碼。
圖片
「添加糾錯碼」:
根據(jù)所選的糾錯級別,為數(shù)據(jù)區(qū)域添加糾錯碼(Error Correction Code)。糾錯碼用于檢測和糾正掃描時可能產(chǎn)生的錯誤。
「掩模處理」:
對數(shù)據(jù)區(qū)域進行掩模處理,通過改變數(shù)據(jù)區(qū)域中某些模塊的顏色來打破圖案中可能出現(xiàn)的規(guī)律性的過程,以提高掃描的可靠性。
圖片
下面是Mask后的一些樣子,我們可以看到被某些Mask XOR了的數(shù)據(jù)變得比較零散了。
圖片
「生成二維碼圖案」:
將經(jīng)過掩模處理的數(shù)據(jù)區(qū)域和所有功能區(qū)域(如定位圖案、對齊圖案、格式信息和版本信息等)組合在一起,生成最終的二維碼圖案。
圖片
「保存和使用二維碼」:
保存生成的二維碼圖案,并可以根據(jù)需要將其打印出來或發(fā)布到網(wǎng)站、社交媒體等平臺上供用戶掃描使用。
代碼生成二維碼
使用zxing生成二維碼
- 引入Zxing庫 在項目中引入Zxing庫,在build.gradle中添加相應(yīng)的依賴:
dependencies {
implementation 'com.google.zxing:core:3.5.3'
}
- 創(chuàng)建QRCodeWriter對象 QRCodeWriter是Zxing庫中的一個類,用于生成二維碼。
QRCodeWriter qrCodeWriter = new QRCodeWriter();
- 設(shè)置二維碼參數(shù) 設(shè)置二維碼的尺寸、糾錯級別等參數(shù)。
int width = 350; // 二維碼寬度
int height = 350; // 二維碼高度
ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.M; // 糾錯級別
HashMap<EncodeHintType, ErrorCorrectionLevel> hints = new HashMap<>();
hints.put(EncodeHintType.ERROR_CORRECTION, errorCorrectionLevel);
- 調(diào)用QRCodeWriter的encode方法生成BitMatrix
將需要編碼的字符串和參數(shù)傳遞給QRCodeWriter的encode方法,生成一個BitMatrix對象。
String text = "沐雨花飛蝶";
BitMatrix bitMatrix = qrCodeWriter.encode(text, BarcodeFormat.QR_CODE, width, height, hints);
- 將BitMatrix轉(zhuǎn)換為圖片
BarcodeEncoder barcodeEncoder = new BarcodeEncoder();
Bitmap bitmap = barcodeEncoder.createBitmap(bitMatrix);
- 顯示或保存二維碼
將生成的bitmap其設(shè)置給ImageView在屏幕上顯示,或者保存到文件系統(tǒng)中。
使用第三方zxing-lite生成二維碼
ZXingLite for Android 是ZXing的精簡極速版,基于ZXing庫優(yōu)化掃碼和生成二維碼/條形碼功能,掃碼界面完全支持自定義;使用ZXingLite可快速實現(xiàn)掃碼識別相關(guān)功能。
- 引入zxing-lite庫 在項目中引入zxing-lite庫,在build.gradle中添加相應(yīng)的依賴:
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代碼實現(xiàn):
/**
* 生成二維碼
* @param content 二維碼的內(nèi)容
* @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) {
//配置參數(shù)
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put( EncodeHintType.CHARACTER_SET, "utf-8");
//容錯級別
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
//設(shè)置空白邊距的寬度
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 {
// 圖像數(shù)據(jù)轉(zhuǎn)換,使用了矩陣轉(zhuǎn)換
BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, heightPix, heightPix, hints);
int[] pixels = new int[heightPix * heightPix];
// 下面這里按照二維碼的算法,逐個生成二維碼的圖片,
// 兩個for循環(huán)是圖片橫列掃描的結(jié)果
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 代表二維碼的一些格式化參數(shù),可以指定二維碼的糾錯級別、字符集、外邊框(白色邊框)的寬度、二維碼版本(QR_VERSION)等。