Android 实现扫描二维码

作者: 晓峰残月 | 来源:发表于2016-03-10 16:45 被阅读2242次

    1、如何使用

    1.新建一个布局

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:showIn="@layout/activity_capture">
    
        <Button
            android:id="@+id/capture_bt"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="扫描二维码"
            />
    
        <TextView
            android:id="@+id/capture_tv"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/capture_bt"
            />
    
        <ImageView
            android:id="@+id/capture_iv"
            android:layout_width="fill_parent"
            android:layout_height="200dp"
            android:scaleType="centerInside"
            android:layout_below="@+id/capture_tv"
            />
    
    </RelativeLayout>
    

    布局很简单,一个跳转到扫描界面的Button,一个用于显示返回扫描结果的TextView和一个用于显示扫描截取图片的ImageView。

    2.Activity中的使用

    private void initView() {
            capture = (Button) this.findViewById(R.id.capture_bt);
            tv = (TextView) this.findViewById(R.id.capture_tv);
            iv = (ImageView) this.findViewById(R.id.capture_iv);
            capture.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Intent intent = new Intent();
                    intent.setClass(CaptureActivity.this, com.jwenfeng.fastdev.zxing.Activity.CaptureActivity.class);
                    intent.putExtra("path", Environment.getExternalStorageDirectory().getPath()+"/QRcode/");
                    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                    startActivityForResult(intent, SCANNIN_GREQUEST_CODE);
                }
            });
        }
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            switch (requestCode) {
                case SCANNIN_GREQUEST_CODE:
                    if(resultCode == RESULT_OK){
                        Bundle bundle = data.getExtras();
                        //显示扫描到的内容
                        tv.setText(bundle.getString("result"));
                        iv.setImageBitmap(BitmapFactory.decodeFile(bundle.getString("filepath")));
    
                    }
                    break;
            }
        }
    

    Activity 中就是跳转到扫描的界面,在onActivityResult处理返回的数据

    2、工作原理

    在扫描界面这个Activity主要处理扫描界面的类,比如,扫描成功有声音和振动等等,主要关注里面的handleDecode(Result result, Bitmap barcode)方法,扫描完成之后将扫描到的结果和二维码的bitmap当初参数传递到handleDecode(Result result, Bitmap barcode)里面,我们只需要在里面写出相对应的处理代码即可

    /**
         * 处理扫描结果
         *
         * @param result
         * @param barcode
         */
        public void handleDecode(Result result, Bitmap barcode) {
            inactivityTimer.onActivity();
            playBeepSoundAndVibrate();
            String resultString = result.getText();
            if (resultString.equals("")) {
                Toast.makeText(CaptureActivity.this, "Scan failed!", Toast.LENGTH_SHORT).show();
            } else {
                Intent resultIntent = new Intent();
                Bundle bundle = new Bundle();
                bundle.putString("result", resultString);
                if (path == null || path.equals("")){
    
                }else{
                    if (!new File(path).exists()){
                        new File(path).mkdir();
                    }
                    String fileName = System.currentTimeMillis()+".jpg";
                    saveQRcodeToSDcard(path+fileName,barcode);
                    bundle.putString("filepath", path + fileName);
                }
                //bundle.putParcelable("bitmap", barcode);
                resultIntent.putExtras(bundle);
                this.setResult(RESULT_OK, resultIntent);
            }
            CaptureActivity.this.finish();
        }
    

    参考网上http://blog.csdn.net/xiaanming/article/details/10163203
    这篇文章修改了一下扫码的界面,代码注释很全。

    package com.jwenfeng.fastdev.zxing.view;/*
     * Copyright (C) 2008 ZXing authors
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    
    import java.util.Collection;
    import java.util.HashSet;
    
    import android.content.Context;
    import android.content.res.Resources;
    import android.graphics.Bitmap;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Rect;
    import android.graphics.Typeface;
    import android.util.AttributeSet;
    import android.view.View;
    
    import com.google.zxing.ResultPoint;
    import com.jwenfeng.fastdev.R;
    import com.jwenfeng.fastdev.zxing.camera.CameraManager;
    
    /**
     * This view is overlaid on top of the camera preview. It adds the viewfinder
     * rectangle and partial transparency outside it, as well as the laser scanner
     * animation and result points.
     *
     */
    public final class ViewfinderView extends View {
        private static final String TAG = "log";
        /**
         * 刷新界面的时间
         */
        private static final long ANIMATION_DELAY = 10L;
        private static final int OPAQUE = 0xFF;
    
        /**
         * 四个绿色边角对应的长度
         */
        private int ScreenRate;
    
        /**
         * 四个绿色边角对应的宽度
         */
        private static final int CORNER_WIDTH = 10;
        /**
         * 扫描框中的中间线的宽度
         */
        private static final int MIDDLE_LINE_WIDTH = 6;
    
        /**
         * 扫描框中的中间线的与扫描框左右的间隙
         */
        private static final int MIDDLE_LINE_PADDING = 5;
    
        /**
         * 中间那条线每次刷新移动的距离
         */
        private static final int SPEEN_DISTANCE = 5;
    
        /**
         * 手机的屏幕密度
         */
        private static float density;
        /**
         * 字体大小
         */
        private static final int TEXT_SIZE = 16;
        /**
         * 字体距离扫描框下面的距离
         */
        private static final int TEXT_PADDING_TOP = 30;
    
        /**
         * 画笔对象的引用
         */
        private Paint paint;
    
        /**
         * 中间滑动线的最顶端位置
         */
        private int slideTop;
    
        /**
         * 中间滑动线的最底端位置
         */
        private int slideBottom;
    
        /**
         * 将扫描的二维码拍下来,这里没有这个功能,暂时不考虑
         */
        private Bitmap resultBitmap;
        private final int maskColor;
        private final int resultColor;
    
        private final int resultPointColor;
        private Collection<ResultPoint> possibleResultPoints;
        private Collection<ResultPoint> lastPossibleResultPoints;
    
        boolean isFirst;
    
        public ViewfinderView(Context context, AttributeSet attrs) {
            super(context, attrs);
    
            density = context.getResources().getDisplayMetrics().density;
            //将像素转换成dp
            ScreenRate = (int)(20 * density);
    
            paint = new Paint();
            Resources resources = getResources();
            maskColor = resources.getColor(R.color.viewfinder_mask);
            resultColor = resources.getColor(R.color.result_view);
    
            resultPointColor = resources.getColor(R.color.possible_result_points);
            possibleResultPoints = new HashSet<ResultPoint>(5);
        }
    
        @Override
        public void onDraw(Canvas canvas) {
            //中间的扫描框,你要修改扫描框的大小,去CameraManager里面修改
            Rect frame = CameraManager.get().getFramingRect();
            if (frame == null) {
                return;
            }
    
            //初始化中间线滑动的最上边和最下边
            if(!isFirst){
                isFirst = true;
                slideTop = frame.top;
                slideBottom = frame.bottom;
            }
    
            //获取屏幕的宽和高
            int width = canvas.getWidth();
            int height = canvas.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);
    
    
    
            if (resultBitmap != null) {
                // Draw the opaque result bitmap over the scanning rectangle
                paint.setAlpha(OPAQUE);
                canvas.drawBitmap(resultBitmap, frame.left, frame.top, paint);
            } else {
    
                //画扫描框边上的角,总共8个部分
                paint.setColor(Color.GREEN);
                canvas.drawRect(frame.left, frame.top, frame.left + ScreenRate,
                        frame.top + CORNER_WIDTH, paint);
                canvas.drawRect(frame.left, frame.top, frame.left + CORNER_WIDTH, frame.top
                        + ScreenRate, paint);
                canvas.drawRect(frame.right - ScreenRate, frame.top, frame.right,
                        frame.top + CORNER_WIDTH, paint);
                canvas.drawRect(frame.right - CORNER_WIDTH, frame.top, frame.right, frame.top
                        + ScreenRate, paint);
                canvas.drawRect(frame.left, frame.bottom - CORNER_WIDTH, frame.left
                        + ScreenRate, frame.bottom, paint);
                canvas.drawRect(frame.left, frame.bottom - ScreenRate,
                        frame.left + CORNER_WIDTH, frame.bottom, paint);
                canvas.drawRect(frame.right - ScreenRate, frame.bottom - CORNER_WIDTH,
                        frame.right, frame.bottom, paint);
                canvas.drawRect(frame.right - CORNER_WIDTH, frame.bottom - ScreenRate,
                        frame.right, frame.bottom, paint);
    
    
                //绘制中间的线,每次刷新界面,中间的线往下移动SPEEN_DISTANCE
                slideTop += SPEEN_DISTANCE;
                if(slideTop >= frame.bottom){
                    slideTop = frame.top;
                }
                canvas.drawRect(frame.left + MIDDLE_LINE_PADDING, slideTop - MIDDLE_LINE_WIDTH/2, frame.right - MIDDLE_LINE_PADDING,slideTop + MIDDLE_LINE_WIDTH/2, paint);
    
    
                //画扫描框下面的字
                paint.setColor(Color.WHITE);
                paint.setTextSize(TEXT_SIZE * density);
                paint.setAlpha(0x40);
                paint.setTypeface(Typeface.create("System", Typeface.BOLD));
                canvas.drawText(getResources().getString(R.string.scan_text), frame.left, (float) (frame.bottom + (float)TEXT_PADDING_TOP *density), paint);
    
    
    
                Collection<ResultPoint> currentPossible = possibleResultPoints;
                Collection<ResultPoint> currentLast = lastPossibleResultPoints;
                if (currentPossible.isEmpty()) {
                    lastPossibleResultPoints = null;
                } else {
                    possibleResultPoints = new HashSet<ResultPoint>(5);
                    lastPossibleResultPoints = currentPossible;
                    paint.setAlpha(OPAQUE);
                    paint.setColor(resultPointColor);
                    for (ResultPoint point : currentPossible) {
                        canvas.drawCircle(frame.left + point.getX(), frame.top
                                + point.getY(), 6.0f, paint);
                    }
                }
                if (currentLast != null) {
                    paint.setAlpha(OPAQUE / 2);
                    paint.setColor(resultPointColor);
                    for (ResultPoint point : currentLast) {
                        canvas.drawCircle(frame.left + point.getX(), frame.top
                                + point.getY(), 3.0f, paint);
                    }
                }
    
    
                //只刷新扫描框的内容,其他地方不刷新
                postInvalidateDelayed(ANIMATION_DELAY, frame.left, frame.top,
                        frame.right, frame.bottom);
    
            }
        }
    
        public void drawViewfinder() {
            resultBitmap = null;
            invalidate();
        }
    
        /**
         * Draw a bitmap with the result points highlighted instead of the live
         * scanning display.
         *
         * @param barcode
         *            An image of the decoded barcode.
         */
        public void drawResultBitmap(Bitmap barcode) {
            resultBitmap = barcode;
            invalidate();
        }
    
        public void addPossibleResultPoint(ResultPoint point) {
            possibleResultPoints.add(point);
        }
    
    }
    

    在 CaptureActivity 中创建CaptureActivityHandler

    handler = new CaptureActivityHandler(this, decodeFormats, characterSet);
    

    在CaptureActivityHandler的初始化启动扫描解码的线程,在 handler 中对handleDecode回调传递参数

    public CaptureActivityHandler(CaptureActivity activity, Vector<BarcodeFormat> decodeFormats,
                                      String characterSet) {
            this.activity = activity;
            decodeThread = new DecodeThread(activity, decodeFormats, characterSet,
                    new ViewfinderResultPointCallback(activity.getViewfinderView()));
            decodeThread.start();
            state = State.SUCCESS;
            // Start ourselves capturing previews and decoding.
            CameraManager.get().startPreview();
            restartPreviewAndDecode();
        }
    
        @Override
        public void handleMessage(Message message) {
            if (message.what == R.id.auto_focus) {//Log.d(TAG, "Got auto-focus message");
                // When one auto focus pass finishes, start another. This is the closest thing to
                // continuous AF. It does seem to hunt a bit, but I'm not sure what else to do.
                if (state == State.PREVIEW) {
                    CameraManager.get().requestAutoFocus(this, R.id.auto_focus);
                }
    
            } else if (message.what == R.id.restart_preview) {
                Log.d(TAG, "Got restart preview message");
                restartPreviewAndDecode();
    
            } else if (message.what == R.id.decode_succeeded) {
                Log.d(TAG, "Got decode succeeded message");
                state = State.SUCCESS;
                Bundle bundle = message.getData();
    
                /***********************************************************************/
                Bitmap barcode = bundle == null ? null :
                        (Bitmap) bundle.getParcelable(DecodeThread.BARCODE_BITMAP);
    
                activity.handleDecode((Result) message.obj, barcode);
    
            } else if (message.what == R.id.decode_failed) {// We're decoding as fast as possible, so when one decode fails, start another.
                state = State.PREVIEW;
                CameraManager.get().requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
    
            } else if (message.what == R.id.return_scan_result) {
                Log.d(TAG, "Got return scan result message");
                activity.setResult(Activity.RESULT_OK, (Intent) message.obj);
                activity.finish();
    
            } else if (message.what == R.id.launch_product_query) {
                Log.d(TAG, "Got product query message");
                String url = (String) message.obj;
                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
                activity.startActivity(intent);
    
            }
        }
    

    最后附上demo的效果图

    S60310-163413.jpg S60310-163501.jpg S60310-163507.jpg

    最后别忘了导入 core.jar 的jar包

    demo 提取密码: vedd

    转载请注明转载地址:http://www.jianshu.com/p/5ee933cf1ed9
    Android 实现扫描二维码
    如有什么不对或者不规范的地方请留言或私信我,非常感谢

    相关文章

      网友评论

      本文标题:Android 实现扫描二维码

      本文链接:https://www.haomeiwen.com/subject/tmezkttx.html