android 二维码扫描的逻辑

lachesism / 2024-09-04 / 原文

用的是ZXing库

依赖有

//二维码依赖(ZXing库)
    implementation 'com.journeyapps:zxing-android-embedded:4.3.0'
    implementation 'androidx.appcompat:appcompat:1.4.2'

 


1.建立一个二维码扫描的工具类(QrCodeScanner)

里面的逻辑有

  • 可以根据不同的标识符处理不同的扫描出来的结果
  • 权限的处理
public class QrCodeScanner {

    private static final int CAMERA_REQUEST_CODE = 100;  // 相机权限请求码
    private static final String TAG = "QrCodeScanner";  // 日志标签
    private final Fragment fragment;  // 用于启动扫描的Fragment
    private final String identifier;  // 标识符,用于区分不同的扫描请求
    private final ScanResultCallback callback;  // 扫描结果回调接口

    // 扫描结果回调接口,用于处理扫描结果
    public interface ScanResultCallback {
        void onScanResult(String msg);
    }

    // 构造函数,初始化Fragment、标识符和回调接口
    public QrCodeScanner(Fragment fragment, String identifier, ScanResultCallback callback) {
        this.fragment = fragment;
        this.identifier = identifier;
        this.callback = callback;
    }

    // 启动二维码扫描
    public void startScan() {
        Log.d(TAG, "startScan: Initiating scan");
        // 检查相机权限
        if (ContextCompat.checkSelfPermission(fragment.requireContext(), android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            Log.d(TAG, "startScan: Camera permission not granted, requesting permission");
            // 如果没有权限,请求相机权限
            fragment.requestPermissions(new String[]{android.Manifest.permission.CAMERA}, CAMERA_REQUEST_CODE);
        } else {
            // 已有权限,启动二维码扫描
            Log.d(TAG, "startScan: Camera permission granted, starting scan");
            initiateScan();
        }
    }

    // 实际启动扫描的方法
    private void initiateScan() {
        Log.d(TAG, "initiateScan: Starting QR code scan");
        Intent intent = new Intent(fragment.getActivity(), CustomCaptureActivity.class);
        fragment.startActivityForResult(intent, CAMERA_REQUEST_CODE);
    }

    // 处理权限请求结果
    public void handlePermissionsResult(int requestCode, @NonNull int[] grantResults) {
        if (requestCode == CAMERA_REQUEST_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Log.d(TAG, "handlePermissionsResult: Camera permission granted, starting scan");
                initiateScan();  // 如果权限被授予,启动扫描
            } else {
                Log.d(TAG, "handlePermissionsResult: Camera permission denied");
                Toast.makeText(fragment.getContext(), "相机权限被拒绝,无法进行二维码扫描", Toast.LENGTH_SHORT).show();
            }
        }
    }

    // 处理扫描结果
    public void handleScanResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == CAMERA_REQUEST_CODE && resultCode == Activity.RESULT_OK && data != null) {
            String scannedText = data.getStringExtra("SCANNED_TEXT");
            Log.d(TAG, "handleScanResult: Scanned text = " + scannedText);
            handleScannedResult(scannedText);
        } else {
            Log.d(TAG, "handleScanResult: Scan failed or cancelled");
            Toast.makeText(fragment.getContext(), "扫描失败或已取消", Toast.LENGTH_SHORT).show();
        }
    }

    // 根据不同的标识符处理扫描结果
    private void handleScannedResult(String scannedText) {
        if ("com/example/bighealth/mine/family".equals(identifier)) {
            Log.d(TAG, "handleScannedResult: Processing result for family identifier");
            // 将扫描结果传递给 FamilyActivity
            Intent intent = new Intent(fragment.getActivity(), FamilyActivity.class);
            intent.putExtra("scannedContent", scannedText);
            fragment.getActivity().startActivity(intent);
        } else {
            Log.d(TAG, "handleScannedResult: Unhandled identifier: " + identifier);
            if (callback != null) {
                callback.onScanResult(scannedText);
            }
        }
    }
}

2.建立一个CustomCaptureActivity(用来自定义扫描界面和解码)

这里我搞了个扫描线,和四角的框。

扫描的线我的逻辑是搞了一个正方形的框,扫描的线就在这个正方形里面移动,因为我还能获取到实际扫描框的大小,只能这样曲线救国了

public class CustomCaptureActivity extends Activity {

    private static final String TAG = "CustomCaptureActivity"; // 日志标签
    private CaptureManager capture;
    private CustomDecoratedBarcodeView barcodeScannerView;
    private ImageView scannerLine;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.zxing_custom_capture);  // 使用自定义的布局

        // 初始化BarcodeScannerView和扫描线
        barcodeScannerView = findViewById(R.id.zxing_barcode_scanner);  // 使用自定义的DecoratedBarcodeView
        scannerLine = findViewById(R.id.scanner_line);

        // 启动扫描线动画
        startScannerAnimation();

        // 初始化CaptureManager
        Log.d(TAG, "Initializing CaptureManager...");
        capture = new CaptureManager(this, barcodeScannerView);

        // 从Intent中初始化并启动解码
        Log.d(TAG, "Initializing from intent...");
        capture.initializeFromIntent(getIntent(), savedInstanceState);

        Log.d(TAG, "Starting QR code decode...");
        capture.decode();

        // 设置解码回调
        barcodeScannerView.decodeContinuous(new BarcodeCallback() {
            @Override
            public void barcodeResult(BarcodeResult result) {
                if (result != null) {
                    String scannedText = result.getText();
                    Log.d(TAG, "Scanned text: " + scannedText);

                    // 返回扫描结果
                    Intent resultIntent = new Intent();
                    resultIntent.putExtra("SCANNED_TEXT", scannedText);
                    setResult(Activity.RESULT_OK, resultIntent);
                    finish();  // 关闭活动
                }
            }

            @Override
            public void possibleResultPoints(List<ResultPoint> resultPoints) {
                // 可以处理可能的结果点
            }
        });
    }

    private void startScannerAnimation() {
        if (scannerLine != null) {
            View scannerFrame = findViewById(R.id.scanner_frame);
            int frameHeight = scannerFrame.getLayoutParams().height;

            TranslateAnimation animation = new TranslateAnimation(
                    0, 0, // x从0到0
                    0, frameHeight); // y从0到frameHeight
            animation.setDuration(2000); // 动画持续时间2秒
            animation.setRepeatCount(Animation.INFINITE); // 无限循环
            animation.setRepeatMode(Animation.REVERSE); // 动画反向播放
            scannerLine.startAnimation(animation); // 启动动画
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "Resuming CaptureManager...");
        capture.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "Pausing CaptureManager...");
        capture.onPause();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "Destroying CaptureManager...");
        capture.onDestroy();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        Log.d(TAG, "Handling permissions result...");
        capture.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        Log.d(TAG, "Handling key down event: " + keyCode);
        return barcodeScannerView.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
    }
}

上面的代码用的是下面这个自定义布局

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- 使用自定义的 CustomDecoratedBarcodeView -->
    <com.example.bighealth.customView.CustomDecoratedBarcodeView
        android:id="@+id/zxing_barcode_scanner"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <!-- 自定义扫描框 (正方形) -->
    <RelativeLayout
        android:id="@+id/scanner_frame"
        android:layout_width="310dp"
        android:layout_height="250dp"
        android:layout_gravity="center"
        android:background="@android:color/transparent">

        <!-- 自定义扫描线 -->
        <ImageView
            android:id="@+id/scanner_line"
            android:layout_width="match_parent"
            android:layout_height="4dp"
            android:background="#E8B66C"
            android:layout_alignParentTop="true" />
    </RelativeLayout>

</FrameLayout>

3.工具都做好了,接下来就是怎么用

  // 初始化二维码扫描器,传入标识符"family"
        qrCodeScanner = new QrCodeScanner(this, "com/example/bighealth/mine/family", new QrCodeScanner.ScanResultCallback() {
            @Override
            public void onScanResult(String msg) {
                // 将服务器返回的msg内容打印到日志中
                Log.d(TAG, "服务器返回的消息: " + msg);
                scannedQrCodeContent = msg;
                fetchUserData(scannedQrCodeContent);
                Log.d(TAG, "fetchUserData 方法被调用");
            }
        });