蛰伏半月有余,一直在准备期末考试,期间抽空研究了一些Android的源代码,现在我就把在这其中的一些收获分享给大家。
今天想分享给大家的是二维码扫描。说起二维码,大家一定不会陌生,尤其是微信火了以后,在我们的生活中几乎随处都可以看到二维码的影
子。相关科技媒体甚至把二维码当成是未来移动互联网的入口,因此研究二维码的相关技术就显得意义非凡。目前在移动开发领域,使用最为广泛的二
维码库有两个,分别是ZXing和ZBar,其中ZXing在Android开发中较为常见,而ZBar则在iOS开发中较为常见,更重要的一点是,这两个库都是开源
的,因此我们可以从源代码中获得很多有用的东西。关于ZXing,网上有很多相关的博文,我今天不想多说,我今天想说的是ZBar,你可能会说,ZBar
不是用在IOS中,怎么今天要说ZBar呢?其实我是从这两个库使用的难易程度来选择的,ZXing功能强大,但是使用起来比较繁琐,网上有很多简化的
教程,大家可以自行前去研究。相比较而言,ZBar则比较简单,使用起来容易上手,因此我们今天选择了ZBar作为我们的库来使用。
一、准备工作
下载ZBar的SDK:由于ZBar的项目托管在sourceforge,所以在这里给出下载地址:http://download.csdn.net/detail/qinyuanpei/6794713
二、导入项目
下载完成后,我们直接解压,可以看到下面的目录结构
打开android文件夹,我们可以找到一个Example的文件夹,这是官方给出的示例代码,我们下面的所有工作都是基于这个示例程序而来。我们
自行创建一个Android项目,并将这两个文件拷贝到我们的项目中,同时引入ZBar相关的库文件。
三、建立布局
首先建立主界面,即扫描二维码的界面,界面布局代码如下:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="40dp"
- android:background="@drawable/title_bg" >
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:layout_centerVertical="true"
- android:text="@string/Scan"
- android:textColor="#ffffff"
- android:textSize="18sp" />
- <Button
- android:id="@+id/BtnAbout"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_alignParentTop="true"
- android:text="@string/BtnAbout" />
- </RelativeLayout>
- <FrameLayout
- android:id="@+id/cameraPreview"
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="1"/>
- </LinearLayout>
实现的布局效果如下图所示:
接下里,我们在来设计一个用于显示结果的界面,界面布局代码如下:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#ffffff"
- android:orientation="vertical" >
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="40dp"
- android:background="@drawable/title_bg" >
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:layout_centerVertical="true"
- android:textSize="18sp"
- android:textColor="#ffffff"
- android:text="@string/Result" />
- <Button
- android:id="@+id/BtnBack"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
- android:text="@string/BtnBack" />
- </RelativeLayout>
- <TextView
- android:id="@+id/TextResult"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textColor="#000000"
- android:layout_margin="8dp"
- android:textIsSelectable="true"/>
- </LinearLayout>
实现的界面效果如图所示:
四、编写代码
首先我们来写一个用于扫描的相机预览视图CameraPreview,此文件由ZBar的SDK提供,这里我做了下简单的修改
接下来,我们来编写主界面的逻辑代码,在这里我们需要搞清楚的几个问题有:
1、相机的获取及相机的交互处理
2、二维码图片的获取
3、二维码图片的解析
对于第一个问题,需要我们深入地了解相机的工作原理,即我们需要了解Camera类。
获取相机的代码如下:
-
- public static Camera getCameraInstance()
- {
- Camera mCamera = null;
- try
- {
- mCamera = Camera.open();
-
- if (mCamera == null)
- {
- Camera.CameraInfo mCameraInfo = new Camera.CameraInfo();
- int cameraCount = Camera.getNumberOfCameras();
- for (int camIdx = 0; camIdx < cameraCount; camIdx++)
- {
- Camera.getCameraInfo(camIdx, mCameraInfo);
- if (mCameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT)
- {
- mCamera = Camera.open(camIdx);
- }
- }
- }
- }
- catch (Exception e)
- {
- e.printStackTrace();
- }
- return mCamera;
- }
释放照相机的方法
-
- private void releaseCamera()
- {
- if (mCamera != null)
- {
- IsPreview = false;
- mCamera.setPreviewCallback(null);
- mCamera.release();
- mCamera = null;
- }
- }
相机反馈的方法,即扫描到二维码后的处理
- PreviewCallback previewCb = new PreviewCallback()
- {
- public void onPreviewFrame(byte[] data, Camera camera)
- {
- Camera.Parameters parameters = camera.getParameters();
-
- Size mSize = parameters.getPreviewSize();
-
- Image mResult = new Image(mSize.width, mSize.height, "Y800");
-
- mResult.setData(data);
-
- int mResultCode = mScanner.scanImage(mResult);
-
- if (mResultCode != 0)
- {
-
- IsPreview = false;
- mCamera.setPreviewCallback(null);
- mCamera.stopPreview();
-
- SymbolSet Syms = mScanner.getResults();
- for (Symbol mSym : Syms)
- {
-
- if (mSym.getType() == Symbol.CODE128 || mSym.getType() == Symbol.QRCODE ||
- mSym.getType() == Symbol.CODABAR || mSym.getType() == Symbol.ISBN10 ||
- mSym.getType() == Symbol.ISBN13|| mSym.getType()==Symbol.DATABAR ||
- mSym.getType()==Symbol.DATABAR_EXP || mSym.getType()==Symbol.I25)
-
- {
-
- Vibrator mVibrator=(Vibrator)getSystemService(VIBRATOR_SERVICE);
- mVibrator.vibrate(400);
- Intent intent=new Intent(MainActivity.this,ResultActivity.class);
- intent.putExtra("ScanResult", "扫描类型:"+GetResultByCode(mSym.getType())+"\n"+ mSym.getData());
-
-
-
-
- startActivity(intent);
- IsScanned = true;
- }
- else
- {
-
- IsScanned = false;
- mCamera.setPreviewCallback(previewCb);
- mCamera.startPreview();
- IsPreview = true;
- mCamera.autoFocus(autoFocusCB);
- }
- }
- }
- }
- };
对于第二个问题,从上面的代码中我们可以看出,Image类用于获取二维码图片,ImageScanner类用于对图片的初步解析,而图片的最终解析是在SymbolSet类和
Symbol中去实现的,由此,第三个问题得以解答。下面给出完整代码:
-
-
-
-
-
-
-
-
-
-
-
- package com.Android.ZBar4Android;
-
- import com.Android.ZBar4Android.CameraPreview;
- import com.Android.ZBar4Android.R;
-
- import android.app.Activity;
- import android.app.AlertDialog;
- import android.content.DialogInterface;
- import android.content.Intent;
- import android.content.pm.ActivityInfo;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.Vibrator;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.ViewGroup.LayoutParams;
- import android.view.Window;
- import android.view.View.OnClickListener;
- import android.widget.FrameLayout;
- import android.widget.Button;
- import android.widget.PopupWindow;
- import android.hardware.Camera;
- import android.hardware.Camera.PreviewCallback;
- import android.hardware.Camera.AutoFocusCallback;
- import android.hardware.Camera.Size;
-
- import net.sourceforge.zbar.ImageScanner;
- import net.sourceforge.zbar.Image;
- import net.sourceforge.zbar.Symbol;
- import net.sourceforge.zbar.SymbolSet;
- import net.sourceforge.zbar.Config;
-
- public class MainActivity extends Activity
- {
-
- private Button BtnAbout;
-
- private Camera mCamera;
-
- private CameraPreview mPreview;
-
- private Handler mAutoFocusHandler;
-
- private ImageScanner mScanner;
-
- private PopupWindow mPopupWindow;
-
- private boolean IsScanned = false;
-
- private boolean IsPreview = true;
-
- private boolean IsShowPopup=false;
-
-
- static
- {
- System.loadLibrary("iconv");
- }
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
-
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- setContentView(R.layout.layout_main);
-
- setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
-
- mAutoFocusHandler = new Handler();
-
- mCamera = getCameraInstance();
- if(mCamera == null)
- {
-
- AlertDialog.Builder mBuilder=new AlertDialog.Builder(this);
- mBuilder.setTitle("ZBar4Android");
- mBuilder.setMessage("ZBar4Android获取相机失败,请重试!");
- mBuilder.setPositiveButton("确定", new DialogInterface.OnClickListener()
- {
-
- @Override
- public void onClick(DialogInterface mDialogInterface, int mIndex)
- {
- MainActivity.this.finish();
- }
- });
- AlertDialog mDialog=mBuilder.create();
- mDialog.show();
- }
-
- mScanner = new ImageScanner();
- mScanner.setConfig(0, Config.X_DENSITY, 3);
- mScanner.setConfig(0, Config.Y_DENSITY, 3);
-
- mPreview = new CameraPreview(this, mCamera, previewCb, autoFocusCB);
- FrameLayout preview = (FrameLayout)findViewById(R.id.cameraPreview);
- preview.addView(mPreview);
- if (IsScanned)
- {
- IsScanned = false;
- mCamera.setPreviewCallback(previewCb);
- mCamera.startPreview();
- IsPreview = true;
- mCamera.autoFocus(autoFocusCB);
- }
-
- BtnAbout=(Button)findViewById(R.id.BtnAbout);
- BtnAbout.setOnClickListener(new OnClickListener()
- {
- @Override
- public void onClick(View v)
- {
-
- if(IsShowPopup)
- {
- mPopupWindow.dismiss();
- IsShowPopup=false;
- }
- else
- {
-
- mPopupWindow=new PopupWindow();
- LayoutInflater mInflater=LayoutInflater.from(getApplicationContext());
- View view=mInflater.inflate(R.layout.layout_about, null);
- mPopupWindow.setContentView(view);
- mPopupWindow.setWidth(LayoutParams.WRAP_CONTENT);
- mPopupWindow.setHeight(LayoutParams.WRAP_CONTENT);
- mPopupWindow.showAtLocation(mPreview, 0, 100, 100);
- IsShowPopup=true;
- }
- }
- });
- }
-
- public void onPause()
- {
- super.onPause();
- releaseCamera();
- }
-
- public static Camera getCameraInstance()
- {
- Camera mCamera = null;
- try
- {
- mCamera = Camera.open();
-
- if (mCamera == null)
- {
- Camera.CameraInfo mCameraInfo = new Camera.CameraInfo();
- int cameraCount = Camera.getNumberOfCameras();
- for (int camIdx = 0; camIdx < cameraCount; camIdx++)
- {
- Camera.getCameraInfo(camIdx, mCameraInfo);
- if (mCameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT)
- {
- mCamera = Camera.open(camIdx);
- }
- }
- }
- }
- catch (Exception e)
- {
- e.printStackTrace();
- }
- return mCamera;
- }
-
-
- private void releaseCamera()
- {
- if (mCamera != null)
- {
- IsPreview = false;
- mCamera.setPreviewCallback(null);
- mCamera.release();
- mCamera = null;
- }
- }
-
- private Runnable doAutoFocus = new Runnable()
- {
- public void run()
- {
- if (IsPreview)
- mCamera.autoFocus(autoFocusCB);
- }
- };
-
- PreviewCallback previewCb = new PreviewCallback()
- {
- public void onPreviewFrame(byte[] data, Camera camera)
- {
- Camera.Parameters parameters = camera.getParameters();
-
- Size mSize = parameters.getPreviewSize();
-
- Image mResult = new Image(mSize.width, mSize.height, "Y800");
-
- mResult.setData(data);
-
- int mResultCode = mScanner.scanImage(mResult);
-
- if (mResultCode != 0)
- {
-
- IsPreview = false;
- mCamera.setPreviewCallback(null);
- mCamera.stopPreview();
-
- SymbolSet Syms = mScanner.getResults();
- for (Symbol mSym : Syms)
- {
-
- if (mSym.getType() == Symbol.CODE128 || mSym.getType() == Symbol.QRCODE ||
- mSym.getType() == Symbol.CODABAR || mSym.getType() == Symbol.ISBN10 ||
- mSym.getType() == Symbol.ISBN13|| mSym.getType()==Symbol.DATABAR ||
- mSym.getType()==Symbol.DATABAR_EXP || mSym.getType()==Symbol.I25)
-
- {
-
- Vibrator mVibrator=(Vibrator)getSystemService(VIBRATOR_SERVICE);
- mVibrator.vibrate(400);
- Intent intent=new Intent(MainActivity.this,ResultActivity.class);
- intent.putExtra("ScanResult", "扫描类型:"+GetResultByCode(mSym.getType())+"\n"+ mSym.getData());
-
-
-
-
- startActivity(intent);
- IsScanned = true;
- }
- else
- {
-
- IsScanned = false;
- mCamera.setPreviewCallback(previewCb);
- mCamera.startPreview();
- IsPreview = true;
- mCamera.autoFocus(autoFocusCB);
- }
- }
- }
- }
- };
-
-
- AutoFocusCallback autoFocusCB = new AutoFocusCallback()
- {
- public void onAutoFocus(boolean success, Camera camera)
- {
- mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
- }
- };
-
-
- public String GetResultByCode(int CodeType)
- {
- String mResult="";
- switch(CodeType)
- {
-
- case Symbol.CODABAR:
- mResult="条形码";
- break;
-
- case Symbol.CODE128:
- mResult="二维码";
- break;
-
- case Symbol.QRCODE:
- mResult="二维码";
- break;
-
- case Symbol.ISBN10:
- mResult="图书ISBN号";
- break;
-
- case Symbol.ISBN13:
- mResult="图书ISBN号";
- break;
- }
- return mResult;
- }
-
-
- }
对于显示扫描结果的界面,代码比较简单
-
-
-
-
-
-
-
- package com.Android.ZBar4Android;
-
- import android.app.Activity;
- import android.content.Intent;
- import android.os.Bundle;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.view.Window;
- import android.widget.Button;
- import android.widget.TextView;
-
- public class ResultActivity extends Activity
- {
-
- private TextView tv;
- private Button BtnBack;
- @Override
- protected void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- setContentView(R.layout.layout_result);
-
- Intent intent=getIntent();
- Bundle mData=intent.getExtras();
- CharSequence mResult=mData.getCharSequence("ScanResult");
- StringHelper mHelper=new StringHelper(mResult.toString());
- mResult=mHelper.SplitFormDict();
- tv=(TextView)findViewById(R.id.TextResult);
- tv.setText(mResult);
-
- BtnBack=(Button)findViewById(R.id.BtnBack);
- BtnBack.setOnClickListener(new OnClickListener()
- {
- @Override
- public void onClick(View arg0)
- {
- Intent intent=new Intent(ResultActivity.this,MainActivity.class);
- startActivity(intent);
- }
-
- });
- }
-
- @Override
- protected void onPause()
- {
- super.onPause();
- }
-
- }
五、总结
经过测试,可以快速地对二维码进行识别,并显示扫描结果。目前尚存在的问题有:
1、官方的文档说它是支持条形码、ISBN、二维码等多种形式的编码的,并且在程序代码中亦有所体现,但是实际测试中,发现二维码可以扫,其余的无法扫描
2、锁屏后再次打开程序会报错
3、与微信等类似的需要相机功能的软件冲突
4、校准框死活画不出来
5、在扫描结果界面下按下返回键,程序报错,无法拦截
欢迎大家积极寻找解决问题的方法,再次谢谢大家!
源代码下载