安卓扫描二维码功能:zxing

需求:实现扫描二维码验证身份。(服务端这部分会提供一个二维码,扫描二维码会生成一个token,根据token的结果向某个url发送网络连接请求,服务端返回验证结果。我只负责安卓扫码和网络连接请求部分)。

基本扫码功能

1.引入google开源库:

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

网上大部分使用的是zxing这个开源库,资料比较多,所以我也用了。。哈哈

2.创建菜单栏,里面增加item为扫描二维码。然后创建IntentIntegrator对象,initiateScan开始扫描。其中IntentIntegrator对象有一些set函数,这里用到的一个是CapacityActivity,默认不set的话是用默认的捕获活动来扫描,这里是自己自定义了一个扫码样式所以显式指定了一下。setPrompt是扫描框下面的一行字。

@Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if(item.getItemId()==R.id.qr_code){
            IntentIntegrator intentIntegrator = new IntentIntegrator(NewMainActivity.this);
            intentIntegrator.setCaptureActivity(CustomCaptureActivity.class);
            intentIntegrator.setPrompt("将二维码放入框内,即可自动扫描");
            intentIntegrator.initiateScan();

        }

3.重写onActivityResult方法接收扫描结果。

  @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        IntentResult result = IntentIntegrator.parseActivityResult(requestCode,resultCode,data);
        if(result==null) {
            super.onActivityResult(requestCode, resultCode, data);
        }
        if (result.getContents() == null) {
            Toast.makeText(this, "取消扫描", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(this, result.getContents(), Toast.LENGTH_SHORT).show();
        }
   }

这几部完成之后,基本的扫码功能就完成了。但是扫码的时候你会发现扫描过程屏幕会横过来,因为其默认扫描界面是横屏,可以在manifest中指定。设置为竖屏。

 <activity
            android:name=".qrcode.CustomCaptureActivity"    
            android:theme="@style/AppTheme"
            android:screenOrientation="portrait"
            tools:replace="screenOrientation" />

基本功能完成了,但是界面比较丑。还可以自定义扫码界面的样式。

自定义扫码界面

1.源码中可以看到CapacityActivity这个活动,之前做扫码功能也主要由这个活动完成,可以直接copy这个活动,创建自己的扫码活动,CustomCapacityActivity,在manifest中进行活动的声明。然后就可以用setCapacityActivity来指定扫码活动了。完美替换。如上。如果能扫码成功,表示替换成功。

2.修改扫码布局

新创建的扫码活动使用的还是源码的布局,我们也不能修改,所以copy一份出来(zxing_capacity),进行修改(activity_zxing_layout)。然后修改customCapacityActivity的布局。

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_zxing_layout);
    }

3.自定义样式

都copy出来了就可以自定义样式了。研究DecoratedBarcodeView会发现使用的布局是zxing_barcode_scanner。同样把这个布局copy出来。custom_barcode_scanner.xml

<?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">
    <com.journeyapps.barcodescanner.BarcodeView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/zxing_barcode_surface"/>

    <com.example.qrcode.CustomViewfinderView
        android:layout_width="match_parent"
        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="center"
        android:layout_marginTop="160dp"
        android:background="@color/zxing_transparent"
        android:text="@string/zxing_msg_default_status"
        android:textColor="@color/zxing_status_text"/>

</FrameLayout>

让DecoratedBarcodeView加载新定义布局,而不是默认的布局。

<?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">

    <com.journeyapps.barcodescanner.DecoratedBarcodeView
        android:id="@+id/zxing_barcode_scanner"

        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:zxing_framing_rect_width="250dp"
        app:zxing_framing_rect_height="250dp"
        app:zxing_preview_scaling_strategy="centerCrop"
        app:zxing_scanner_layout="@layout/custom_barcode_scanner"
        app:zxing_use_texture_view="true" />

</LinearLayout>

开始自定义扫描视图,继承ViewfinderView重写onDraw方法。

public class CustomViewfinderView extends ViewfinderView {
    /**
     * 重绘时间间隔
     */
    public static final long CUSTOME_ANIMATION_DELAY = 16;

    /* ******************************************    边角线相关属性    ************************************************/

    /**
     * "边角线长度/扫描边框长度"的占比 (比例越大,线越长)
     */
    public float mLineRate = 0.1F;

    /**
     * 边角线厚度 (建议使用dp)
     */
    public float mLineDepth =  TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getResources().getDisplayMetrics());

    /**
     * 边角线颜色
     */
    public int mLineColor = Color.RED;

    /* *******************************************    扫描线相关属性    ************************************************/

    /**
     * 扫描线起始位置
     */
    public int mScanLinePosition = 0;

    /**
     * 扫描线厚度
     */
    public float mScanLineDepth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getResources().getDisplayMetrics());

    /**
     * 扫描线每次重绘的移动距离
     */
    public float mScanLineDy = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getResources().getDisplayMetrics());

    /**
     * 线性梯度
     */
    public LinearGradient mLinearGradient;

    /**
     * 线性梯度位置
     */
    public float[] mPositions = new float[]{0f, 0.5f, 1f};

    /**
     * 线性梯度各个位置对应的颜色值
     */
    public int[] mScanLineColor = new int[]{0x00FFFFFF, Color.RED, 0x00FFFFFF};


    public CustomViewfinderView(Context context, AttributeSet attrs) {
        super(context, attrs);

    }

   @Override
    protected void refreshSizes() {
        if(cameraPreview == null) {
            return;
        }
        Rect framingRect = cameraPreview.getFramingRect();
        Rect previewFramingRect = cameraPreview.getPreviewFramingRect();
        if(framingRect != null && previewFramingRect != null) {
            this.framingRect = framingRect;
            this.previewFramingRect = previewFramingRect;
        }
    }

    @Override
    public void onDraw(Canvas canvas) {
        refreshSizes();
        if (framingRect == null || previewFramingRect == null) {
            return;
        }

        Rect frame = framingRect;
        Rect previewFrame = previewFramingRect;

        int width = canvas.getWidth();
        int height = canvas.getHeight();

        //绘制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);

        // Draw the exterior (i.e. outside the framing rect) darkened
        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);

        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);

            float scaleX = frame.width() / (float) previewFrame.width();
            float scaleY = frame.height() / (float) previewFrame.height();

            List<ResultPoint> currentPossible = possibleResultPoints;
            List<ResultPoint> currentLast = lastPossibleResultPoints;
            int frameLeft = frame.left;
            int frameTop = frame.top;
            if (currentPossible.isEmpty()) {
                lastPossibleResultPoints = null;
            } else {
                possibleResultPoints = new ArrayList<>(5);
                lastPossibleResultPoints = currentPossible;
                paint.setAlpha(CURRENT_POINT_OPACITY);
                paint.setColor(resultPointColor);
                for (ResultPoint point : currentPossible) {
                    canvas.drawCircle(frameLeft + (int) (point.getX() * scaleX),
                            frameTop + (int) (point.getY() * scaleY),
                            POINT_SIZE, paint);
                }
            }
            if (currentLast != null) {
                paint.setAlpha(CURRENT_POINT_OPACITY / 2);
                paint.setColor(resultPointColor);
                float radius = POINT_SIZE / 2.0f;
                for (ResultPoint point : currentLast) {
                    canvas.drawCircle(frameLeft + (int) (point.getX() * scaleX),
                            frameTop + (int) (point.getY() * scaleY),
                            radius, paint);
                }
            }
        }

        // Request another update at the animation interval, but only repaint the laser line,
        // not the entire viewfinder mask.
        postInvalidateDelayed(CUSTOME_ANIMATION_DELAY,
                frame.left,
                frame.top,
                frame.right,
                frame.bottom);
    }
}

更加详细的内容参考这里:https://www.jianshu.com/p/b85812b6f7c1

发布了25 篇原创文章 · 获赞 1 · 访问量 7526

猜你喜欢

转载自blog.csdn.net/qq_28334237/article/details/99635761