Procházet zdrojové kódy

初步完善的Android的扫描功能

Kevin před 6 roky
rodič
revize
7720a6a4d0

+ 10 - 3
android/src/main/java/info/geteasy/fqreader/FqreaderPlugin.java

@@ -57,14 +57,21 @@ public class FqreaderPlugin implements MethodCallHandler {
           result.error("ScanView","Scan Already initialized",null);
           return;
         }
+        HashMap<String,Object> viewRectMap = call.argument("viewRect");
+        Rect viewRect = new Rect(
+                (int)viewRectMap .get("left"),
+                (int)viewRectMap .get("top"),
+                (int)viewRectMap .get("right"),
+                (int)viewRectMap .get("bottom")
+        );
         HashMap<String,Object> scanRectMap = call.argument("scanRect");
         Rect scanRect = new Rect(
                 (int)scanRectMap.get("left"),
                 (int)scanRectMap.get("top"),
-                (int)scanRectMap.get("width"),
-                (int)scanRectMap.get("height")
+                (int)scanRectMap.get("right"),
+                (int)scanRectMap.get("bottom")
         );
-        scanView = new ScanView(view,registrar,scanRect,result);
+        scanView = new ScanView(view,registrar,viewRect,scanRect,result);
         break;
       case "startScan":
         scanView.startScan();

+ 103 - 38
android/src/main/java/info/geteasy/fqreader/ScanView.java

@@ -4,9 +4,13 @@ import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
 import android.hardware.Camera;
+import android.os.Handler;
+import android.os.Message;
 import android.util.Log;
 
+import com.google.zxing.BarcodeFormat;
 import com.google.zxing.BinaryBitmap;
+import com.google.zxing.DecodeHintType;
 import com.google.zxing.LuminanceSource;
 import com.google.zxing.MultiFormatReader;
 import com.google.zxing.NotFoundException;
@@ -15,6 +19,8 @@ import com.google.zxing.ReaderException;
 import com.google.zxing.common.HybridBinarizer;
 
 import java.io.IOException;
+import java.util.Hashtable;
+import java.util.List;
 
 import io.flutter.plugin.common.EventChannel;
 import io.flutter.plugin.common.MethodChannel;
@@ -26,7 +32,7 @@ import static android.content.ContentValues.TAG;
 import static java.security.AccessController.getContext;
 
 
-public class ScanView implements Camera.PreviewCallback{
+public class ScanView implements Camera.PreviewCallback,Runnable{
     private FlutterView view;
     private TextureRegistry.SurfaceTextureEntry textureEntry;
     private Camera camera;
@@ -34,8 +40,11 @@ public class ScanView implements Camera.PreviewCallback{
     private PluginRegistry.Registrar registrar;
     private MultiFormatReader multiFormatReader;
     private EventChannel.EventSink eventSink;
+    private Handler resultHandler;
+    private Thread decodeThread;
+    private byte[] imageBytes;
 
-    ScanView(FlutterView view,PluginRegistry.Registrar registrar,Rect scanRect, MethodChannel.Result result){
+    ScanView(FlutterView view,PluginRegistry.Registrar registrar,Rect viewRect,Rect scanRect, MethodChannel.Result result){
         this.view = view;
         this.registrar = registrar;
         this.scanRect = scanRect;
@@ -43,13 +52,49 @@ public class ScanView implements Camera.PreviewCallback{
             multiFormatReader = new MultiFormatReader();
             this.textureEntry = view.createSurfaceTexture();
             camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
-//            Camera.Parameters param = camera.getParameters();
-//            param.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
-//            camera.setParameters(param);
+            Camera.Parameters param = camera.getParameters();
+            List<Camera.Size> sizes = param.getSupportedPreviewSizes();
+            param.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
+            param.setRotation(90);
+            double viewRatio = viewRect.width() * 1.0 / viewRect.height();
+            Camera.Size currentSize = sizes.get(0);
+            double currentRatio = currentSize.width * 1.0 / currentSize.height;
+            for(int i =1;i<sizes.size();i++){
+                Camera.Size  item= sizes.get(i);
+                double diffOld = Math.abs(currentRatio - viewRatio);
+                double diffNew = Math.abs(item.width * 1.0 / item.height - viewRatio);
+                if(diffOld < diffNew){
+                    currentSize = item;
+                    currentRatio = item.width * 1.0 / item.height;
+                }else if(diffOld == diffNew){
+                    if(item.height > currentSize.height){
+                        currentSize = item;
+                    }
+                }
+            }
+
+            param.setPreviewSize(currentSize.width,currentSize.height); // 设置预览图像大小
+            camera.setParameters(param);
             camera.setDisplayOrientation(90);
             camera.setPreviewTexture(textureEntry.surfaceTexture());
             result.success(textureEntry.id());
             registerEventChannel();
+            decodeThread = new Thread(this);
+            decodeThread.start();
+            resultHandler = new Handler() {
+                @Override
+                public void handleMessage(Message msg) {
+                    super.handleMessage(msg);
+                    switch (msg.what){
+                        case 1://继续扫描
+                            camera.setOneShotPreviewCallback(ScanView.this);
+                            break;
+                        case 2: //停止扫描
+                            eventSink.success((String) msg.obj);
+                            break;
+                    }
+                }
+            };
 
         }catch (Exception e) {
             result.error("ScanView init",e.getMessage(),null);
@@ -62,6 +107,7 @@ public class ScanView implements Camera.PreviewCallback{
     }
 
     void stopScan(){
+        camera.setOneShotPreviewCallback(null);
         camera.stopPreview();
     }
 
@@ -91,49 +137,68 @@ public class ScanView implements Camera.PreviewCallback{
 
     @Override
     public void onPreviewFrame(byte[] bytes, Camera camera) {
-        try {
-            Camera.Parameters parameters = camera.getParameters();
-            Camera.Size size = parameters.getPreviewSize();
-            int width = size.width;
-            int height = size.height;
-
+        synchronized (this){
+            this.imageBytes = bytes;
+            this.notify();
+        }
+    }
 
-            com.google.zxing.Result rawResult = null;
-            PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(bytes, width, height,scanRect.left,scanRect.top,scanRect.width(),scanRect.height(),false);
 
-            BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
+    @Override
+    public void run() {
+        while (true) {
             try {
-                rawResult = multiFormatReader.decodeWithState(bitmap);
-            } catch (ReaderException re) {
-                // continue
-            } catch (NullPointerException npe) {
-                // This is terrible
-            } catch (ArrayIndexOutOfBoundsException aoe) {
-
-            } finally {
-                multiFormatReader.reset();
-            }
+                synchronized (this) {
+                    this.wait();
+                }
+                Camera.Parameters parameters = camera.getParameters();
+                Camera.Size size = parameters.getPreviewSize();
+                int width = size.width;
+                int height = size.height;
+
+
+                com.google.zxing.Result rawResult = null;
+                PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(imageBytes, width, height, scanRect.top, scanRect.left, scanRect.height(), scanRect.width(), false);
 
-            if (rawResult == null) {
-                LuminanceSource invertedSource = source.invert();
-                bitmap = new BinaryBitmap(new HybridBinarizer(invertedSource));
+                BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
                 try {
-                    rawResult = multiFormatReader.decodeWithState(bitmap);
-                } catch (NotFoundException e) {
+                    rawResult = multiFormatReader.decode(bitmap);
+                } catch (ReaderException re) {
                     // continue
+                } catch (NullPointerException npe) {
+                    // This is terrible
+                } catch (ArrayIndexOutOfBoundsException aoe) {
+
                 } finally {
                     multiFormatReader.reset();
                 }
+//
+//                    if (rawResult == null) {
+//                        LuminanceSource invertedSource = source.invert();
+//                        bitmap = new BinaryBitmap(new HybridBinarizer(invertedSource));
+//                        try {
+//                            rawResult = multiFormatReader.decodeWithState(bitmap);
+//
+//                        } catch (NotFoundException e) {
+//                            // continue
+//                        } finally {
+//                            multiFormatReader.reset();
+//                        }
+//                    }
+
+                if (rawResult != null) {
+                    Message msg = new Message();
+                    msg.what = 2;
+                    msg.obj = rawResult.getText();
+                    this.resultHandler.sendMessage(msg);
+                } else {
+                    Message msg = new Message();
+                    msg.what = 1;
+                    this.resultHandler.sendMessage(msg);
+                }
+            } catch (InterruptedException e) {
+                e.printStackTrace();
             }
-
-            if (rawResult != null) {
-                eventSink.success(rawResult.getText());
-            } else {
-                camera.setOneShotPreviewCallback(this);
-            }
-        } catch(RuntimeException e) {
-            // TODO: Terrible hack. It is possible that this method is invoked after camera is released.
-            Log.e(TAG, e.toString(), e);
         }
     }
 }

+ 9 - 8
example/lib/main.dart

@@ -4,6 +4,7 @@ import 'dart:async';
 import 'package:flutter/services.dart';
 import 'package:fqreader/fqreader.dart';
 import 'package:flustars/flustars.dart';
+import 'package:cool_ui/cool_ui.dart';
 
 void main() => runApp(new MyApp());
 
@@ -27,6 +28,7 @@ class _MyAppState extends State<MyApp> {
         ScreenUtil.getInstance().init(context);
 
         double bodyHeight = (ScreenUtil.screenHeight - ScreenUtil.appBarHeight);
+        Rect viewRect = Rect.fromLTRB(0, 60, ScreenUtil.screenWidth, ScreenUtil.screenHeight);
         Rect scanRect= Rect.fromLTWH(
             ScreenUtil.screenWidth * 0.1,
             (bodyHeight - ScreenUtil.screenWidth * 0.8) / 2,
@@ -39,21 +41,20 @@ class _MyAppState extends State<MyApp> {
           body: Stack(
             children: <Widget>[
               ScanView(onScan: (value){
-                print(value);
-              },scanRect: Rect.fromLTWH(
-                  scanRect.left * MediaQuery.of(context).devicePixelRatio,
-                  scanRect.top * MediaQuery.of(context).devicePixelRatio,
-                  scanRect.width * MediaQuery.of(context).devicePixelRatio,
-                  scanRect.height * MediaQuery.of(context).devicePixelRatio)),
+                showWeuiSuccessToast(
+                    context: context,
+                    message:Text("扫描成功:" + value)
+                );
+              },viewRect: viewRect,scanRect:scanRect),
               Positioned(
                 top: 0.0,
                 left: 0.0,
-                child:FlatButton(child: Text("启动扫描"),onPressed: ()=>Fqreader.startScan(),),
+                child:FlatButton(child: Text("启动扫描"),color: Colors.red,onPressed: ()=>Fqreader.startScan(),),
               ),
               Positioned(
                 top: 0.0,
                 left: 80.0,
-                child:FlatButton(child: Text("暂停扫描"),onPressed: ()=>Fqreader.stopScan(),),
+                child:FlatButton(child: Text("暂停扫描"),color: Colors.red,onPressed: ()=>Fqreader.stopScan(),),
               ),
               Positioned(
                 top: scanRect.top,

+ 1 - 0
example/pubspec.yaml

@@ -20,6 +20,7 @@ dependencies:
   # Use with the CupertinoIcons class for iOS style icons.
   cupertino_icons: ^0.1.2
   flustars: 0.1.3
+  cool_ui: 0.1.3
   fqreader:
     path: ../
 

+ 19 - 7
lib/fqreader.dart

@@ -1,5 +1,9 @@
+library fqreader;
+
 import 'dart:async';
 
+import 'dart:ui' as ui;
+
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 
@@ -7,13 +11,19 @@ class Fqreader {
   static const MethodChannel _channel =
   const MethodChannel('fqreader');
 
-  static Future<int> initView({@required Rect scanRect}) async {
+  static Future<int> initView({@required Rect viewRect,@required Rect scanRect,double devicePixelRatio}) async {
     final int textureId = await _channel.invokeMethod('initView',{
+      "viewRect":{
+        "left":(viewRect.left * devicePixelRatio).toInt(),
+        "top":(viewRect.top* devicePixelRatio).toInt(),
+        "right":(viewRect.right* devicePixelRatio).toInt(),
+        "bottom":(viewRect.bottom* devicePixelRatio).toInt()
+      },
       "scanRect":{
-        "left":scanRect.left.toInt(),
-        "top":scanRect.top.toInt(),
-        "width":scanRect.width.toInt(),
-        "height":scanRect.height.toInt()
+        "left":(scanRect.left* devicePixelRatio).toInt(),
+        "top":(scanRect.top* devicePixelRatio).toInt(),
+        "right":(scanRect.right* devicePixelRatio).toInt(),
+        "bottom":(scanRect.bottom* devicePixelRatio).toInt(),
       }
     });
     return textureId;
@@ -31,8 +41,9 @@ class Fqreader {
 class ScanView extends StatefulWidget{
   final ValueChanged<String> onScan;
   final Rect scanRect;
+  final Rect viewRect;
 
-  const ScanView({this.onScan,@required this.scanRect});
+  const ScanView({this.onScan,@required this.viewRect,@required this.scanRect});
 
   @override
   State<StatefulWidget> createState() =>ScanViewState();
@@ -44,7 +55,8 @@ class ScanViewState extends State<ScanView>{
   void initState() {
     // TODO: implement initState
     super.initState();
-    Fqreader.initView(scanRect:widget.scanRect).then((textureId){
+    MediaQueryData mediaQuery = MediaQueryData.fromWindow(ui.window);
+    Fqreader.initView(viewRect: widget.viewRect,scanRect:widget.scanRect,devicePixelRatio:mediaQuery.devicePixelRatio).then((textureId){
       setState(() {
         _textureId = textureId;
       });