萌萌の初音
萌萌の初音
发布于 2021-11-02 / 1014 阅读
0

zxing二维码扫描CaptureActivity抽象类封装

接入zxing二维码开源项目,直接使用写好的CaptureActivity需要进行修改,使用不便,将CaptureActivity修改为抽象类方便接入实现定制化的需求。
效果图:
Screenshot_20211102_1649511.jpg

直接贴CaptureActivity代码:

package com.google.zxing.activity;

import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Handler;
import android.view.SurfaceHolder;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.Result;
import com.google.zxing.camera.CameraManager;
import com.google.zxing.decoding.CaptureActivityHandler;
import com.google.zxing.decoding.InactivityTimer;
import com.google.zxing.view.ViewfinderView;

import java.io.IOException;
import java.util.Vector;

abstract public class CaptureActivity extends AppCompatActivity {

    public CaptureActivityHandler handler;
    public ViewfinderView viewfinderView;
    public Vector<BarcodeFormat> decodeFormats;
    public String characterSet;
    public InactivityTimer inactivityTimer;

    public Handler getHandler() {
        return handler;
    }

    public ViewfinderView getViewfinderView() {
        return viewfinderView;
    }

    /**
     * Handler scan result
     *
     * @param result
     * @param barcode
     */
    public void handleDecode(Result result, Bitmap barcode) {
        inactivityTimer.onActivity();
        onPauseScan();
        scanResult(result, barcode);
    }

    abstract public void scanResult(Result result, Bitmap barcode);

    public abstract void init();

    public void drawViewfinder() {
        viewfinderView.drawViewfinder();
    }

    /**
     * {@inheritDoc}
     * <p>
     * Perform initialization of all fragments.
     *
     * @param savedInstanceState
     */
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initCamera();
    }

    public void initCamera() {
        CameraManager.init(getApplication());
        inactivityTimer = new InactivityTimer(this);
    }

    public void onPauseScan() {
        if (handler != null) {
            handler.quitSynchronously();
            handler = null;
        }
        CameraManager.get().closeDriver();
    }

    public void initCamera(SurfaceHolder surfaceHolder) {
        try {
            CameraManager.get().openDriver(surfaceHolder);
        } catch (IOException ioe) {
            return;
        } catch (RuntimeException e) {
            return;
        }
        if (handler == null) {
            handler = new CaptureActivityHandler(this, decodeFormats, characterSet);
        }
    }
}

修改后接入在自己的activity上,首先在layout中对布局文件进行修改,以activity_main.xml为例:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

    <FrameLayout
            android:id="@+id/qrcode_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        <SurfaceView
                android:id="@+id/qrcode_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_gravity="center" />

        <com.google.zxing.view.ViewfinderView
                android:id="@+id/viewfinder_content"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:corner_color="#5677fc"
                app:frame_color="#00000000"
                app:label_text=""
                app:label_text_color="#ff4081"
                app:laser_color="#5677fc"
                app:mask_color="#99000000"
                app:result_color="#000000"
                app:result_point_color="#FFFB00" />

    </FrameLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

只需要将FrameLayout内容加入进你的布局文件即可。
SurfaceView用于摄像头画面显示,ViewfinderView用于二维码扫描动画UI(可换成自己的UI)。
以MainActivity.kt为例,先贴代码:

package com.rainblog.scanqr

import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.os.Bundle
import android.view.SurfaceHolder
import android.view.SurfaceView
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.google.zxing.activity.CaptureActivity

class MainActivity : CaptureActivity(), SurfaceHolder.Callback {

    private var hasSurface = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        init()
    }

    /**
     * Dispatch onResume() to fragments.  Note that for better inter-operation
     * with older versions of the platform, at the point of this call the
     * fragments attached to the activity are *not* resumed.
     */
    override fun onResume() {
        super.onResume()
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), 1)
        } else onRestartScan()
    }

    /**
     * Dispatch onPause() to fragments.
     */
    override fun onPause() {
        onPauseScan()
        super.onPause()
    }

    override fun onDestroy() {
        inactivityTimer.shutdown()
        super.onDestroy()
    }

    /**
     * Callback for the result from requesting permissions. This method
     * is invoked for every call on [.requestPermissions].
     *
     *
     * **Note:** It is possible that the permissions request interaction
     * with the user is interrupted. In this case you will receive empty permissions
     * and results arrays which should be treated as a cancellation.
     *
     *
     * @param requestCode The request code passed in [.requestPermissions].
     * @param permissions The requested permissions. Never null.
     * @param grantResults The grant results for the corresponding permissions
     * which is either [android.content.pm.PackageManager.PERMISSION_GRANTED]
     * or [android.content.pm.PackageManager.PERMISSION_DENIED]. Never null.
     *
     * @see .requestPermissions
     */
    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == 1 && permissions.size > 0) {
            for (s in permissions) {
                if (ContextCompat.checkSelfPermission(this, s) != PackageManager.PERMISSION_GRANTED) {
                    val dialog = AlertDialog.Builder(this).setMessage("缺少相机权限").create()
                    dialog.setOnDismissListener {
                        finish()
                    }
                    dialog.show()
                } else {
                    finish()
                    startActivity(Intent(this,MainActivity::class.java))
                }
            }
        }
    }

    override fun scanResult(result: com.google.zxing.Result?, barcode: Bitmap?) {
        val dialog = AlertDialog.Builder(this).setMessage("扫描结果").setMessage(result?.text).create()
        dialog.setOnDismissListener {
            onRestartScan()
        }
        dialog.show()
    }

    override fun init() {
        viewfinderView = findViewById(R.id.viewfinder_content)
    }

    /**
     * This is called immediately after the surface is first created.
     * Implementations of this should start up whatever rendering code
     * they desire.  Note that only one thread can ever draw into
     * a [Surface], so you should not draw into the Surface here
     * if your normal rendering will be in another thread.
     *
     * @param holder The SurfaceHolder whose surface is being created.
     */
    override fun surfaceCreated(holder: SurfaceHolder) {
        if (!hasSurface) {
            hasSurface = true
            initCamera(holder)
        }
    }

    /**
     * This is called immediately after any structural changes (format or
     * size) have been made to the surface.  You should at this point update
     * the imagery in the surface.  This method is always called at least
     * once, after [.surfaceCreated].
     *
     * @param holder The SurfaceHolder whose surface has changed.
     * @param format The new [PixelFormat] of the surface.
     * @param width The new width of the surface.
     * @param height The new height of the surface.
     */
    override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {}

    /**
     * This is called immediately before a surface is being destroyed. After
     * returning from this call, you should no longer try to access this
     * surface.  If you have a rendering thread that directly accesses
     * the surface, you must ensure that thread is no longer touching the
     * Surface before returning from this function.
     *
     * @param holder The SurfaceHolder whose surface is being destroyed.
     */
    override fun surfaceDestroyed(holder: SurfaceHolder) {
        hasSurface = false
    }

    private fun onRestartScan() {
        val surfaceView = findViewById<SurfaceView>(R.id.qrcode_view)
        val surfaceHolder = surfaceView.holder
        if (hasSurface) {
            initCamera(surfaceHolder)
        } else {
            surfaceHolder.addCallback(this)
            surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS)
        }
        decodeFormats = null
        characterSet = null
    }
}

需要继承和引入CaptureActivity(), SurfaceHolder.Callback,摄像头初始化已经在抽象类CaptureActivity中调用,只需要在自己的activity上判断摄像头权限即可,在onResume中进行摄像头画面的显示,CaptureActivity中获取到扫描结果后会自动暂停扫描,此时需要在scanResult中进行处理是否继续扫描,onRestartScan()方法可以使暂停的扫描继续。非常简单。

最后还需要在主项目的gradle中引入implementation 'com.google.zxing:core:3.3.3'

项目git库:私有库