zxing針對不同開發平臺,都給出瞭解析二維碼的例子,我這裏只聊聊關於android系統的解析。
對於android手機來說,二維碼圖像獲取方式有拍照掃描,以及讀取本地圖片資源。無論是哪種方式,解析過程的核心內容基本是一樣的。關於手機拍照掃描這塊,由於要涉及到很多問題要講,所以我打算把這塊放在下篇文章再細緻講解,這次只講如何對圖片進行解析。
首先,和生成二維碼一樣,我們要告訴系統解析二維碼的設置參數。這裏我選擇了支持主流的三類方式,其中一種爲一維碼(條形碼)。設置解析的字符位UTF8。如果不設置字符解析方式,它會自己去識別內容,然後自己判斷該用哪種方式。
看一下設置參數的代碼:
- MultiFormatReader multiFormatReader = new MultiFormatReader();
- // 解碼的參數
- Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>(
- 2);
- // 可以解析的編碼類型
- Vector<BarcodeFormat> decodeFormats = new Vector<BarcodeFormat>();
- if (decodeFormats == null || decodeFormats.isEmpty()) {
- decodeFormats = new Vector<BarcodeFormat>();
- // 這裏設置可掃描的類型,我這裏選擇了都支持
- decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS);
- decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
- decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);
- }
- hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
- // 設置繼續的字符編碼格式爲UTF8
- // hints.put(DecodeHintType.CHARACTER_SET, "UTF8");
- // 設置解析配置參數
- multiFormatReader.setHints(hints);
MultiFormatReader multiFormatReader = new MultiFormatReader();
// 解碼的參數
Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>(
2);
// 可以解析的編碼類型
Vector<BarcodeFormat> decodeFormats = new Vector<BarcodeFormat>();
if (decodeFormats == null || decodeFormats.isEmpty()) {
decodeFormats = new Vector<BarcodeFormat>();
// 這裏設置可掃描的類型,我這裏選擇了都支持
decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS);
decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);
}
hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
// 設置繼續的字符編碼格式爲UTF8
// hints.put(DecodeHintType.CHARACTER_SET, "UTF8");
// 設置解析配置參數
multiFormatReader.setHints(hints);
補一句:zxing源碼中對UTF8的定義字符串內容不是UTF-8,而是UTF8。
private static final String UTF8 = "UTF8";
再來就是解析部分:
- // 開始對圖像資源解碼
- Result rawResult = null;
- try {
- rawResult = multiFormatReader
- .decodeWithState(new BinaryBitmap(new HybridBinarizer(
- new BitmapLuminanceSource(BitmapFactory
- .decodeResource(getResources(),
- R.drawable.weibo)))));
- } catch (NotFoundException e) {
- e.printStackTrace();
- }
// 開始對圖像資源解碼
Result rawResult = null;
try {
rawResult = multiFormatReader
.decodeWithState(new BinaryBitmap(new HybridBinarizer(
new BitmapLuminanceSource(BitmapFactory
.decodeResource(getResources(),
R.drawable.weibo)))));
} catch (NotFoundException e) {
e.printStackTrace();
}
按照zxing的解碼規則,需要傳入一個LuminanceSource類的對象,最後就會得到解析結果result對象,也就是解碼後的信息類。這裏唯一需要自己實現的就是BitmapLuminanceSource類。
BitmapLuminanceSource繼承自LuminanceSource這個抽象類,需要實現它的構造方法,並重載getMatrix()和getRow(int y, byte[] row)方法。其構造方法中需要傳入寬高,這兩個值指的就是圖片的寬和高。getMatrix()方法會返回一個byte數組,這個數組就是圖片的像素數組。getRow(int y, byte[] row)如字面的意義,就是得到圖片像素數組的一行。其中的y就是需要的哪一個行的像素數組。
先看構造方法:
- protected BitmapLuminanceSource(Bitmap bitmap) {
- super(bitmap.getWidth(), bitmap.getHeight());
- // 首先,要取得該圖片的像素數組內容
- int[] data = new int[bitmap.getWidth() * bitmap.getHeight()];
- this.bitmapPixels = new byte[bitmap.getWidth() * bitmap.getHeight()];
- bitmap.getPixels(data, 0, getWidth(), 0, 0, getWidth(), getHeight());
- // 將int數組轉換爲byte數組
- for (int i = 0; i < data.length; i++) {
- this.bitmapPixels[i] = (byte) data[i];
- }
- }
protected BitmapLuminanceSource(Bitmap bitmap) {
super(bitmap.getWidth(), bitmap.getHeight());
// 首先,要取得該圖片的像素數組內容
int[] data = new int[bitmap.getWidth() * bitmap.getHeight()];
this.bitmapPixels = new byte[bitmap.getWidth() * bitmap.getHeight()];
bitmap.getPixels(data, 0, getWidth(), 0, 0, getWidth(), getHeight());
// 將int數組轉換爲byte數組
for (int i = 0; i < data.length; i++) {
this.bitmapPixels[i] = (byte) data[i];
}
}
注意:這裏的byte數組是指圖片的像素數組,而不是所謂Bitmap轉換成byte數組,有人出現解析的錯誤,大多是對這個參數用途沒理解造成的。
Bitmap對象的getPixels方法可以取得的像素數組,但它得到是int型數組。根據其api文檔解釋,取得的是color,也就是像素顏色值。每個像素值包含透明度,紅色,綠色,藍色。所以白色就是0xffffffff,黑色就是0xff000000。直接由int型轉成byte型,實現上相當於我們這裏只取其藍色值部分。
再來就是getRow方法:
- @Override
- public byte[] getRow(int y, byte[] row) {
- // 這裏要得到指定行的像素數據
- System.arraycopy(bitmapPixels, y * getWidth(), row, 0, getWidth());
- return row;
- }
@Override
public byte[] getRow(int y, byte[] row) {
// 這裏要得到指定行的像素數據
System.arraycopy(bitmapPixels, y * getWidth(), row, 0, getWidth());
return row;
}
補充:getPixels得到的像素數組是一維的,也就是按照圖片寬度逐行取像素顏色值錄入。如果想得到單行的像素數組內容,通過y*width就可以找該行的第一個像素值,拷貝後面width個就可以得到該行的像素內容。
最後一個就是getMatrix()方法,它用來返回我們的圖像轉換成的像素數組。
- @Override
- public byte[] getMatrix() {
- // 返回我們生成好的像素數據
- return bitmapPixels;
- }
@Override
public byte[] getMatrix() {
// 返回我們生成好的像素數據
return bitmapPixels;
}
以下是完整的BitmapLuminanceSource類:
- public class BitmapLuminanceSource extends LuminanceSource {
- private byte bitmapPixels[];
- protected BitmapLuminanceSource(Bitmap bitmap) {
- super(bitmap.getWidth(), bitmap.getHeight());
- // 首先,要取得該圖片的像素數組內容
- int[] data = new int[bitmap.getWidth() * bitmap.getHeight()];
- this.bitmapPixels = new byte[bitmap.getWidth() * bitmap.getHeight()];
- bitmap.getPixels(data, 0, getWidth(), 0, 0, getWidth(), getHeight());
- // 將int數組轉換爲byte數組,也就是取像素值中藍色值部分作爲辨析內容
- for (int i = 0; i < data.length; i++) {
- this.bitmapPixels[i] = (byte) data[i];
- }
- }
- @Override
- public byte[] getMatrix() {
- // 返回我們生成好的像素數據
- return bitmapPixels;
- }
- @Override
- public byte[] getRow(int y, byte[] row) {
- // 這裏要得到指定行的像素數據
- System.arraycopy(bitmapPixels, y * getWidth(), row, 0, getWidth());
- return row;
- }
- }
public class BitmapLuminanceSource extends LuminanceSource {
private byte bitmapPixels[];
protected BitmapLuminanceSource(Bitmap bitmap) {
super(bitmap.getWidth(), bitmap.getHeight());
// 首先,要取得該圖片的像素數組內容
int[] data = new int[bitmap.getWidth() * bitmap.getHeight()];
this.bitmapPixels = new byte[bitmap.getWidth() * bitmap.getHeight()];
bitmap.getPixels(data, 0, getWidth(), 0, 0, getWidth(), getHeight());
// 將int數組轉換爲byte數組,也就是取像素值中藍色值部分作爲辨析內容
for (int i = 0; i < data.length; i++) {
this.bitmapPixels[i] = (byte) data[i];
}
}
@Override
public byte[] getMatrix() {
// 返回我們生成好的像素數據
return bitmapPixels;
}
@Override
public byte[] getRow(int y, byte[] row) {
// 這裏要得到指定行的像素數據
System.arraycopy(bitmapPixels, y * getWidth(), row, 0, getWidth());
return row;
}
}
好了,最後就得到了我們想要的Result對象。
這個對象中包含了很多內容,包括內容,編碼方式,解析時間等。
我們想要的內容就放在rawResult.getText()中,你還可以得到它的編碼方式rawResult.getBarcodeFormat()。