最近因为需求,用到了扫码,所以也就自己记录下,该文并没有太多详细的分析,只有自己使用的过程,并且只涉及android的扫二维码以及条形码,并没有多余的功能。(Zxing 代码具体什么版本忘记了,小伙伴可以自行替换)。
先来看下目录的结构
代码主要就是2部分,一部分是Zxing的代码,另一部分是2个Activity,其中ScanActivity就是调用了Zxing的扫码页面,ScanCodeActivity就是显示返回的扫码结果页。
然后看下对应的页面效果:
ScanCodeActivity:
ScanActivity:
具体代码实现:
public final class ScanActivity extends Activity implements ScanListener, View.OnClickListener {
static final String TAG = ScanActivity.class.getSimpleName();
private static final int RQ_CARAME = 102;
private static final int SCAN_REQUEST = 7;
SurfaceView scanPreview = null;
private boolean isSuccess = false;
private String scanResult;
View scanContainer;
View scanCropView;
ImageView scanLine;
ScanManager scanManager;
TextView iv_light;
TextView qrcode_g_gallery;
TextView qrcode_ic_back;
Button rescan ;
ImageView scan_image;
ImageView authorize_return;
TextView title;
TextView scan_hint;
TextView tv_scan_result;
final int PHOTOREQUESTCODE = 1111;
private int scanMode;//扫描模型(条形,二维码,全部)
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_scan);
scanMode=getIntent().getIntExtra(Constant.REQUEST_SCAN_MODE,Constant.REQUEST_SCAN_MODE_ALL_MODE);
initView();
}
void initView() {
rescan = (Button)findViewById(R.id.service_register_rescan);
scan_image = (ImageView)findViewById(R.id.scan_image);
authorize_return = (ImageView)findViewById(R.id.authorize_return);
title = (TextView) findViewById(R.id.common_title_TV_center);
scan_hint = (TextView) findViewById(R.id.scan_hint);
tv_scan_result = (TextView) findViewById(R.id.tv_scan_result);
scanPreview = (SurfaceView) findViewById(R.id.capture_preview);
scanContainer = findViewById(R.id.capture_container);
scanCropView = findViewById(R.id.capture_crop_view);
scanLine = (ImageView) findViewById(R.id.capture_scan_line);
qrcode_g_gallery = (TextView) findViewById(R.id.qrcode_g_gallery);
qrcode_g_gallery.setOnClickListener(this);
qrcode_ic_back = (TextView) findViewById(R.id.qrcode_ic_back);
qrcode_ic_back.setOnClickListener(this);
iv_light = (TextView) findViewById(R.id.iv_light);
iv_light.setOnClickListener(this);
switch (scanMode){
case DecodeThread.BARCODE_MODE://0X100
title.setText(R.string.scan_barcode_title);
scan_hint.setText(R.string.scan_barcode_hint);
break;
case DecodeThread.QRCODE_MODE://0X200
title.setText(R.string.scan_qrcode_title);
scan_hint.setText(R.string.scan_qrcode_hint);
break;
case DecodeThread.ALL_MODE://0X300
title.setText(R.string.scan_allcode_title);
scan_hint.setText(R.string.scan_allcode_hint);
break;
}
rescan.setOnClickListener(this);
authorize_return.setOnClickListener(this);
scanManager = new ScanManager(this, scanPreview, scanContainer, scanCropView, scanLine, scanMode,this);
}
@Override
public void onResume() {
super.onResume();
Log.e("scanManager","scanManager init 4");
if(scanManager != null){
scanManager.onResume();
}
rescan.setVisibility(View.INVISIBLE);
scan_image.setVisibility(View.GONE);
}
@Override
public void onPause() {
super.onPause();
if(scanManager != null){
scanManager.onPause();
}
}
/**
*
*/
public void scanResult(Result rawResult, Bundle bundle) {
//扫描成功后,扫描器不会再连续扫描,如需连续扫描,调用reScan()方法。
//scanManager.reScan();
// Toast.makeText(that, "result="+rawResult.getText(), Toast.LENGTH_LONG).show();
if (!scanManager.isScanning()) { //如果当前不是在扫描状态
//设置再次扫描按钮出现
rescan.setVisibility(View.VISIBLE);
scan_image.setVisibility(View.VISIBLE);
Bitmap barcode = null;
byte[] compressedBitmap = bundle.getByteArray(DecodeThread.BARCODE_BITMAP);
if (compressedBitmap != null) {
barcode = BitmapFactory.decodeByteArray(compressedBitmap, 0, compressedBitmap.length, null);
barcode = barcode.copy(Bitmap.Config.ARGB_8888, true);
}
scan_image.setImageBitmap(barcode);
}
rescan.setVisibility(View.VISIBLE);
scan_image.setVisibility(View.VISIBLE);
tv_scan_result.setVisibility(View.VISIBLE);
scanResult = rawResult.getText().toString();
Drawable drawable= getResources().getDrawable(R.mipmap.scan_success);
qrcode_ic_back.setBackground(drawable);
isSuccess = true;
tv_scan_result.setText("结果:"+rawResult.getText());
}
void startScan() {
if (rescan.getVisibility() == View.VISIBLE) {
rescan.setVisibility(View.INVISIBLE);
scan_image.setVisibility(View.GONE);
Drawable drawable= getResources().getDrawable(R.mipmap.scan2code_icon_back_nor);
qrcode_ic_back.setBackground(drawable);
isSuccess = false;
scanManager.reScan();
}
}
@Override
public void scanError(Exception e) {
Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
//相机扫描出错时
if(e.getMessage()!=null&&e.getMessage().startsWith("相机")){
scanPreview.setVisibility(View.INVISIBLE);
}
}
public void showPictures(int requestCode) {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("image/*");
startActivityForResult(intent, requestCode);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
String photo_path;
if (resultCode == RESULT_OK) {
switch (requestCode) {
case PHOTOREQUESTCODE:
String[] proj = {MediaStore.Images.Media.DATA};
Cursor cursor = this.getContentResolver().query(data.getData(), proj, null, null, null);
if (cursor.moveToFirst()) {
int colum_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
photo_path = cursor.getString(colum_index);
if (photo_path == null) {
photo_path = Utils.getPath(getApplicationContext(), data.getData());
}
scanManager.scanningImage(photo_path);
}
}
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.qrcode_g_gallery:
showPictures(PHOTOREQUESTCODE);
break;
case R.id.iv_light:
scanManager.switchLight();
break;
case R.id.qrcode_ic_back:
if(isSuccess){
Intent intent = new Intent(ScanActivity.this, ScanCodeActivity.class);//这种方法今天才学的,记下!方便这样写,坑爹的有些教程,这块没有给Inent绑定
intent.putExtra("scanResult",scanResult);
setResult(Activity.RESULT_OK, intent);
finish();
}else{
finish();
}
break;
case R.id.service_register_rescan://再次开启扫描
startScan();
break;
case R.id.authorize_return:
finish();
break;
default:
break;
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/capture_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white" >
<SurfaceView
android:id="@+id/capture_preview"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<!-- 扫描框上面的布局 -->
<RelativeLayout
android:id="@+id/top_mask"
android:layout_width="match_parent"
android:layout_height="150dp"
android:layout_alignParentTop="true"
android:background="@color/scan_bg">
<RelativeLayout
android:id="@+id/title_bar"
android:layout_width="fill_parent"
android:layout_height="45dip"
android:background="@android:color/white" >
<ImageView
android:id="@+id/authorize_return"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:background="@null"
android:paddingLeft="12dp"
android:paddingRight="20dp"
android:src="@mipmap/ic_my_returns_arrow"
/>
<TextView
android:id="@+id/common_title_TV_center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:ellipsize="middle"
android:singleLine="true"
android:text="二维码扫描"
android:textSize="18sp" />
</RelativeLayout>
<TextView
android:id="@+id/tv_scan_result"
android:layout_marginTop="20dp"
android:layout_below="@id/title_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="哈哈"
android:textSize="14sp"
android:layout_centerInParent="true"
android:textColor="@android:color/white"
android:visibility="gone"
/>
</RelativeLayout>
<!-- 扫描框底部的布局 -->
<RelativeLayout
android:id="@+id/bottom_mask"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_alignParentBottom="true"
android:background="@color/scan_bg"
android:orientation="vertical"
android:paddingBottom="16dp"
>
<TextView
android:id="@+id/scan_hint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="@string/scan_allcode_hint"
android:textColor="#b4b4b4"
android:textSize="14sp"
android:paddingTop="20dp"
android:paddingBottom="20dp"
/>
<TextView
android:id="@+id/iv_light"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight="30dp"
android:layout_below="@id/scan_hint"
android:background="@drawable/shouquan_qrcode_s_flashgun"
android:clickable="true" />
<TextView
android:id="@+id/qrcode_ic_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_below="@id/scan_hint"
android:background="@mipmap/scan2code_icon_back_nor"
android:clickable="true" />
<TextView
android:id="@+id/qrcode_g_gallery"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginLeft="30dp"
android:layout_below="@id/scan_hint"
android:background="@drawable/shouquan_qrcode_g_gallery"
android:clickable="true"
android:textSize="20sp" />
<Button
android:id="@+id/service_register_rescan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:background="@drawable/rescan_shape_button"
android:padding="10dp"
android:text="再次扫描"
android:textColor="@android:color/white"
android:textSize="18sp"
android:layout_centerHorizontal="true"
android:layout_below="@id/qrcode_g_gallery"
android:visibility="visible"
android:layout_marginTop="20dp"
/>
</RelativeLayout>
<!-- 扫描框中间的布局 -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/top_mask"
android:layout_above="@id/bottom_mask"
>
<ImageView
android:id="@+id/left_mask"
android:layout_width="34dp"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:background="@color/scan_bg"
android:contentDescription="@string/app_name" />
<ImageView
android:id="@+id/right_mask"
android:layout_width="34dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:background="@color/scan_bg" />
<RelativeLayout
android:id="@+id/capture_crop_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toLeftOf="@id/right_mask"
android:layout_toRightOf="@id/left_mask"
android:layout_centerHorizontal="true"
android:background="@android:color/transparent" >
<ImageView
android:id="@+id/capture_scan_line"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_margin="5dp"
android:background="@mipmap/scanning_line" />
<com.skt.scandemo.view.MyImageView
android:id="@+id/scan_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
</RelativeLayout>
</RelativeLayout>
说明一下:
首先需要初始化扫码的管理类,扫码的操作都在这个管理类当中,当然其中的listener需要该类实现ScanListener。
/**
* @param activity 扫描的activity
* @param scanPreview 预览的SurfaceView
* @param scanContainer 扫描的布局,全屏布局
* @param scanCropView 扫描的矩形区域
* @param scanLine 扫描线
* @param scanMode 扫码模型
* @param listener 扫码的监听
*/
scanManager = new ScanManager(this, scanPreview, scanContainer, scanCropView, scanLine, scanMode,this);
然后ScanListerer中只有两个方法,一个成功,一个失败的回调
public interface ScanListener {
/**
* 返回扫描结果
* @param rawResult 结果对象
* @param bundle 存放了截图,或者是空的
*/
public void scanResult(Result rawResult, Bundle bundle);
/**
* 扫描抛出的异常
* @param e
*/
public void scanError(Exception e);
}
成功后,执行scanResult方法,在该方法中,主要做的操作就是更新UI,解析数据,展示数据。
public void scanResult(Result rawResult, Bundle bundle) {
//扫描成功后,扫描器不会再连续扫描,如需连续扫描,调用reScan()方法。
//scanManager.reScan();
// Toast.makeText(that, "result="+rawResult.getText(), Toast.LENGTH_LONG).show();
if (!scanManager.isScanning()) { //如果当前不是在扫描状态
//设置再次扫描按钮出现
rescan.setVisibility(View.VISIBLE);
scan_image.setVisibility(View.VISIBLE);
Bitmap barcode = null;
byte[] compressedBitmap = bundle.getByteArray(DecodeThread.BARCODE_BITMAP);
if (compressedBitmap != null) {
barcode = BitmapFactory.decodeByteArray(compressedBitmap, 0, compressedBitmap.length, null);
barcode = barcode.copy(Bitmap.Config.ARGB_8888, true);
}
scan_image.setImageBitmap(barcode);
}
rescan.setVisibility(View.VISIBLE);
scan_image.setVisibility(View.VISIBLE);
tv_scan_result.setVisibility(View.VISIBLE);
scanResult = rawResult.getText().toString();
Drawable drawable= getResources().getDrawable(R.mipmap.scan_success);
qrcode_ic_back.setBackground(drawable);
isSuccess = true;
tv_scan_result.setText("结果:"+rawResult.getText());
}
ScanManager.java
public class ScanManager implements SurfaceHolder.Callback{
boolean isHasSurface = false;
CameraManager cameraManager;
//用于拍摄扫描的handler
CaptureActivityHandler handler;
//用于照片扫描的handler,不可共用,图片扫描是不需要摄像机的
PhotoScanHandler photoScanHandler;
Rect mCropRect = null;
InactivityTimer inactivityTimer;
public BeepManager beepManager;
SurfaceView scanPreview = null;
View scanContainer;
View scanCropView;
ImageView scanLine;
final String TAG= ScanManager.class.getSimpleName();
Activity activity;
ScanListener listener;
boolean isOpenLight=false;
private int scanMode;//扫描模型(条形,二维码,全部)
/**
* 用于启动照相机扫描二维码,在activity的onCreate里面构造出来
* 在activity的生命周期中调用此类相对应的生命周期方法
* @param activity 扫描的activity
* @param scanPreview 预览的SurfaceView
* @param scanContainer 扫描的布局,全屏布局
* @param scanCropView 扫描的矩形区域
* @param scanLine 扫描线
* @param scanMode 扫码模型
* @param listener 扫码的监听
*
*/
public ScanManager(Activity activity, SurfaceView scanPreview, View scanContainer,
View scanCropView, ImageView scanLine, int scanMode, ScanListener listener) {
this.activity=activity;
this.scanPreview=scanPreview;
this.scanContainer=scanContainer;
this.scanCropView=scanCropView;
this.scanLine=scanLine;
this.listener=listener;
this.scanMode=scanMode;
//启动动画
TranslateAnimation animation = new TranslateAnimation(Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT,
0.9f);
animation.setDuration(4500);
animation.setRepeatCount(-1);
animation.setRepeatMode(Animation.RESTART);
scanLine.startAnimation(animation);
}
/**
* 用于图片扫描的构造函数
* @param listener 结果的监听回调
*/
public ScanManager(ScanListener listener){
this.listener=listener;
}
public void onResume(){
// CameraManager must be initialized here, not in onCreate(). This is
// necessary because we don't
// want to open the camera driver and measure the screen size if we're
// going to show the help on
// first launch. That led to bugs where the scanning rectangle was the
// wrong size and partially
// off screen.
inactivityTimer = new InactivityTimer(activity);
beepManager = new BeepManager(activity);
cameraManager = new CameraManager(activity.getApplicationContext());
handler = null;
if (isHasSurface) {
// The activity was paused but not stopped, so the surface still
// exists. Therefore
// surfaceCreated() won't be called, so init the camera here.
initCamera(scanPreview.getHolder());
} else {
// Install the callback and wait for surfaceCreated() to init the
// camera.
scanPreview.getHolder().addCallback(this);
}
inactivityTimer.onResume();
}
public void onPause() {
if (handler != null) {
handler.quitSynchronously();
handler = null;
}
inactivityTimer.onPause();
beepManager.close();
cameraManager.closeDriver();
if (!isHasSurface) {
scanPreview.getHolder().removeCallback(this);
}
}
public void onDestroy() {
inactivityTimer.shutdown();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (holder == null) {
Log.e(TAG, "*** WARNING *** surfaceCreated() gave us a null surface!");
}
if (!isHasSurface) {
isHasSurface = true;
initCamera(holder);
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
isHasSurface = false;
}
void initCamera(SurfaceHolder surfaceHolder) {
if (surfaceHolder == null) {
throw new IllegalStateException("No SurfaceHolder provided");
}
if (cameraManager.isOpen()) {
Log.w(TAG, "initCamera() while already open -- late SurfaceView callback?");
return;
}
try {
cameraManager.openDriver(surfaceHolder);
// Creating the handler starts the preview, which can also throw a
// RuntimeException.
if (handler == null) {
handler = new CaptureActivityHandler(this, cameraManager, scanMode);
Log.e("hongliang1", "handler new成功!:"+handler);
}
initCrop();
} catch (IOException ioe) {
Log.e(TAG,"hongliang", ioe);
//弹出提示,报错
ioe.printStackTrace();
listener.scanError(new Exception("相机打开出错,请检查是否被禁止了该权限!"));
} catch (RuntimeException e) {
Log.e(TAG, "hongliang", e);
//弹出提示,报错
e.printStackTrace();
listener.scanError(new Exception("相机打开出错,请检查是否被禁止了该权限!"));
}
}
/**
* 开关闪关灯
*/
public void switchLight(){
if(isOpenLight){
cameraManager.offLight();
}else{
cameraManager.openLight();
}
isOpenLight=!isOpenLight;
}
public Handler getHandler() {
return handler;
}
public CameraManager getCameraManager() {
return cameraManager;
}
public Rect getCropRect() {
return mCropRect;
}
/**
* 扫描成功的结果回调
* @param rawResult
* @param bundle
*/
public void handleDecode(Result rawResult, Bundle bundle) {
inactivityTimer.onActivity();
//扫描成功播放声音滴一下,可根据需要自行确定什么时候播
beepManager.playBeepSoundAndVibrate();
bundle.putInt("width", mCropRect.width());
bundle.putInt("height", mCropRect.height());
bundle.putString("result", rawResult.getText());
listener.scanResult(rawResult, bundle);
}
public void handleDecodeError(Exception e){
listener.scanError(e);
}
/**
* 初始化截取的矩形区域
*/
void initCrop() {
int cameraWidth = cameraManager.getCameraResolution().y;
int cameraHeight = cameraManager.getCameraResolution().x;
/** 获取布局中扫描框的位置信息 */
int[] location = new int[2];
scanCropView.getLocationInWindow(location);
int cropLeft = location[0];
int cropTop = location[1] - getStatusBarHeight();
int cropWidth = scanCropView.getWidth();
int cropHeight = scanCropView.getHeight();
/** 获取布局容器的宽高 */
int containerWidth = scanContainer.getWidth();
int containerHeight = scanContainer.getHeight();
/** 计算最终截取的矩形的左上角顶点x坐标 */
int x = cropLeft * cameraWidth / containerWidth;
/** 计算最终截取的矩形的左上角顶点y坐标 */
int y = cropTop * cameraHeight / containerHeight;
/** 计算最终截取的矩形的宽度 */
int width = cropWidth * cameraWidth / containerWidth;
/** 计算最终截取的矩形的高度 */
int height = cropHeight * cameraHeight / containerHeight;
/** 生成最终的截取的矩形 */
mCropRect = new Rect(x, y, width + x, height + y);
}
int getStatusBarHeight() {
try {
Class<?> c = Class.forName("com.android.internal.R$dimen");
Object obj = c.newInstance();
Field field = c.getField("status_bar_height");
int x = Integer.parseInt(field.get(obj).toString());
return activity.getResources().getDimensionPixelSize(x);
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
/**
* 用于扫描本地图片二维码或者一维码
* @param photo_path2 本地图片的所在位置
* @return
*/
public void scanningImage(final String photo_path2) {
if(TextUtils.isEmpty(photo_path2)){
listener.scanError(new Exception("photo url is null!"));
}
photoScanHandler=new PhotoScanHandler(this);
new Thread(new Runnable() {
@Override
public void run() {
//获取初始化的设置器
Map<DecodeHintType, Object> hints = DecodeThread.getHints();
hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
// Hashtable<DecodeHintType, String> hints = new Hashtable<DecodeHintType, String>();
Bitmap bitmap= BitmapUtil.decodeBitmapFromPath(photo_path2,600,600);
RGBLuminanceSource source = new RGBLuminanceSource(bitmap);
BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source));
QRCodeReader reader = new QRCodeReader();
MultiFormatReader multiFormatReader=new MultiFormatReader();
try {
Message msg= Message.obtain();
msg.what=PhotoScanHandler.PHOTODECODEOK;
msg.obj = multiFormatReader.decode(bitmap1, hints);
photoScanHandler.sendMessage(msg);
} catch (Exception e) {
Message msg= Message.obtain();
msg.what=PhotoScanHandler.PHOTODECODEERROR;
msg.obj=new Exception("图片有误,或者图片模糊!");
photoScanHandler.sendMessage(msg);
}
}
}).start();
}
/**
* 扫描一次后,如需再次扫描,请调用这个方法
*/
public void reScan(){
if(handler!=null){
handler.sendEmptyMessage(R.id.restart_preview);
}
}
public boolean isScanning(){
if(handler!=null){
return handler.isScanning();
}
return false;
}
}
代码挺详细的就不再细说了。
Demo地址:https://download.csdn.net/download/silence_sep/10567267