人的活动如果没有理想的鼓舞,就会变得空虚而渺小。
本讲内容:二维码
一、zxing简介:
zxing是一个开放源码的,zxing可以实现使用手机的内置的摄像头完成条形码和二维码的扫描与解码。
二、Android上zxing的使用(二种方法):
1.将zxing的jar包放到工程的lib库中,然后将com.mining.app.zxing.camera,com.mining.app.zxing.decoding,com.mining.app.zxing.view这三个包拷贝到你的项目中
2.将已经弄好zxing的工程作为当前工程的依赖库,然后直接使用就可以了;
示例一:将已经弄好zxing的工程作为当前工程的依赖库
下面是res/layout/activity_main.xml 布局文件:
<RelativeLayout 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" >
<Button
android:id="@+id/id_b1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="扫一扫" />
</RelativeLayout>
下面是MainActivity.java主界面文件:
public class MainActivity extends Activity implements OnClickListener {
private static final int PHOTO_PIC = 1;// 拍照
private Button b1;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
b1 = (Button) findViewById(R.id.id_b1);
b1.setOnClickListener(this);
}
/**
* 由于我们是使用startActivityForResult()方法来启动某活动,
* 在某活动被销毁之后会回调上一个活动的onActivityResult()方法
* requestCode区分启动不同的活动,resultCode判断处理结果是否成功
*/
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
switch (requestCode) {
case PHOTO_PIC:
String result = data.getExtras().getString("result");
Toast.makeText(MainActivity.this, "解析结果:" + result,Toast.LENGTH_LONG).show();
break;
}
}
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.id_b1:
// 跳转到拍照界面扫描二维码
Intent intent = new Intent(MainActivity.this, CaptureActivity.class);
startActivityForResult(intent, PHOTO_PIC);
break;
}
}
}
manifest的配置部分,需要加入权限,和依赖库中的一个Activity的声明:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.zxing.activity.CaptureActivity"></activity>
</application>
示例二:拷贝相关源码到你的项目中更改扫一扫界面
项目的结构
直接将com.mining.app.zxing.camera,com.mining.app.zxing.decoding,com.mining.app.zxing.view这三个包拷贝到你的项目中,还需要引用Zxing.jar资源包。
下面是res/layout/activity_title.xml 布局文件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/mmtitle_bg_alpha" >
<Button
android:id="@+id/button_back"
android:layout_width="75dp"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:background="@drawable/mm_title_back_btn"
android:text="返回"
android:textColor="@android:color/white" />
<TextView
android:id="@+id/textview_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:gravity="center_vertical"
android:text="二维码扫描"
android:textColor="@android:color/white"
android:textSize="18sp" />
<ImageButton
android:id="@+id/button_function"
android:layout_width="75dp"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight="2dip"
android:background="@drawable/mm_title_right_btn"
android:minWidth="70dip"
android:src="@drawable/mm_title_btn_menu_normal" />
</RelativeLayout>
下面是res/layout/activity_capture.xml 布局文件:(改写扫一扫界面)
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<SurfaceView
android:id="@+id/preview_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true" />
<com.mining.app.zxing.view.ViewfinderView
android:id="@+id/viewfinder_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<include
android:id="@+id/include1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
layout="@layout/activity_title" />
</RelativeLayout>
</FrameLayout>
下面是CaptureActivity.java扫一扫界面文件:(改写之前直接引入我项目的代码的,主要处理扫描界面的类)
public class CaptureActivity extends Activity implements Callback,OnClickListener {
private CaptureActivityHandler handler;
private ViewfinderView viewfinderView;
private boolean hasSurface;
private Vector<BarcodeFormat> decodeFormats;
private String characterSet;
private InactivityTimer inactivityTimer;
private MediaPlayer mediaPlayer;
private boolean playBeep;
private static final float BEEP_VOLUME = 0.10f;
private boolean vibrate;
private static final int REQUEST_CODE = 100;
private static final int PARSE_BARCODE_SUC = 300;
private static final int PARSE_BARCODE_FAIL = 303;
private ProgressDialog mProgress;
private String photo_path;
private Bitmap scanBitmap;
private Button mButtonBack;
private ImageButton mImageButton;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_capture);
CameraManager.init(getApplication());
viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);
mButtonBack = (Button) findViewById(R.id.button_back);
mButtonBack.setOnClickListener(this);
hasSurface = false;
inactivityTimer = new InactivityTimer(this);
mImageButton=(ImageButton) findViewById(R.id.button_function);
mImageButton.setOnClickListener(this);
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.button_back:
CaptureActivity.this.finish();
break;
case R.id.button_function:
//打开手机中的相册
Intent innerIntent=new Intent(Intent.ACTION_GET_CONTENT);
innerIntent.setType("image/*");//所有图片格式
Intent wrapperIntent=Intent.createChooser(innerIntent, "选择二维码图片");
this.startActivityForResult(wrapperIntent, REQUEST_CODE);
break;
}
}
private Handler mHandler = new Handler(){
public void handleMessage(Message msg) {
super.handleMessage(msg);
mProgress.dismiss();
switch (msg.what) {
case PARSE_BARCODE_SUC:
onResultHandler((String)msg.obj, scanBitmap);
break;
case PARSE_BARCODE_FAIL:
Toast.makeText(CaptureActivity.this, (String)msg.obj, Toast.LENGTH_LONG).show();
break;
}
}
};
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == RESULT_OK){
switch (requestCode) {
case REQUEST_CODE:
// 获取选中图片的路径
String[] proj = new String[] { MediaStore.Images.Media.DATA };
Cursor cursor = getContentResolver().query(data.getData(), proj, null, null, null);
if (cursor.moveToFirst()) {
int columnIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA);
// 获取到用户选择的二维码图片的绝对路径
photo_path = cursor.getString(columnIndex);
}
cursor.close();
mProgress = new ProgressDialog(CaptureActivity.this);
mProgress.setMessage("正在扫描...");
mProgress.setCancelable(false);
mProgress.show();
new Thread(new Runnable(){
public void run() {
Result result =parseBitmap(photo_path);
if (result != null) {
Message m = mHandler.obtainMessage();
m.what = PARSE_BARCODE_SUC;
m.obj = result.getText();
mHandler.sendMessage(m);
} else {
Message m = mHandler.obtainMessage();
m.what = PARSE_BARCODE_FAIL;
m.obj = "Scan failed!";
mHandler.sendMessage(m);
}
}
}).start();
break;
}
}
}
// 解析二维码图片,返回结果封装在Result对象中
private Result parseBitmap(String path) {
if (TextUtils.isEmpty(path)) {
return null;
}
// 解析转换类型UTF-8
Hashtable<DecodeHintType, String> hints = new Hashtable<DecodeHintType, String>();
hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
// 获取到待解析的图片
BitmapFactory.Options options = new BitmapFactory.Options();
// 如果我们把inJustDecodeBounds设为true,那么BitmapFactory.decodeFile(String path,
// Options opt)
// 并不会真的返回一个Bitmap给你,它仅仅会把它的宽,高取回来给你
options.inJustDecodeBounds = true;
// 此时的bitmap是null,这段代码之后,options.outWidth 和 options.outHeight就是我们想要的宽和高了
Bitmap bitmap = BitmapFactory.decodeFile(path, options);
// 我们现在想取出来的图片的边长(二维码图片是正方形的)设置为400像素
/**
* options.outHeight = 400; options.outWidth = 400;
* options.inJustDecodeBounds = false; bitmap =
* BitmapFactory.decodeFile(bitmapPath, options);
*/
// 以上这种做法,虽然把bitmap限定到了我们要的大小,但是并没有节约内存,如果要节约内存,我们还需要使用inSimpleSize这个属性
options.inSampleSize = options.outHeight / 400;
if (options.inSampleSize <= 0) {
options.inSampleSize = 1; // 防止其值小于或等于0
}
/**
* 辅助节约内存设置
*
* options.inPreferredConfig = Bitmap.Config.ARGB_4444; //
* 默认是Bitmap.Config.ARGB_8888 options.inPurgeable = true;
* options.inInputShareable = true;
*/
options.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeFile(path, options);
// 新建一个RGBLuminanceSource对象,将bitmap图片传给此对象
RGBLuminanceSource rgbLuminanceSource = new RGBLuminanceSource(bitmap);
// 将图片转换成二进制图片
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(
rgbLuminanceSource));
// 初始化解析对象
QRCodeReader reader = new QRCodeReader();
// 开始解析
Result result = null;
try {
result = reader.decode(binaryBitmap, hints);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
protected void onResume() {
super.onResume();
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
SurfaceHolder surfaceHolder = surfaceView.getHolder();
if (hasSurface) {
initCamera(surfaceHolder);
} else {
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
decodeFormats = null;
characterSet = null;
playBeep = true;
AudioManager audioService = (AudioManager) getSystemService(AUDIO_SERVICE);
if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
playBeep = false;
}
initBeepSound();
vibrate = true;
}
protected void onPause() {
super.onPause();
if (handler != null) {
handler.quitSynchronously();
handler = null;
}
CameraManager.get().closeDriver();
}
protected void onDestroy() {
inactivityTimer.shutdown();
super.onDestroy();
}
/**
* 处理扫描结果
*
* @param surfaceHolder
*/
public void handleDecode(Result result, Bitmap barcode) {
inactivityTimer.onActivity();
playBeepSoundAndVibrate();
String resultString = result.getText();
onResultHandler(resultString, barcode);
}
/**
* 跳转到上一个页面
* @param surfaceHolder
*/
private void onResultHandler(String resultString, Bitmap bitmap){
if(TextUtils.isEmpty(resultString)){
Toast.makeText(CaptureActivity.this, "Scan failed!", Toast.LENGTH_SHORT).show();
return;
}
Intent resultIntent = new Intent();
Bundle bundle = new Bundle();
bundle.putString("result", resultString);
bundle.putParcelable("bitmap", bitmap);
resultIntent.putExtras(bundle);
this.setResult(RESULT_OK, resultIntent);
CaptureActivity.this.finish();
}
private void initCamera(SurfaceHolder surfaceHolder) {
try {
CameraManager.get().openDriver(surfaceHolder);
} catch (IOException ioe) {
return;
} catch (RuntimeException e) {
return;
}
if (handler == null) {
handler = new CaptureActivityHandler(this, decodeFormats,
characterSet);
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
public void surfaceCreated(SurfaceHolder holder) {
if (!hasSurface) {
hasSurface = true;
initCamera(holder);
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
hasSurface = false;
}
public ViewfinderView getViewfinderView() {
return viewfinderView;
}
public Handler getHandler() {
return handler;
}
public void drawViewfinder() {
viewfinderView.drawViewfinder();
}
private void initBeepSound() {
if (playBeep && mediaPlayer == null) {
setVolumeControlStream(AudioManager.STREAM_MUSIC);
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setOnCompletionListener(beepListener);
AssetFileDescriptor file = getResources().openRawResourceFd(
R.raw.beep);
try {
mediaPlayer.setDataSource(file.getFileDescriptor(),
file.getStartOffset(), file.getLength());
file.close();
mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);
mediaPlayer.prepare();
} catch (IOException e) {
mediaPlayer = null;
}
}
}
private static final long VIBRATE_DURATION = 200L;
private void playBeepSoundAndVibrate() {
if (playBeep && mediaPlayer != null) {
mediaPlayer.start();
}
if (vibrate) {
Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
vibrator.vibrate(VIBRATE_DURATION);
}
}
private final OnCompletionListener beepListener = new OnCompletionListener() {
public void onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.seekTo(0);
}
};
}
下面是RGBLuminanceSource.java文件:(识别二维码图片)
/**
* 识别二维码图片
*/
public class RGBLuminanceSource extends LuminanceSource {
private final byte[] luminances;
public RGBLuminanceSource(Bitmap bitmap) {
super(bitmap.getWidth(), bitmap.getHeight());
//得到图片的宽高
int width = bitmap.getWidth();
int height = bitmap.getHeight();
//得到图片的像素
int[] pixels = new int[width * height];
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
//为了测量纯解码速度,我们将整个图像灰度阵列前面,这是一样的通道
// YUVLuminanceSource在现实应用。
//得到像素大小的字节数
luminances = new byte[width * height];
//得到图片每点像素颜色
for (int y = 0; y < height; y++) {
int offset = y * width;
for (int x = 0; x < width; x++) {
int pixel = pixels[offset + x];
int r = (pixel >> 16) & 0xff;
int g = (pixel >> 8) & 0xff;
int b = pixel & 0xff;
//当某一点三种颜色值相同时,相应字节对应空间赋值为其值
if (r == g && g == b) {
luminances[offset + x] = (byte) r;
}
//其它情况字节空间对应赋值为:
else {
luminances[offset + x] = (byte) ((r + g + g + b) >> 2);
}
}
}
}
public RGBLuminanceSource(String path) throws FileNotFoundException {
this(loadBitmap(path));
}
public byte[] getMatrix() {
return luminances;
}
public byte[] getRow(int arg0, byte[] arg1) {
if (arg0 < 0 || arg0 >= getHeight()) {
throw new IllegalArgumentException(
"Requested row is outside the image: " + arg0);
}
int width = getWidth();
if (arg1 == null || arg1.length < width) {
arg1 = new byte[width];
}
System.arraycopy(luminances, arg0 * width, arg1, 0, width);
return arg1;
}
private static Bitmap loadBitmap(String path) throws FileNotFoundException {
Bitmap bitmap = BitmapFactory.decodeFile(path);
if (bitmap == null) {
throw new FileNotFoundException("Couldn't open " + path);
}
return bitmap;
}
}
下面是MainActivity.java主界面文件:
public class MainActivity extends Activity implements OnClickListener {
private static final int PHOTO_PIC = 1;// 拍照
private Button b1;
/**
* 显示扫描结果
*/
private TextView mTextView;
/**
* 显示扫描拍的图片
*/
private ImageView mImageView;
private String photo_path;// 选择图片的绝对路径
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
b1 = (Button) findViewById(R.id.id_b1);
b1.setOnClickListener(this);
mTextView = (TextView) findViewById(R.id.id_result);
mImageView = (ImageView) findViewById(R.id.id_qrcode_bitmap);
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.id_b1:
// 跳转到拍照界面扫描二维码
Intent intent = new Intent(MainActivity.this, CaptureActivity.class);
startActivityForResult(intent, PHOTO_PIC);
break;
}
}
/**
* 由于我们是使用startActivityForResult()方法来启动某活动,
* 在某活动被销毁之后会回调上一个活动的onActivityResult()方法
* requestCode区分启动不同的活动,resultCode判断处理结果是否成功
*/
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
switch (requestCode) {
case PHOTO_PIC:
String result = data.getExtras().getString("result");
mTextView.setText(result);
mImageView.setImageBitmap((Bitmap) data.getParcelableExtra("bitmap"));
break;
}
}
}
}
manifest的配置部分(注意包名改变了)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.zxingdemo"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="21" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.<span style="color:#ff0000;">example.zxingdemo</span>.CaptureActivity"></activity>
</application>
</manifest>