文章目錄
- 前言
- 效果示範
- 一、Zxing是什麼?
- 二、插件Android部分
-
- 1. 建立Flutter插件
- 2. 引入Zxing依賴庫
- 3. 權限配置
- 4. ZXingLite代碼引入
- 5. 編寫QrScannerPlugin
- 三、插件Flutter部分
- 總結
-
- Github位址
前言
在Dart Packages找了一圈二維碼掃描插件,發現幾個問題:
1. 擴充性低,自己想定制一下掃描框的樣式,改起來很麻煩;
2. 攝像頭不能拉進,導緻比較小的二維碼識别不了。
于是,在GitHub上找了一下Android的二維碼掃描插件(我的項目裡面隻針對Android裝置),ZXingLite比較符合我的要求,識别率高,代碼可讀性高,友善自己改寫。在此,感謝作者無私開源。
效果示範
二維碼掃描 | 生成二維碼 |
---|---|
一、Zxing是什麼?
Zxing(“zebra crossing”)是Google開源的,适用于Java、Android的條形碼/二維碼掃描庫。
二、插件Android部分
1. 建立Flutter插件
注意不要使用Kotlin,使用java進行Android部分代碼開發。當然你也可以自己用Kotlin改寫。
2. 引入Zxing依賴庫
修改插件的Android目錄下的build.gradle:
android {
compileSdkVersion 29
defaultConfig {
minSdkVersion 21 // camera2最低版本要求
}
lintOptions {
disable 'InvalidPackage'
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0'
// zxing
implementation 'com.google.zxing:core:3.4.1'
// camera2
implementation 'androidx.camera:camera-camera2:1.0.0-rc01'
implementation 'androidx.camera:camera-lifecycle:1.0.0-rc01'
implementation 'androidx.camera:camera-view:1.0.0-alpha19'
}
3. 權限配置
修改插件的Android目錄下的src/main/AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.vincent.qr_scanner">
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.FLASHLIGHT"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application>
<activity
android:name="com.vincent.qr_scanner.CaptureActivity"
android:screenOrientation="portrait"
android:theme="@style/CaptureTheme"/>
</application>
</manifest>
4. ZXingLite代碼引入
兩種方式處理:
- 通過gradle依賴引入
//AndroidX 版本
implementation 'com.king.zxing:zxing-lite:1.1.9-androidx'
//Android 版本
implementation 'com.king.zxing:zxing-lite:1.1.9'
- 直接複制其libs下面的代碼。
如果使用第一種方案,gradle配置中,隻需要設定minSdkVersion為21就可以了。JDK和dependencies 都不用設定。
我采用的是第二種方案,友善自己做一些擴充。
5. 編寫QrScannerPlugin
需要注意的幾個點:
- 為了擷取到Activity執行個體,該類需要實作ActivityAware相關方法
@Override
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
// 擷取activity執行個體
this.activity = binding.getActivity();
// 設定Activity傳回回調
binding.addActivityResultListener(this);
}
- 為了接收掃描傳回結果,該類需要實作ActivityResultListener相關方法
@Override
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK && data != null) {
if (requestCode == REQUEST_CODE) {
// 掃描傳回的結果
String result = CameraScan.parseScanResult(data);
this.result.success(result);
return true;
} else if (requestCode == REQUEST_IMAGE) {
// 選擇相冊後的結果
final String path = UriUtils.getImagePath(activity.getApplicationContext(), data);
String code = CodeReader.parseCode(path);
result.success(code);
return true;
}
}
return false;
}
- 實作onMethodCall方法
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
switch (call.method) {
case "scan":
Intent codeIntent = new Intent(activity, CaptureActivity.class);
activity.startActivityForResult(codeIntent, REQUEST_CODE);
this.result = result;
break;
case "pickImage":
if(!PermissionUtils.checkPermission(activity.getApplicationContext(), Manifest.permission.READ_EXTERNAL_STORAGE)){
PermissionUtils.requestPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE, 1);
result.success(null);
return;
}
Intent imgIntent = new Intent();
imgIntent.setAction(Intent.ACTION_PICK);
imgIntent.setType("image/*");
activity.startActivityForResult(imgIntent, REQUEST_IMAGE);
this.result = result;
break;
case "scanPath":
if(!PermissionUtils.checkPermission(activity.getApplicationContext(), Manifest.permission.READ_EXTERNAL_STORAGE)){
PermissionUtils.requestPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE, 1);
result.success(null);
return;
}
String path = call.argument("path");
result.success(CodeReader.parseCode(path));
break;
case "scanBitmap":
byte[] bytes = call.argument("bytes");
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes , 0, bytes != null ? bytes.length : 0);
result.success(CodeReader.parseCode(bitmap));
break;
case "createQRCode":
String qrCode = call.argument("code");
int qrWidth = call.argument("width");
long qrColor = call.argument("color");
Bitmap qrBt = CodeCreator.createQRCode(qrCode, qrWidth, (int) qrColor);
if (qrBt != null) {
ByteArrayOutputStream qrBo = new ByteArrayOutputStream();
qrBt.compress(Bitmap.CompressFormat.JPEG, 90, qrBo);
result.success(qrBo.toByteArray());
return;
}
result.success(null);
break;
case "createBarCode":
String barCode = call.argument("code");
int barWidth = call.argument("width");
int barHeight = call.argument("height");
boolean showText = call.argument("showText");
int fontSize = call.argument("fontSize");
long barColor = call.argument("color");
Bitmap barBt = CodeCreator.createBarCode(barCode, BarcodeFormat.CODE_128, barWidth, barHeight, null, showText, fontSize, (int) barColor);
if (barBt != null) {
ByteArrayOutputStream barBo = new ByteArrayOutputStream();
barBt.compress(Bitmap.CompressFormat.JPEG, 90, barBo);
result.success(barBo.toByteArray());
return;
}
result.success(null);
break;
default:
result.notImplemented();
}
}
三、插件Flutter部分
class QrScanner {
static const MethodChannel _channel =
const MethodChannel('vincent/qr_scanner');
/// 開啟掃描
static Future<String> scan() async {
return await _channel.invokeMethod('scan');
}
/// 選擇圖檔識别
static Future<String> pickImage() async {
return await _channel.invokeMethod('pickImage');
}
/// 識别圖檔檔案位址
static Future<String> scanPath(String imgPath) async {
assert(imgPath != null && File(imgPath).existsSync());
return await _channel.invokeMethod('scanPath', {'path': imgPath});
}
/// 識别圖檔資料流
static Future<String> scanBitmap(Uint8List uint8list) async {
assert(uint8list != null && uint8list.isNotEmpty);
return await _channel.invokeMethod('scanBitmap', {'bytes' : uint8list});
}
/// 生成二維碼
/// - [code] 碼
/// - [width] 二維碼寬高
/// - [color] 16進制顔色
static Future<Uint8List> createQRCode(String code, {
int width = 200,
int color = 0xFF000000
}) async {
return await _channel.invokeMethod('createQRCode', {
'code': code,
'width': width,
'color': color
});
}
/// 生成條形碼
/// - [code] 碼
/// - [width] 條形碼的寬
/// - [height] 條形碼的高
/// - [showText] 是否顯示文字
/// - [fontSize] 文字大小
/// - [color] 16進制顔色
static Future<Uint8List> createBarCode(String code, {
int width = 200,
int height = 200,
bool showText = false,
int fontSize = 14,
int color = 0xFF000000
}) async {
return await _channel.invokeMethod('createBarCode', {
'code': code,
'width': width,
'height': height,
'showText': showText,
'fontSize': fontSize,
'color': color
});
}
}
總結
Github位址
qr_scanner,README.MD檔案裡面詳細的說明插件使用方式