使用谷歌的开源库zxing.jar实现二维码扫描功能。
zxing.jar实现二维码扫描的教程网上一搜一大堆,不过大都是千篇一律,而且比较复杂,封装的也不太好。
比如,大多数都会有这么一大堆文件(如下图),来回用handler传消息,很难看懂。布局也基本上是固定的,SurfaceView只能全屏,想要改界面还得先看懂,比较费劲。包里边既有Activity,又有View,又用到了R中的资源。自由度太低了。总的来说,感觉封装的很差劲。

经过学习,总结,最终发现,其实zxing.jar只是提供了二维码数据的解码,扫码过程中图片预览用的是摄像头Camera+SurfaceView,和zxing完全没有关系。
实现打开摄像头,并显示摄像头拍到的画面,只需要调用
camera=Camera.open();//打开摄像头 camera.setPreviewDisplay(surfaceView.getHolder());//用SurfaceView显示画面 camera.startPreview();//开始预览
就这么简单,何必写那一大堆类。
扫面二维码过程中,有一个矩形框,实际识别的是矩形框中的图片。矩形框的尺寸和位置其实是我们自己定义的。
我们只需要定义一个Rect对象,然后再定义一个ViewfinderView类来根据Rect对象画出矩形框和一条扫描线。
这么一来,从表面上看,扫描二维码的app已经做好了,但是最核心的扫描功能还没有加上。
我们需要从摄像头捕获的图像中,获取到我们自己定义的Rect对象区域内的部分,交给zxing.jar来进行解码。
实际上,我们不是从SurfaceView中截图,因为1、从SurfaceView中截图并不简单,2、效率会很差。
为了提高效率,Camera有一个setPreviewCallback方法,设置一个PreviewCallback对象,PreviewCallback对象需要实现一个方法:
public void onPreviewFrame(byte[] arg0, Camera arg1)
其中byte[]就是Camera捕获的图片的数据(水平不够,具体格式不了解)。
实际上,我们是在这里把数据进行转换然后交给zxing进行解码的。
具体转换过程可以参考别人写好的代码。
由于解码耗时较长,大约需要几十毫秒,为了界面看起来比较流畅,需要开一个独立的线程用于解码。
另外,byte[]数组得到的图片的尺寸和surfaceview的尺寸一般不会一样,需要了解一下Camera的一些方法,比如:getParameters().getPreviewSize()方法。
大致思路就是如此。并不需要做的那么复杂。
而且这么封装,自由度也高,可以任意布局。
核心类JcQrCodeScanner.java:
package cn.jucheng.jclibs.qrcode;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.Camera;
import android.hardware.Camera.PreviewCallback;
import android.hardware.Camera.Size;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.SurfaceView;
import com.google.zxing.MultiFormatReader;
/**
* 二维码扫描。
* <h3>使用方式:</h3>
* <h4>开启扫描:</h4>
* <pre>
* JcQrCodeScanner.init(context);
* JcQrCodeScanner.get().hd=hd;
* JcQrCodeScanner.get().openDriver(surfaceView);
* JcQrCodeScanner.get().startPreview();
* </pre>
* <h4>停止扫描:</h4>
* <pre>
* if(JcQrCodeScanner.get()!=null){
* JcQrCodeScanner.get().stopPreview();
* JcQrCodeScanner.get().closeDriver();
* JcQrCodeScanner.get().destroy();
* }
* </pre>
* <p>可以通过{@link #setFramingRect}设置扫描区域。</p>
* <p>可以通过{@link #getFramingRect}获取扫描区域,用于显示扫描框。扫描框可参考{@link cn.jucheng.jclibs.qrcode.ViewfinderView},当然你也可以自己定义。</p>
* <p>可以获取扫描区域的原始图像{@link #captureFramingBitmap},摄像头捕获的原始图像{@link #captureBitmap}</p>
* <h4>注意:</h4>
* <p>依赖zxing3.2.1.jar。记得导入jar包,注意版本号。</p>
* <p>记得在AndroidManifest.xml中添加Camera权限。</p>
* <pre><uses-permission android:name="android.permission.CAMERA"/></pre>
*
* @author 作者:hanyeah
* @date 创建时间:2017-2-16 上午10:34:42
*/
public class JcQrCodeScanner {
private static final String TAG="JcQrCodeScanner";
/**版本号**/
public static final String VERSION = "1.0.1";
private static JcQrCodeScanner jcQrCodeScanner;
private Context context;
private Camera camera;
private SurfaceView surfaceView;
private Rect framingRect;
private Rect framingRectInPreview;
private Boolean previewing=false;
private int previewFormat;
/**用于传递消息,比如解码成功。**/
public Handler hd;
private String result="";
public static final int DECODE_SUCCESS=1;
private JcQrCodeScanner(Context context) {
// TODO Auto-generated constructor stub
this.context=context;
decodeThread=new DecodeThread();
decodeThread.start();
}
/**
* 获取最近一次解码的结果。
* @return
*/
public String getReault(){
return result;
}
/**
* 初始化实例。
* @param context 使用该类的Activity对象。
*/
public static void init(Context context){
if(jcQrCodeScanner==null){
jcQrCodeScanner=new JcQrCodeScanner(context);
}
}
/**
* 获取实例。
* <p>获取之前需要先调用init进行初始化。</p>
* @return 该类的实例
*/
public static JcQrCodeScanner get(){
return jcQrCodeScanner;
}
/**
* 打开摄像头。
* @param surfaceView 用于显示摄像头捕获的图像的SurfaceView对象。
* @return Boolean
*/
public Boolean openDriver(SurfaceView surfaceView){
this.surfaceView=surfaceView;
if(camera==null){
camera=Camera.open();
if(camera==null){
return false;
}
try {
camera.setPreviewDisplay(surfaceView.getHolder());
previewFormat=camera.getParameters().getPreviewFormat();
cameraSize=camera.getParameters().getPreviewSize();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return true;
}
/**
* 关闭摄像头。
*/
public void closeDriver(){
if(camera!=null){
camera.release();
camera=null;
}
}
/**
* 开始预览,在之前设置的surfaceView中显示摄像头捕捉到的画面。
*/
public void startPreview(){
if(camera!=null&&!previewing){
camera.startPreview();
previewing=true;
isSucceed=false;
camera.setPreviewCallback(previewCallback);
}
}
/**
* 停止预览。
*/
public void stopPreview(){
if(camera!=null&&previewing){
camera.setPreviewCallback(null);
camera.stopPreview();
previewing=false;
}
}
/**
* 彻底销毁。
* 解码线程,对象实例都会销毁。
*/
public void destroy(){
stopPreview();
closeDriver();
Message quit = Message.obtain(decodeThread.getHandler(), 0);
quit.sendToTarget();
try {
decodeThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
Log.e(TAG,e.getMessage());
}
jcQrCodeScanner=null;
}
/**
* 设置Camera的预览尺寸。Camera的预览尺寸并不是连续的,所以实际值可能会和设置的值不符。
* @param width
* @param height
*/
public void setCameraPreviewSize(int width,int height){
if(camera!=null){
Camera.Parameters par=camera.getParameters();
par.setPreviewSize(width, height);
par.setPictureSize(width, height);
camera.setParameters(par);
cameraSize=camera.getParameters().getPreviewSize();
framingRectInPreview=null;
Log.d(TAG, "cameraSize="+cameraSize.width+","+cameraSize.height);
}
}
/**
* 设置预览框的矩形区域。
* @param rect
*/
public void setFramingRect(Rect rect){
framingRect=new Rect(rect);
framingRectInPreview=null;
}
/**
* 获取预览框的矩形区域。
* <p>实际显示尺寸。</p>
* @return Rect
*/
public Rect getFramingRect(){
if(framingRect==null){
Point p=new Point(surfaceView.getWidth(), surfaceView.getHeight());
framingRect=new Rect(p.x/4, p.y/4, p.x*3/4, p.y*3/4);
}
return framingRect;
}
/**
* 获取预览框的矩形区域。
* <p>相对于Camera获取的原始图片的尺寸。</p>
* @return
*/
public Rect getFramingRectInPreview(){
if(framingRectInPreview==null){
Point p=new Point(surfaceView.getWidth(), surfaceView.getHeight());
Rect rect = new Rect(getFramingRect());
rect.left = rect.left * cameraSize.width / p.x;
rect.right = rect.right * cameraSize.width / p.x;
rect.top = rect.top * cameraSize.height / p.y;
rect.bottom = rect.bottom * cameraSize.height / p.y;
framingRectInPreview = rect;
}
return framingRectInPreview;
}
/**
* <p>扫描区域截图。</p>
* <p>截取的是扫描区域的原始图像,可能和实际显示的大小不一致。</p>
* @return Bitmap
*/
public Bitmap captureFramingBitmap(){
Rect rect = getFramingRectInPreview();
return JcQrCode.capture(data,previewFormat, cameraSize.width, cameraSize.height, rect.left, rect.top, rect.width(), rect.height());
}
/**
* <p>Camera截图。</p>
* <p>截取的是Camera捕获的原始图像。</p>
* <p>也可以考虑用camera.takePicture实现。</p>
* @return Bitmap
*/
public Bitmap captureBitmap(){
return JcQrCode.capture(data,previewFormat, cameraSize.width, cameraSize.height, 0,0,cameraSize.width, cameraSize.height);
}
/**
* 保存Camera捕获的用于解码的图像数据。在PreviewCallback中更新。由于解码比较耗时,所以该数据的更新并不及时。
*/
private byte[] data;
/**
* 是否正在解码
*/
private Boolean isDecoding=false;
/**
* 是否解码成功
*/
private Boolean isSucceed=false;
/**
* Camera的预览尺寸,通过camera.getParameters().getPreviewSize()获取。
*/
private Size cameraSize;
private PreviewCallback previewCallback=new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] arg0, Camera arg1) {
// TODO Auto-generated method stub
if(isDecoding||isSucceed){
return;
}
isDecoding=true;
data=arg0;
decodeThread.getHandler().sendEmptyMessage(1);
}
};
/**
* 解码成功处理。
* <p>解码线程调用。</p>
* @param result
*/
private void decodeSuccess(String result){
Log.d(TAG, "解码成功:"+result);
this.result=result;
stopPreview();
closeDriver();
if(hd!=null){
Message msg=hd.obtainMessage(DECODE_SUCCESS);
msg.obj=result;
hd.sendMessage(msg);
}
}
private final DecodeThread decodeThread;
/**
* 解码线程。
* <p>解码比较耗时(几十毫秒),需要放到子线程中。</p>
* @author 作者:hanyeah
* @date 创建时间:2017-2-16 下午3:03:19
*/
private class DecodeThread extends Thread{
private final MultiFormatReader multiFormatReader;
private Handler handler;
private final CountDownLatch handlerInitLatch;//CountDownLatch这个要学习一下。
public DecodeThread() {
// TODO Auto-generated constructor stub
multiFormatReader = new MultiFormatReader();
handlerInitLatch = new CountDownLatch(1);
}
Handler getHandler() {
try {
handlerInitLatch.await();
} catch (InterruptedException ie) {
ie.printStackTrace();
Log.e(TAG, ie.getMessage());
// continue?
}
return handler;
}
/**
* 解码
*/
private void decode(){
try{
Rect rect = getFramingRectInPreview();
//Log.d(TAG, rect.toString());
//Log.d(TAG, String.format("%d,%d , %d",cameraSize.width, cameraSize.height,data.length));
String result=JcQrCode.decode(data, cameraSize.width, cameraSize.height, rect.left, rect.top, rect.width(), rect.height());
if(result!=""){
//解码成功,停止扫描,发送成功消息。
isSucceed=true;
decodeSuccess(result);
}
isDecoding=false;
}
catch(Exception e){
e.printStackTrace();
Log.d(TAG, "解码出错:"+e);
}
}
public void run() {
Looper.prepare();
//在线程中创建handler。
handler=new Handler(){
public void handleMessage(Message msg) {
switch(msg.what){
case 1:
decode();
break;
case 0:
Looper.myLooper().quit();
break;
}
};
};
handlerInitLatch.countDown();
Looper.loop();
};
};
}用到了自己封装的一个工具类JcQrCode.java
package cn.jucheng.jclibs.qrcode;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.util.Log;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.ChecksumException;
import com.google.zxing.DecodeHintType;
import com.google.zxing.EncodeHintType;
import com.google.zxing.FormatException;
import com.google.zxing.LuminanceSource;
import com.google.zxing.NotFoundException;
import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.RGBLuminanceSource;
import com.google.zxing.Result;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
import com.google.zxing.qrcode.QRCodeWriter;
/**
* 二维码编码解码。
* @author 作者:hanyeah
* @date 创建时间:2017-2-15 上午11:51:27
*/
public class JcQrCode {
private static final String TAG="JcQrCode";
/**
* 字符串编码为二维码图片。
* @param url 要编码的字符串
* @param qrWidth 二维码宽
* @param qrHeight 二维码高
* @return Bitmap 二维码图片
*/
public static Bitmap encode(String url,int qrWidth,int qrHeight)
{
try
{
//判断URL合法性
if (url == null || "".equals(url) || url.length() < 1)
{
Log.d(TAG, "二维码参数不合法");
return null;
}
HashMap<EncodeHintType, String> encodeHints = new HashMap<EncodeHintType, String>();
encodeHints.put(EncodeHintType.CHARACTER_SET, "utf-8");
//图像数据转换,使用了矩阵转换
BitMatrix bitMatrix = new QRCodeWriter().encode(url, BarcodeFormat.QR_CODE, qrWidth, qrHeight, encodeHints);
int[] pixels = new int[qrWidth * qrHeight];
//下面这里按照二维码的算法,逐个生成二维码的图片,
//两个for循环是图片横列扫描的结果
for (int y = 0; y < qrHeight; y++)
{
for (int x = 0; x < qrWidth; x++)
{
if (bitMatrix.get(x, y))
{
pixels[y * qrWidth + x] = 0xff000000;
}
else
{
pixels[y * qrWidth + x] = 0xffffffff;
}
}
}
//生成二维码图片的格式,使用ARGB_8888
Bitmap bitmap = Bitmap.createBitmap(qrWidth, qrHeight, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, qrWidth, 0, 0, qrWidth, qrHeight);
return bitmap;
}
catch (WriterException e)
{
e.printStackTrace();
}
return null;
}
/**
* 二维码图片解码为字符串。
* @param bmp 二维码图片
* @return String 二维码图片中的信息,解码失败返回""。
*/
public static String decode(Bitmap bmp){
int[] intArray = new int[bmp.getWidth()*bmp.getHeight()];
bmp.getPixels(intArray, 0, bmp.getWidth(), 0, 0, bmp.getWidth(), bmp.getHeight());
LuminanceSource source = new RGBLuminanceSource(bmp.getWidth(), bmp.getHeight(), intArray);
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
try {
HashMap<DecodeHintType, String> decodeHints = new HashMap<DecodeHintType, String>();
decodeHints.put(DecodeHintType.CHARACTER_SET, "utf-8");
Result result=new QRCodeReader().decode(binaryBitmap,decodeHints);
return result.getText();
} catch (NotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ChecksumException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (FormatException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "";
}
/**
* 图片数据中指定二维码区域进行解码。
* @param data byte[] 图片数据,具体格式不知道。
* @param dataWidth int 图片宽
* @param dataHeight int 图片高
* @param left int
* @param top int
* @param width int
* @param height int
* @return String
*/
public static String decode(byte[] data,int dataWidth,int dataHeight,int left,int top,int width,int height){
PlanarYUVLuminanceSource source=new PlanarYUVLuminanceSource(data, dataWidth, dataHeight,
left, top, width, height, false);
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
Result result;
try {
HashMap<DecodeHintType, String> decodeHints = new HashMap<DecodeHintType, String>();
decodeHints.put(DecodeHintType.CHARACTER_SET, "utf-8");
result = new QRCodeReader().decode(binaryBitmap,decodeHints);
return result.getText();
} catch (NotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ChecksumException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (FormatException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "";
}
/**
* 截取指定区域的图片。
* @param data byte[] 源数据源
* @param format int camera.getParameters().getPreviewFormat()
* @param dataWidth int 数据宽
* @param dataHeight int 数据高
* @param left int
* @param top int
* @param width int
* @param height int
* @return Bitmap
*/
static Bitmap capture(byte[] data,int format,int dataWidth,int dataHeight,int left,int top,int width,int height){
//参考:http://blog.csdn.net/hipilee/article/details/8629234
YuvImage yuvImg = new YuvImage(data,format,dataWidth,dataHeight,null);
ByteArrayOutputStream outputstream = new ByteArrayOutputStream();
Rect rect=new Rect(left,top,left+width,top+height);
yuvImg.compressToJpeg(rect, 100, outputstream);
Bitmap bitmap = BitmapFactory.decodeByteArray(outputstream.toByteArray(), 0,outputstream.size());
return bitmap;
}
/**
* 截取指定区域的图片。
* 灰度图。
* @param data
* @param dataWidth
* @param dataHeight
* @param left
* @param top
* @param width
* @param height
* @return
*/
static Bitmap renderCroppedGreyscaleBitmap(byte[] data,int dataWidth,int dataHeight,int left,int top,int width,int height) {
int[] pixels = new int[width * height];
byte[] yuv = data;
int inputOffset = top * dataWidth + left;
for (int y = 0; y < height; y++) {
int outputOffset = y * width;
for (int x = 0; x < width; x++) {
int grey = yuv[inputOffset + x] & 0xff;
pixels[outputOffset + x] = 0xFF000000 | (grey * 0x00010101);
}
inputOffset += dataWidth;
}
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
return bitmap;
}
}扫描框类ViewfinderView.java
/*
* 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.
*/
package cn.jucheng.jclibs.qrcode;
import java.util.Collection;
import java.util.HashSet;
import android.content.Context;
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;
/**
* 扫描框。
* <p>使用方式:</p>
* <p>在启动JcQrCodeScanner之后调用</p>
* <pre>
* viewfinderView.stopSaomiao=false;
* viewfinderView.invalidate();
* </pre>
* <p>停止扫描时,调用</p>
* <pre>
* vf.stopSaomiao=true;
* </pre>
*/
public final class ViewfinderView extends View {
private static final String TAG = "ViewfinderView";
/**
* 刷新界面的时间间隔(毫秒)。
*/
public int animationDelay = 20;
/**
* 四个绿色边角对应的长度
*/
public int cornerLineLength=10;
/**
* 四个绿色边角对应的宽度
*/
public int cornerLineWidth = 3;
/**
* 四个绿色边角对应的颜色
*/
public int cornerLineColor=0xFFFFCE85;
/**
* 扫描框中的中间线的宽度
*/
public int middleLineWidth = 2;
/**
* 扫描框中的中间线的与扫描框左右的间隙
*/
public int middleLinePadding = 5;
/**
* 中间那条线每次刷新移动的距离
*/
public float speed = 5f;
/**
* 扫描线的位置(y方向坐标)
*/
public float middleLineLocation=0f;
/**
* 字体大小
*/
private int textSize = 16;
/**
* 字体颜色
*/
private int textColor=Color.WHITE;
/**
* 字体距离扫描框下面的距离
*/
private int textPaddingTop = 30;
/**
* 显示的文字
*/
private String text="扫描中...";
/**
* 画笔对象的引用
*/
private Paint paint = new Paint();
/**
* 二维码区域外的颜色
*/
public int maskColor = 0x88000000;
/**
* 停止扫描
*/
public Boolean stopSaomiao=false;
/**
*
*/
public int resultPointColor = Color.YELLOW;;
private Collection<ResultPoint> possibleResultPoints = new HashSet<ResultPoint>(5);
private Collection<ResultPoint> lastPossibleResultPoints;
public ViewfinderView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void onDraw(Canvas canvas) {
if (JcQrCodeScanner.get() == null) {
return;
}
Rect frame = JcQrCodeScanner.get().getFramingRect();
if (frame == null) {
return;
}
// 获取屏幕的宽和高
int width = canvas.getWidth();
int height = canvas.getHeight();
paint.setColor(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(stopSaomiao)return;
// 画扫描框边上的角,总共8个部分
paint.setColor(cornerLineColor);
canvas.drawRect(frame.left, frame.top, frame.left + cornerLineLength, frame.top + cornerLineWidth, paint);
canvas.drawRect(frame.left, frame.top, frame.left + cornerLineWidth, frame.top + cornerLineLength, paint);
canvas.drawRect(frame.right - cornerLineLength, frame.top, frame.right, frame.top + cornerLineWidth, paint);
canvas.drawRect(frame.right - cornerLineWidth, frame.top, frame.right, frame.top + cornerLineLength, paint);
canvas.drawRect(frame.left, frame.bottom - cornerLineWidth, frame.left + cornerLineLength, frame.bottom, paint);
canvas.drawRect(frame.left, frame.bottom - cornerLineLength, frame.left + cornerLineWidth, frame.bottom, paint);
canvas.drawRect(frame.right - cornerLineLength, frame.bottom - cornerLineWidth, frame.right, frame.bottom, paint);
canvas.drawRect(frame.right - cornerLineWidth, frame.bottom - cornerLineLength, frame.right, frame.bottom, paint);
// 绘制中间的线,每次刷新界面,中间的线往下移动SPEEN_DISTANCE
middleLineLocation += speed;
if (middleLineLocation >= frame.bottom||middleLineLocation<frame.top) {
middleLineLocation = frame.top;
}
canvas.drawRect(frame.left + middleLinePadding, middleLineLocation - middleLineWidth / 2, frame.right - middleLinePadding, middleLineLocation + middleLineWidth / 2, paint);
//画小黄点
Collection<ResultPoint> currentPossible = possibleResultPoints;
Collection<ResultPoint> currentLast = lastPossibleResultPoints;
if (currentPossible.isEmpty()) {
lastPossibleResultPoints = null;
} else {
possibleResultPoints = new HashSet<ResultPoint>(5);
lastPossibleResultPoints = currentPossible;
paint.setColor(resultPointColor);
for (ResultPoint point : currentPossible) {
canvas.drawCircle(frame.left + point.getX(), frame.top + point.getY(), 6.0f, paint);
}
}
if (currentLast != null) {
paint.setColor(resultPointColor);
for (ResultPoint point : currentLast) {
canvas.drawCircle(frame.left + point.getX(), frame.top + point.getY(), 3.0f, paint);
}
}
// 画扫描框下面的字
drawText(canvas,frame);
// 只刷新扫描框的内容,其他地方不刷新,刷新区域没有用到
postInvalidateDelayed(animationDelay, frame.left, frame.top, frame.right, frame.bottom);
}
/**
* 画文字
* @param canvas
* @param frame
*/
protected void drawText(Canvas canvas,Rect frame){
paint.setColor(textColor);
paint.setTextSize(textSize);
paint.setTypeface(Typeface.create("System", Typeface.BOLD));
canvas.drawText(text, frame.left, (float) (frame.bottom + (float) textPaddingTop), paint);
}
/**
* 添加小黄点。
* 小黄点是什么意思,还不了解。
* @param point
*/
public void addPossibleResultPoint(ResultPoint point) {
possibleResultPoints.add(point);
}
}
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。