β

安卓-基于ZXing的二维码扫描解码Demo

国祥のBlog 2241 阅读

最近在做毕设。需要有一个二维码扫描获取信息的模块。于是谷歌百度之,发现了开源库ZXing,于是果断研究起来。历时3天弄出了一个可直接应用的Demo。

扫描的整体思路是,启用相机获取有效的预览帧处理后交给ZXing解码。解码成功就会返回一个Result,可以用Result的方法获取需要的类型的数据。

运行本代码前,您需要下载ZXING JAR包并导入您的工程。下载地址:http://repo1.maven.org/maven2/com/google/zxing/core/
截至发布前,本工程使用的ZXING版本为core-3.1.0。

本Demo需要以下权限:

必须:<uses-permission android:name="android.permission.CAMERA"/>
可选:<uses-permission android:name="android.permission.FLASHLIGHT"/>

另附源码:

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.PreviewCallback;
import android.hardware.Camera.Size;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.KeyEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewGroup.LayoutParams;

import com.google.zxing.BinaryBitmap;
import com.google.zxing.ChecksumException;
import com.google.zxing.DecodeHintType;
import com.google.zxing.FormatException;
import com.google.zxing.NotFoundException;
import com.google.zxing.RGBLuminanceSource;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;

public class QRCodeGetterActivity extends Activity implements
        SurfaceHolder.Callback
{
    private SurfaceView sv;
    private SurfaceHolder sh;

    private int catchWidth;
    private int catchHeight;

    private Camera cam;
    private Camera.Parameters camParam;

    private boolean isSupportAutoFocus = true;
    private boolean isSupportFlash;

    private AutoFocusCallBack autoFocusCallback;
    private PreviewCallBack previewCallback;

    private boolean canDecode = false;

    private DeCodeFramePicture decode;

    Timer autoFocus;
    Timer handFocus;

    class AutoFocusCallBack implements AutoFocusCallback
    {
        @Override
        public void onAutoFocus(boolean success, Camera camera)
        {
            canDecode = true;
        }
    }

    class PreviewCallBack implements PreviewCallback
    {
        Rect rect = new Rect(0, 0, camParam.getPreviewSize().width,
                camParam.getPreviewSize().height);

        YuvImage img;
        ByteArrayOutputStream baos;
        Bitmap bm;

        @Override
        public void onPreviewFrame(byte[] data, Camera camera)
        {
            if (!canDecode)
            {
                return;
            }

            img = new YuvImage(data, ImageFormat.NV21,
                    camParam.getPreviewSize().width,
                    camParam.getPreviewSize().height, null);

            baos = new ByteArrayOutputStream();

            if (img.compressToJpeg(rect, 100, baos))
            {
                bm = BitmapFactory.decodeByteArray(baos.toByteArray(), 0,
                        baos.size());
            }

            Result result = decode.decode(bm);
            if (result != null)
            {
                if (isSupportAutoFocus)
                {
                    autoFocus.cancel();
                } else
                {
                    handFocus.cancel();
                }
                String addr = result.getText();
				
                Intent i = new Intent();
                i.putExtra("QRStringData", addr);
                setResult(Activity.RESULT_OK,i);
                finish();
            }

            cam.startPreview();
            canDecode = false;
        }
    }

    class AutoFocusTimer extends TimerTask
    {
        @Override
        public void run()
        {
            cam.autoFocus(autoFocusCallback);
        }
    }

    class NotSupportAutoFocusTimer extends TimerTask
    {
        @Override
        public void run()
        {
            canDecode = true;
        }
    }

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

        sv = (SurfaceView) findViewById(R.id.sv);
        sh = sv.getHolder();
        sh.addCallback(this);
        LayoutParams lp = sv.getLayoutParams();
        catchHeight = lp.height;
        catchWidth = lp.width;
        sh.setFixedSize(catchWidth, catchHeight);
        sh.setKeepScreenOn(true);
        decode = new DeCodeFramePicture();
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event)
    {
        super.onKeyUp(keyCode, event);
        if (keyCode == KeyEvent.KEYCODE_BACK)
        {
            setResult(Activity.RESULT_CANCELED);
            finish();
        }
        return false;
    }

    @Override
    protected void onPause()
    {
        super.onPause();
        if (isSupportAutoFocus)
        {
            autoFocus.cancel();
        }
		else
		{
			handFocus.cancel();
		}
        cam.stopPreview();
        cam.release();
    }

    @Override
    protected void onResume()
    {
        super.onResume();
        cam = Camera.open();
        camParam = cam.getParameters();

        DisplayMetrics metric = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metric);
        if (metric.widthPixels < metric.heightPixels)// 横竖屏问题
        {
            cam.setDisplayOrientation(90);
        } else
        {
            cam.setDisplayOrientation(0);
        }

        // 全屏预览,采用最高支持的Size宽高,绕开图像拉伸问题
        List<Size> szs = camParam.getSupportedPreviewSizes();
        Size sz = szs.get(szs.size() - 1);
        camParam.setPreviewSize(sz.width, sz.height);

        List<String> focusMode = camParam.getSupportedFocusModes();
        List<String> flashLightMode = camParam.getSupportedFlashModes();

        isSupportFlash = flashLightMode != null ? true : false;

        // 闪光灯方式优选
        if (isSupportFlash)
        {
            if (flashLightMode.contains(Camera.Parameters.FLASH_MODE_TORCH))
            {
                camParam.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
            } else if (flashLightMode
                    .contains(Camera.Parameters.FLASH_MODE_AUTO))
            {
                camParam.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
            } else if (flashLightMode.contains(Camera.Parameters.FLASH_MODE_ON))
            {
                camParam.setFlashMode(Camera.Parameters.FLASH_MODE_ON);
            }
        }

        // 对焦方式优选
        if (focusMode.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE))
        {
            camParam.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
        } else if (focusMode.contains(Camera.Parameters.FOCUS_MODE_AUTO))
        {
            camParam.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
        } else if (focusMode.contains(Camera.Parameters.FOCUS_MODE_MACRO))
        {
            camParam.setFocusMode(Camera.Parameters.FOCUS_MODE_MACRO);
        } else
        {
            isSupportAutoFocus = false;
        }
		
        List<int[]> fpsRate = camParam.getSupportedPreviewFpsRange();
        camParam.setPreviewFpsRange(fpsRate.get(0)[0], fpsRate.get(0)[1]);			//预览帧速率
        camParam.setPreviewFormat(ImageFormat.NV21);								//仅支持NV21

        cam.setParameters(camParam);

        previewCallback = new PreviewCallBack();
        cam.setPreviewCallback(previewCallback);

        if (isSupportAutoFocus)
        {
            autoFocusCallback = new AutoFocusCallBack();
            autoFocus = new Timer();
            autoFocus.schedule(new AutoFocusTimer(), 5000, 5000);		//自动对焦相机启动5秒后开始,每5秒对焦一次
        } else
        {
            handFocus = new Timer();
            handFocus.schedule(new NotSupportAutoFocusTimer(), 3000, 3000);		//手动对焦相机启动3秒后开始,每3秒解析一次
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height)
    {
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder)
    {
        try
        {
            cam.setPreviewDisplay(holder);
            cam.startPreview();

        } catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder)
    {
    }
}

class DeCodeFramePicture
{
    Hashtable<DecodeHintType, String> hints;
    Bitmap bm;
    BinaryBitmap bb;
	QRCodeReader reader;

    DeCodeFramePicture()
    {
        hints = new Hashtable<DecodeHintType, String>();
        hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
		reader = new QRCodeReader();
    }

    public Result decode(Bitmap bm)
    {
        int[] data = new int[bm.getHeight() * bm.getWidth()];
        bm.getPixels(data, 0, bm.getWidth(), 0, 0, bm.getWidth(),
                bm.getHeight());

        RGBLuminanceSource source = new RGBLuminanceSource(bm.getWidth(),
                bm.getHeight(), data);

        bb = new BinaryBitmap(new HybridBinarizer(source));
        try
        {
            return reader.decode(bb, hints);
        } catch (ChecksumException e)
        {
            e.printStackTrace();
        } catch (NotFoundException e)
        {
            e.printStackTrace();
        } catch (FormatException e)
        {
            e.printStackTrace();
        }
        return null;
    }
}
作者:国祥のBlog
原“国祥的博客”
原文地址:安卓-基于ZXing的二维码扫描解码Demo, 感谢原作者分享。