Android 集成zxing二维码扫描、自定义

项目主要有zxing的基本使用,包含扫描回调、连续扫描、自定义扫描框:

一、依赖库

implementation 'com.journeyapps:zxing-android-embedded:4.3.0'

Github 这个库是zxing Android端的,封装了一些基本的使用方法

二、基本使用

这里使用的是startActivityForResult的替代方法,registerForActivityResult(),不了解的可以看下这篇Android 带回调的启动Activity 推荐使用registerForActivityResult

  1. 创建一个ActivityResultLauncher对象,用于配置启动界面和回调结果
    barcodeLauncher = registerForActivityResult(ScanContract()) { result ->
                //获取回调结果
                if (result.contents == null) {
                    Toast.makeText(this, "取消", Toast.LENGTH_LONG).show()
                } else {
                    Toast.makeText(
                        this,
                        "扫描结果: " + result.contents + ",${result.barcodeImagePath}",
                        Toast.LENGTH_LONG
                    ).show()
    
                    if(result.barcodeImagePath.isNotEmpty()){
                        ivImage.setImageURI(Uri.parse(result.barcodeImagePath))
                    }
                }
            }

  2. 配置扫描的基本参数,启动扫码界面

       //配置扫描时的基本参数
            val options = ScanOptions()
            options.apply {
                setDesiredBarcodeFormats(ScanOptions.QR_CODE)//图形码的格式:商品码、一维码、二维码、数据矩阵、全部类型
                setPrompt("请将条形码置于取景框内扫描")
                setCameraId(0) //0 后置摄像头  1 前置摄像头
                setBeepEnabled(true)//开启成功声音
                setTimeout(5000)//设置超时时间
                setBarcodeImageEnabled(true)//是否保存图片,扫描成功会截取扫描框的图形保存到手机并在result中返回路径
            }
            //启动扫描二维码界面
            barcodeLauncher.launch(options)

三、连续扫描

创建一个可连续扫描二维码的ContinuousCaptureActivity,布局文件有一个DecoratedBarcodeView(二维码扫描的主要控件)和一个TextView显示结果。

DecoratedBarcodeView : 主要由三个view组成 BarcodeView(相机扫描时的预览控件)、ViewfinderView(扫码框、遮罩和底部提示文字,后面自定义扫描框就是对其自定义)、TextView(提示文本)

  1. 布局        
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">
        <com.journeyapps.barcodescanner.DecoratedBarcodeView
            android:id="@+id/barcode_scanner"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1">
    
        </com.journeyapps.barcodescanner.DecoratedBarcodeView>
    
        <TextView
            android:id="@+id/tv_result"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    
    </LinearLayout>

  2. ContinuousCaptureActivity中的主要代码

     override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_continuous_capture)
            tvResult = findViewById(R.id.tv_result)
            barcodeView = findViewById(R.id.barcode_scanner)
            //码格式
            val formats: Collection<BarcodeFormat> =
                listOf(BarcodeFormat.QR_CODE, BarcodeFormat.CODE_39)
            barcodeView.barcodeView.decoderFactory = DefaultDecoderFactory(formats)
            //初始化barcodeView
            barcodeView.initializeFromIntent(intent)
            //监听扫描结果
            barcodeView.decodeContinuous(callBack)
        }
    
        private val callBack: BarcodeCallback = object : BarcodeCallback {
            @SuppressLint("SetTextI18n")
            override fun barcodeResult(result: BarcodeResult?) {
                result?.let {
                    //扫描结果为空或者两次扫描的结果相同
                    if (it.text.isNullOrEmpty() || it.text == lastText) {
                        return
                    }
                    lastText = it.text
                    tvResult.text = "${tvResult.text}\n$it"
                    Log.e("yufs",lastText)
                }
            }
        }

四、自定义扫描框

        上面有提到DecoratedBarcodeView,主要的三个view:BarcodeView、ViewfinderView、TextView。这里对包括扫描框的ViewfinderView进行自定义。ViewfinderView的默认效果从演示效果图中可以看到是中间的一根红色线,渐隐渐现,扫描时在还能出现些许黄色的小圆点。

        这里我们要做的是去掉中间红色的线,换成我们绘制的扫描线,并在四个角绘制边角。这里我们的自定义view只需要继承原先的ViewfinderView,并重写onDraw(),去掉不需要的,绘制我们的需要的即可。

下面为CustomViewfinderView的全部代码

/**
 * 自定义扫描框样式
 */
public class CustomViewfinderView extends ViewfinderView {

    //重绘时间间隔
    public static final long INT_ANIMATION_DELAY = 12;

    /* ******************************************    边角线相关属性    ************************************************/
    //"边角线长度/扫描边框长度"的占比 (比例越大,线越长)
    public float mLineRate = 0.1F;
    //边角线厚度 (建议使用dp)
    public float mLineDepth =  dp2px(4);
    //边角线颜色
    public int mLineColor;

    /* *******************************************    扫描线相关属性    ************************************************/
    //扫描线起始位置
    public int mScanLinePosition = 0;
    //扫描线厚度
    public float mScanLineDepth = dp2px(4);
    //扫描线每次移动距离
    public float mScanLineDy = dp2px(3);
    //渐变线
    public LinearGradient mLinearGradient;
    //图形paint
    public Paint mBitmapPaint;
    ///颜色在渐变中所占比例,此处均衡渐变
    public float[] mPositions = new float[]{0f, 0.5f, 1f};
    //线性梯度各个位置对应的颜色值
    public int[] mScanLineColor = new int[]{0x00000000, Color.YELLOW, 0x00000000};

    //扫描框宽、高
    public float mScanFrameWidth;
    public float mScanFrameHeight;

    public CustomViewfinderView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomViewfinderView);
        mLineColor = typedArray.getColor(R.styleable.CustomViewfinderView_lineColor,Color.YELLOW);
        mScanLineColor[1]=typedArray.getColor(R.styleable.CustomViewfinderView_cornerColor,Color.YELLOW);
        mScanFrameWidth = typedArray.getDimension(R.styleable.CustomViewfinderView_scanFrameWidth,dp2px(160));
        mScanFrameHeight = typedArray.getDimension(R.styleable.CustomViewfinderView_scanFrameHeight,dp2px(160));
        typedArray.recycle();
        mBitmapPaint = new Paint();
        mBitmapPaint.setAntiAlias(true);
    }

    @SuppressLint({ "DrawAllocation"})
    @Override
    public void onDraw(Canvas canvas) {
        refreshSizes();
        if (framingRect == null||previewSize==null ) {
            return;
        }

        final Rect frame = framingRect;

        final int width = getWidth();
        final int height = getHeight();

        //绘制扫描框外部遮罩
        paint.setColor(resultBitmap != null ? resultColor : maskColor);
        canvas.drawRect(0, 0, width, frame.top, paint);
        canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);
        canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, paint);
        canvas.drawRect(0, frame.bottom + 1, width, height, paint);

        //绘制4个角
        paint.setColor(mLineColor);
        canvas.drawRect(frame.left, frame.top, frame.left + frame.width() * mLineRate, frame.top + mLineDepth, paint);
        canvas.drawRect(frame.left, frame.top, frame.left + mLineDepth, frame.top + frame.height() * mLineRate, paint);

        canvas.drawRect(frame.right - frame.width() * mLineRate, frame.top, frame.right, frame.top + mLineDepth, paint);
        canvas.drawRect(frame.right - mLineDepth, frame.top, frame.right, frame.top + frame.height() * mLineRate, paint);

        canvas.drawRect(frame.left, frame.bottom - mLineDepth, frame.left + frame.width() * mLineRate, frame.bottom, paint);
        canvas.drawRect(frame.left, frame.bottom - frame.height() * mLineRate, frame.left + mLineDepth, frame.bottom, paint);

        canvas.drawRect(frame.right - frame.width() * mLineRate, frame.bottom - mLineDepth, frame.right, frame.bottom, paint);
        canvas.drawRect(frame.right - mLineDepth, frame.bottom - frame.height() * mLineRate, frame.right, frame.bottom, paint);


        if (resultBitmap != null) {
            // Draw the opaque result bitmap over the scanning rectangle
            paint.setAlpha(CURRENT_POINT_OPACITY);
            canvas.drawBitmap(resultBitmap, null, frame, paint);
        } else {
            // 绘制渐变扫描线
            mScanLinePosition += mScanLineDy;
            if (mScanLinePosition >= frame.height()) {
                mScanLinePosition = 0;
            }
            mLinearGradient = new LinearGradient(frame.left, frame.top + mScanLinePosition, frame.right, frame.top + mScanLinePosition, mScanLineColor, mPositions, Shader.TileMode.CLAMP);
            paint.setShader(mLinearGradient);
            canvas.drawRect(frame.left, frame.top + mScanLinePosition, frame.right, frame.top + mScanLinePosition + mScanLineDepth, paint);
            paint.setShader(null);

            //绘制资源图片扫描线
//            Rect lineRect = new Rect();
//            lineRect.left = frame.left;
//            lineRect.top = frame.top + mScanLinePosition;
//            lineRect.right = frame.right;
//            lineRect.bottom = frame.top + dp2px(6) + mScanLinePosition;
//            Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.img_line);
//            canvas.drawBitmap(bitmap, null, lineRect, mBitmapPaint);


            //============绘制扫描时小圆点,效果为默认=======================

            final float scaleX = this.getWidth() / (float) previewSize.width;
            final float scaleY = this.getHeight() / (float) previewSize.height;
            // draw the last possible result points
            if (!lastPossibleResultPoints.isEmpty()) {
                paint.setAlpha(CURRENT_POINT_OPACITY / 2);
                paint.setColor(resultPointColor);
                float radius = POINT_SIZE / 2.0f;
                for (final ResultPoint point : lastPossibleResultPoints) {
                    canvas.drawCircle(
                            (int) (point.getX() * scaleX),
                            (int) (point.getY() * scaleY),
                            radius, paint
                    );
                }
                lastPossibleResultPoints.clear();
            }

            // draw current possible result points
            if (!possibleResultPoints.isEmpty()) {
                paint.setAlpha(CURRENT_POINT_OPACITY);
                paint.setColor(resultPointColor);
                for (final ResultPoint point : possibleResultPoints) {
                    canvas.drawCircle(
                            (int) (point.getX() * scaleX),
                            (int) (point.getY() * scaleY),
                            POINT_SIZE, paint
                    );
                }

                // swap and clear buffers
                final List<ResultPoint> temp = possibleResultPoints;
                possibleResultPoints = lastPossibleResultPoints;
                lastPossibleResultPoints = temp;
                possibleResultPoints.clear();
            }

            //============绘制扫描时小圆点,效果为默认 end=======================
        }

        //定时刷新扫描框
        postInvalidateDelayed(INT_ANIMATION_DELAY,
                frame.left - POINT_SIZE,
                frame.top - POINT_SIZE,
                frame.right + POINT_SIZE,
                frame.bottom + POINT_SIZE);

    }

    protected void refreshSizes() {
        if (cameraPreview == null) {
            return;
        }
        //添加设置边框大小代码
        cameraPreview.setFramingRectSize(new Size((int) mScanFrameWidth,(int)mScanFrameHeight));

        Rect framingRect = cameraPreview.getFramingRect();
        Size previewSize = cameraPreview.getPreviewSize();
        if (framingRect != null && previewSize != null) {
            this.framingRect = framingRect;
            this.previewSize = previewSize;
        }
    }

    private int dp2px(int dp) {
        float density = getContext().getResources().getDisplayMetrics().density;
        return (int) (dp * density + 0.5f);
    }
}

代码中主要看onDraw(),里面的关键代码已添加注释,同时里面的扫描线可以替换成我们需要的图形,有需要打开onDraw中注释的代码即可

自定义的属性文件attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CustomViewfinderView">
        <attr format="color" name="lineColor"/><!--扫描线的颜色-->
        <attr format="color" name="cornerColor"/><!--四边角的颜色-->
        <attr format="dimension" name="scanFrameWidth"/><!--扫描框的宽度-->
        <attr format="dimension" name="scanFrameHeight"/><!--扫描框的高度-->
    </declare-styleable>
</resources>

如何使用?

  1. 创建custom_barcode_scanner.xml
    <?xml version="1.0" encoding="utf-8"?>
    <merge xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">
    
        <com.journeyapps.barcodescanner.BarcodeView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/zxing_barcode_surface"/>
    
        <com.home.testzxing.CustomViewfinderView
            android:layout_width="match_parent"
            app:lineColor="#ff8100"
            app:cornerColor="#ff8100"
            app:scanFrameWidth="180dp"
            app:scanFrameHeight="180dp"
            android:layout_height="match_parent"
            android:id="@+id/zxing_viewfinder_view"/>
    
        <TextView android:id="@+id/zxing_status_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|center_horizontal"
            android:background="@color/zxing_transparent"
            android:text="@string/zxing_msg_default_status"
            android:textColor="@color/zxing_status_text"/>
    </merge>

    里面就包含我们刚才提到的三个View,ViewfinderView替换成我们的CustomViewfinderView,另外这三个View的id不可改变

  2. 在DecoratedBarcodeView的布局文件中通过zxing_scanner_layout属性引入我们创建的xml

       <com.journeyapps.barcodescanner.DecoratedBarcodeView
            android:id="@+id/zxing_barcode_scanner"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:zxing_scanner_layout="@layout/custom_barcode_scanner">
    
        </com.journeyapps.barcodescanner.DecoratedBarcodeView>

五、总结

        主要了解registerForActivityResult()的启动activity、自定义view中LinearGradient绘制渐变的扫描线以及postInvalidateDelayed定时重绘扫描框。

代码下载

参考

Android进阶 - 二维码扫描 - 简书

Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果_Mobile Internet developer-CSDN博客

  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Android 是一种操作系统,而 Google 的 ZXing 是一个开源的二维码扫描库。通过使用 ZXing 库,我们可以轻松地在 Android 应用程序中实现二维码扫描功能。 要在 Android 应用中使用 ZXing,首先需要在项目的 build.gradle 文件中添加以下依赖: ``` implementation 'com.google.zxing:core:3.3.3' implementation 'com.journeyapps:zxing-android-embedded:4.0.0' ``` 接下来,在布局文件中添加一个 SurfaceView 控件,用于显示相机预览画面。 然后,在 Activity 或 Fragment 中添加以下代码: ``` private IntentIntegrator integrator; @Override protected void onCreate(Bundle savedInstanceState) { // ... integrator = new IntentIntegrator(this); integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE); integrator.setPrompt("请将二维码放入扫描框中"); integrator.setCameraId(0); // 后置摄像头 integrator.setBeepEnabled(false); // 关闭扫描提示音 integrator.setBarcodeImageEnabled(false); // 保存扫描的图片 integrator.initiateScan(); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data); if (result != null) { if (result.getContents() == null) { // 用户取消了扫描 } else { String scanResult = result.getContents(); // 在这里处理扫描得到的二维码数据 } } } ``` 在上述代码中,通过 `IntentIntegrator` 类来发起扫描,并在 `onActivityResult` 方法中处理扫描结果。 以上是使用 ZXing 库实现 Android 中的二维码扫描的简要介绍。使用 ZXing 库可以方便地实现二维码扫描功能,并且还提供了许多其他定制选项和扩展功能,可以根据需要进行使用和调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值