源码下载地址:
地址1:https://github.com/alivebao/BarCodeReader
地址2:http://download.csdn.net/detail/miaoyunzexiaobao/8297201
参考链接:
zxing入门:http://www.cnblogs.com/liuan/archive/2012/01/05/2312714.html
BitmapLuminanceSource类实现:http://blog.csdn.net/xyz_fly/article/details/8089558
目标:完成一个条码扫描程序,能识别出一维码和二维码,并将解析出来的结果显示出来
效果图:扫描中-》扫描成功
扫描成功后:
首先在布局上放置一个预览框,用于实时显示摄像头拍摄到的情况,并在其上绘制一条来回扫描的线条。之后在其下方放一个imageView,用于显示摄像头自动聚焦后获取的图片。
实现:该Activity的整体布局为方向vertical的线性布局。该线性布局内嵌着一个帧布局和另一个线性布局。帧布局中先在底层防止了一个SurfaceView,用于显示摄像头预览图片,再在其上放置了一个自己实现的View类ScanLineView,该类继承自View,主要功能为在SurfaceView上绘制一条来回扫描的红色线条。下面的LinearLayout放置了一个ImageView,用于显示聚焦后获得的图片(程序自动对该图片进行解码)
code:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight=".3" >
<SurfaceView
android:id="@+id/preview_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<com.miao.barcodereader.ScanLineView
android:id="@+id/capture_viewfinder_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:color/transparent" />
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight=".7"
android:orientation="vertical" >
<ImageView
android:id="@+id/imageView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:src="@drawable/ic_launcher" />
</LinearLayout>
</LinearLayout>
ScanLineView类:
继承自View类,重载其onDraw函数,在onDraw函数中间隔一段时间发送申请重绘屏幕的请求,drawLine函数的功能为每次被调用时绘制一条横坐标逐渐增加的红线。这个类要是嫌麻烦的话也可以不写,把上面布局文件中的ScanLineView控件删掉就行了,对程序功能没什么影响。
code:
package com.miao.barcodereader;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
public class ScanLineView extends View{
private static final long ANIMATION_DELAY = 10L;
private Paint paint;
private int xLinePos = 0;
private int canvasWidth = 0;
private int canvasHeight = 0;
public ScanLineView(Context context, AttributeSet attrs) {
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
@Override
public void onDraw(Canvas canvas) {
canvasWidth = canvas.getWidth();
canvasHeight = canvas.getHeight();
drawLine(canvas);
postInvalidateDelayed(ANIMATION_DELAY, 0, 0, canvasWidth, canvasHeight);
}
private void drawLine(Canvas canvas) {
int iLineBegin = canvasWidth / 5;
int iLineEnd = canvasWidth * 4 / 5;
int iFrameHigh = canvasHeight ;
Rect frame = new Rect(iLineBegin, 0, canvasWidth, iFrameHigh);
xLinePos += 10;
if (xLinePos > iLineEnd)
xLinePos = iLineBegin;
paint.setColor(Color.RED);
canvas.drawRect(xLinePos, 0, xLinePos + 1, iFrameHigh, paint);
}
}
负责将预览图显示到SurfaceView中,并每隔一段时间自动聚焦,聚焦成功后将获得的图片显示在ImageView控件中,并对该图片进行解析。若解析成功则将结果以对话框形式弹出。在该类的构造函数中,先获取主窗口的activity、imageView控件和SurfaceView控件,然后进行一些其他参数的设置。
本类实现了SurfaceHolder.Callback的接口,因此要实现surfaceChanged、surfaceCreated、surfaceDestroyed三个函数。在surfaceCreated中调用initCamera函数,打开摄像头并将其预览显示在SurfaceView上。
本类实现了一个定时器CameraTimerTask,其功能为在摄像头成功打开的情况下,每间隔一段使摄像头自动聚焦,并在聚焦的同时调用自动聚焦回调函数mAutoFocusCallBack。该函数是自己实现的,其功能为当聚焦成功时,调用预览回调函数previewCallback。这个也是自己实现的,其功能为将获取的预览图片置于ImageView中,并将该图片转为Bitmap格式,传送给类BitmapLuminanceSource(该类收到图片后对图片进行解析,并返回一个string类型变量)。若该类解析成功则会返回解析出的条码内容,若解析失败则返回“empty”。在previewCallback中,若BitmapLuminanceSource返回的string不是“empty”,则证明解析成功,于是将结果显示出来。
code:
package com.miao.barcodereader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.Camera;
import android.util.Log;
import android.view.Display;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.Toast;
public class CameraManage implements SurfaceHolder.Callback {
private SurfaceHolder surfaceHolder;
private Camera camera;
private Activity activity;
SurfaceView surfaceView;
private ImageView imageView;
private Timer mTimer;
private TimerTask mTimerTask;
private Camera.AutoFocusCallback mAutoFocusCallBack;
private Camera.PreviewCallback previewCallback;
CameraManage(Activity ac, ImageView iv, SurfaceView sv) {
activity = ac;
imageView = iv;
surfaceView = sv;
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
AutoFocusSet();
mTimer = new Timer();
mTimerTask = new CameraTimerTask();
mTimer.schedule(mTimerTask, 0, 500);
}
public void AutoFocusSet() {
mAutoFocusCallBack = new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
if (success) {
// isAutoFocus = true;
camera.setOneShotPreviewCallback(previewCallback);
}
}
};
previewCallback = new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera arg1) {
if (data != null) {
Camera.Parameters parameters = camera.getParameters();
int imageFormat = parameters.getPreviewFormat();
Log.i("map", "Image Format: " + imageFormat);
Log.i("CameraPreviewCallback", "data length:" + data.length);
if (imageFormat == ImageFormat.NV21) {
// get full picture
Bitmap image = null;
int w = parameters.getPreviewSize().width;
int h = parameters.getPreviewSize().height;
Rect rect = new Rect(0, 0, w, h);
YuvImage img = new YuvImage(data, ImageFormat.NV21, w,
h, null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
if (img.compressToJpeg(rect, 100, baos)) {
image = BitmapFactory.decodeByteArray(
baos.toByteArray(), 0, baos.size());
image = adjustPhotoRotation(image, 90);
imageView.setImageBitmap(image);
Drawable d = imageView.getDrawable();
BitmapDrawable bd = (BitmapDrawable) d;
Bitmap bm = bd.getBitmap();
String str = BitmapLuminanceSource.getResult(bm);
if (!str.equals("empty"))
Toast.makeText(activity.getApplication(), str,
Toast.LENGTH_SHORT).show();
}
}
}
}
};
}
class CameraTimerTask extends TimerTask {
@Override
public void run() {
if (camera != null) {
camera.autoFocus(mAutoFocusCallBack);
}
}
}
@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
// TODO Auto-generated method stub
}
@Override
public void surfaceCreated(SurfaceHolder arg0) {
// TODO Auto-generated method stub
initCamera(surfaceHolder);
}
@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
// TODO Auto-generated method stub
if (camera != null) {
camera.stopPreview();
camera.release();
camera = null;
}
previewCallback = null;
mAutoFocusCallBack = null;
}
public void initCamera(SurfaceHolder surfaceHolder) {
camera = Camera.open();
if (camera == null) {
return;
}
Camera.Parameters parameters = camera.getParameters();
WindowManager wm = (WindowManager) (activity
.getSystemService(Context.WINDOW_SERVICE));
Display display = wm.getDefaultDisplay();
parameters.setPreviewSize(display.getWidth(), display.getHeight());
camera.setParameters(parameters);
try {
camera.setPreviewDisplay(surfaceHolder);
} catch (IOException e) {
System.out.println(e.getMessage());
}
camera.setDisplayOrientation(90);
camera.startPreview();
}
public Bitmap adjustPhotoRotation(Bitmap bm, final int orientationDegree) {
Matrix m = new Matrix();
m.setRotate(orientationDegree, (float) bm.getWidth() / 2,
(float) bm.getHeight() / 2);
try {
Bitmap bm1 = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(),
bm.getHeight(), m, true);
return bm1;
} catch (OutOfMemoryError ex) {
}
return null;
}
}
这里要记得在AndroidManifest.xml中写权限:
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
最后是BitmapLuminanceSource类:
思路见参考链接
code:
package com.miao.barcodereader;
import java.util.HashMap;
import java.util.Map;
import android.graphics.Bitmap;
import com.google.zxing.Binarizer;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.EncodeHintType;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.NotFoundException;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
public class BitmapLuminanceSource extends LuminanceSource {
private byte bitmapPixels[];
protected BitmapLuminanceSource(Bitmap bitmap) {
super(bitmap.getWidth(), bitmap.getHeight());
int[] data = new int[bitmap.getWidth() * bitmap.getHeight()];
this.bitmapPixels = new byte[bitmap.getWidth() * bitmap.getHeight()];
bitmap.getPixels(data, 0, getWidth(), 0, 0, getWidth(), getHeight());
for (int i = 0; i < data.length; i++) {
this.bitmapPixels[i] = (byte) data[i];
}
}
@Override
public byte[] getMatrix() {
return bitmapPixels;
}
@Override
public byte[] getRow(int y, byte[] row) {
System.arraycopy(bitmapPixels, y * getWidth(), row, 0, getWidth());
return row;
}
static public String getResult(Bitmap bitmap){
MultiFormatReader formatReader = new MultiFormatReader();
LuminanceSource source = new BitmapLuminanceSource(bitmap);
Binarizer binarizer = new HybridBinarizer(source);
BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer);
Map hints = new HashMap();
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
Result result = null;
try {
result = formatReader.decode(binaryBitmap, hints);
} catch (NotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(result == null)
return "empty";
else
return result.toString();
}
}