Android開發(fā)框架之自定義ZXing二維碼掃描界面并解決取景框拉伸問(wèn)題
先給大家展示下效果圖:

掃描內(nèi)容是下面這張,二維碼是用zxing庫(kù)生成的

由于改了好幾個(gè)類,還是去年的事都忘得差不多了,所以只能上這個(gè)類的代碼了,主要就是改了這個(gè)CaptureActivity.java
package com.zxing.activity;
import java.io.IOException;
import java.util.Vector;
import android.app.Activity;
import android.content.Intent;
import android.content.res.AssetFileDescriptor;
import android.graphics.Bitmap;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Handler;
import android.os.Vibrator;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.widget.Toast;
import com.ericssonlabs.R;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.Result;
import com.zxing.camera.CameraManager;
import com.zxing.decoding.CaptureActivityHandler;
import com.zxing.decoding.CaptureActivityHandler.DecodeCallback;
import com.zxing.decoding.InactivityTimer;
import com.zxing.view.ViewfinderView;
/**
* Initial the camera
* @author Ryan.Tang
* @modifier Lemon
* @use extends CaptureActivity并且在setContentView方法后調(diào)用init方法
*/
public abstract class CaptureActivity extends Activity implements Callback, DecodeCallback {
// private static final String TAG = "CaptureActivity";
protected Activity context;
protected SurfaceView surfaceView;
protected ViewfinderView viewfinderView;
/**初始化,必須在setContentView之后
* @param context
* @param viewfinderView
*/
protected void init(Activity context, SurfaceView surfaceView, ViewfinderView viewfinderView) {
this.context = context;
this.surfaceView = surfaceView;
this.viewfinderView = viewfinderView;
CameraManager.init(getApplication());
hasSurface = false;
inactivityTimer = new InactivityTimer(this);
}
private CaptureActivityHandler handler;
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 = .f;
private boolean vibrate;
@Override
protected void onResume() {
super.onResume();
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;
}
@Override
protected void onPause() {
super.onPause();
if (handler != null) {
handler.quitSynchronously();
handler = null;
}
CameraManager.get().closeDriver();
}
@Override
protected void onDestroy() {
inactivityTimer.shutdown();
super.onDestroy();
}
public static final String RESULT_QRCODE_STRING = "RESULT_QRCODE_STRING";
/**
* Handler scan result
* @param result
* @param barcode
*/
public void handleDecode(Result result, Bitmap barcode) {
inactivityTimer.onActivity();
playBeepSoundAndVibrate();
String resultString = result.getText();
//FIXME
if (resultString.equals("")) {
Toast.makeText(CaptureActivity.this, "Scan failed!", Toast.LENGTH_SHORT).show();
}
setResult(RESULT_OK, new Intent().putExtra(RESULT_QRCODE_STRING, resultString));
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, viewfinderView, this);
}
}
@Override
public void drawViewfinder() {
viewfinderView.drawViewfinder();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (!hasSurface) {
hasSurface = true;
initCamera(holder);
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
hasSurface = false;
}
public Handler getHandler() {
return handler;
}
private void initBeepSound() {
if (playBeep && mediaPlayer == null) {
// The volume on STREAM_SYSTEM is not adjustable, and users found it
// too loud,
// so we now play on the music stream.
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 = L;
private void playBeepSoundAndVibrate() {
if (playBeep && mediaPlayer != null) {
mediaPlayer.start();
}
if (vibrate) {
Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
vibrator.vibrate(VIBRATE_DURATION);
}
}
/**
* When the beep has finished playing, rewind to queue up another one.
*/
private final OnCompletionListener beepListener = new OnCompletionListener() {
public void onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.seekTo();
}
};
}
使用方法: 新建一個(gè)Activity繼承CaptureActivity并且在setContentView方法后調(diào)用init方法即可。
示例:
CameraScanActivity.java
package zuo.biao.activity;
import zuo.biao.R;
import zuo.biao.library.interfaces.OnBottomDragListener;
import zuo.biao.util.ActivityUtil;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import com.zxing.activity.CaptureActivity;
import com.zxing.camera.CameraManager;
import com.zxing.view.ViewfinderView;
/**掃描二維碼Activity
* @author Lemon
* @use 參考zuo.biao.library.ModelActivity
*/
public class CameraScanActivity extends CaptureActivity implements Callback, OnClickListener, OnBottomDragListener {
public static final String TAG = "CameraScanActivity";
//啟動(dòng)方法<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
/**啟動(dòng)這個(gè)Activity的Intent
* @param context
* @param title
* @return
*/
public static Intent createIntent(Context context) {
return new Intent(context, CameraScanActivity.class);
}
//啟動(dòng)方法>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.camera_scan_activity);
init(this, (SurfaceView) findViewById(R.id.svCameraScan), (ViewfinderView) findViewById(R.id.vfvCameraScan));
//功能歸類分區(qū)方法,必須調(diào)用<<<<<<<<<<
initView();
initData();
initListener();
//功能歸類分區(qū)方法,必須調(diào)用>>>>>>>>>>
}
//UI顯示區(qū)(操作UI,但不存在數(shù)據(jù)獲取或處理代碼,也不存在事件監(jiān)聽代碼)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
public void initView() {//必須調(diào)用
}
//UI顯示區(qū)(操作UI,但不存在數(shù)據(jù)獲取或處理代碼,也不存在事件監(jiān)聽代碼)>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//data數(shù)據(jù)區(qū)(存在數(shù)據(jù)獲取或處理代碼,但不存在事件監(jiān)聽代碼)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
public void initData() {//必須調(diào)用
}
//data數(shù)據(jù)區(qū)(存在數(shù)據(jù)獲取或處理代碼,但不存在事件監(jiān)聽代碼)>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//listener事件監(jiān)聽區(qū)(只要存在事件監(jiān)聽代碼就是)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
public void initListener() {//必須調(diào)用
findViewById(R.id.tvCameraScanReturn).setOnClickListener(this);
findViewById(R.id.ivCameraScanReturn).setOnClickListener(this);
findViewById(R.id.ivCameraScanLight).setOnClickListener(this);
findViewById(R.id.ivCameraScanMyQRCode).setOnClickListener(this);
}
//系統(tǒng)自帶監(jiān)聽方法<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
@Override
public void onDragBottom(boolean rightToLeft) {
if (rightToLeft) {
return;
}
finish();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.tvCameraScanReturn:
case R.id.ivCameraScanReturn:
onDragBottom(false);
break;
case R.id.ivCameraScanLight:
switchLight(! isOpen);
break;
case R.id.ivCameraScanMyQRCode:
//
break;
default:
break;
}
}
private boolean isOpen = false;
/**打開或關(guān)閉閃關(guān)燈
* @param open
*/
private void switchLight(boolean open) {
if (open == isOpen) {
return;
}
isOpen = CameraManager.get().switchLight(open);
}
//類相關(guān)監(jiān)聽<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
//類相關(guān)監(jiān)聽>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//系統(tǒng)自帶監(jiān)聽方法>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//listener事件監(jiān)聽區(qū)(只要存在事件監(jiān)聽代碼就是)>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//內(nèi)部類,盡量少用<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
//內(nèi)部類,盡量少用>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
}
CameraScanActivity布局文件camera_scan_activity.xml
<?xml version="." encoding="utf-"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" style="@style/activity_page" > <SurfaceView android:id="@+id/svCameraScan" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" /> <!-- 必須在最底層,且不能指定寬高,否則掃描讀取很難實(shí)現(xiàn) --> <com.zxing.view.ViewfinderView android:id="@+id/vfvCameraScan" android:layout_width="match_parent" android:layout_height="match_parent" /> <LinearLayout style="@style/ll_vertical_match_match" android:baselineAligned="false" > <RelativeLayout style="@style/topbar_bg" android:background="@color/white_alpha" > <TextView android:id="@+id/tvCameraScanReturn" style="@style/topbar_left_btn" android:text=" " /> <TextView style="@style/topbar_title" android:layout_centerHorizontal="true" android:text="掃一掃" /> </RelativeLayout> <LinearLayout style="@style/ll_vertical_match_match" android:layout_gravity="center_horizontal" android:layout_weight="" > <TextView style="@style/text_middle_white" android:layout_margin="dp" android:text="@string/camera_scan_remind" /> </LinearLayout> <LinearLayout style="@style/ll_horizontal_match_wrap" android:layout_gravity="bottom" android:background="@color/white_alpha" android:gravity="center" > <LinearLayout style="@style/ll_vertical_wrap_wrap" android:layout_margin="dp" android:paddingBottom="dp" android:paddingLeft="dp" android:paddingRight="dp" android:paddingTop="dp" > <ImageView android:id="@+id/ivCameraScanReturn" style="@style/wrap_wrap" android:background="@drawable/cilcle_gray_to_white" android:padding="dp" android:src="@drawable/back_black_light" /> <TextView style="@style/text_small" android:layout_marginTop="dp" android:text="返回" /> </LinearLayout> <LinearLayout style="@style/ll_vertical_wrap_wrap" android:layout_margin="dp" android:paddingBottom="dp" android:paddingLeft="dp" android:paddingRight="dp" android:paddingTop="dp" > <ImageView android:id="@+id/ivCameraScanLight" style="@style/wrap_wrap" android:background="@drawable/cilcle_gray_to_white" android:padding="dp" android:src="@drawable/flash_light" /> <TextView style="@style/text_small" android:layout_marginTop="dp" android:text="開燈/關(guān)燈" /> </LinearLayout> <LinearLayout style="@style/ll_vertical_wrap_wrap" android:layout_margin="dp" android:paddingBottom="dp" android:paddingLeft="dp" android:paddingRight="dp" android:paddingTop="dp" > <ImageView android:id="@+id/ivCameraScanMyQRCode" style="@style/wrap_wrap" android:background="@drawable/cilcle_gray_to_white" android:padding="dp" android:src="@drawable/qrcode" /> <TextView style="@style/text_small" android:layout_marginTop="dp" android:text="我的名片" /> </LinearLayout> </LinearLayout> </LinearLayout> </FrameLayout>
布局文件因?yàn)槭褂昧薢BLibrary中的一些style,color等,只有這個(gè)layout的話會(huì)出現(xiàn)一些錯(cuò)誤。自己新建一個(gè)layout文件并把ViewfinderView放到最外層布局文件內(nèi)就行了。當(dāng)然下載好下面附上的源碼就沒這問(wèn)題了。
對(duì)了,記得在AndroidManifest.xml中加上這些權(quán)限:
<uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" /> <uses-permission android:name="android.permission.FLASHLIGHT" />
附源碼(含使用方法)

GitHub源碼 https://github.com/TommyLemon/Android-ZBLibrary
開源中國(guó)源碼 http://git.oschina.net/Lemon19950301/Android-ZBLibrary
關(guān)于Android開發(fā)框架之自定義ZXing二維碼掃描界面并解決取景框拉伸問(wèn)題就給大家介紹到這里,以上內(nèi)容有點(diǎn)長(zhǎng),希望大家能耐心看完,有任何問(wèn)題歡迎給我留言,小編會(huì)及時(shí)回復(fù)大家的,在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- Android實(shí)現(xiàn)二維碼掃描和生成的簡(jiǎn)單方法
- Android中掃描多媒體文件操作詳解
- Android音樂(lè)播放器制作 掃描本地音樂(lè)顯示在手機(jī)(一)
- Android設(shè)備獲取掃碼槍掃描的內(nèi)容與可能遇到的問(wèn)題解決
- Android編程實(shí)現(xiàn)wifi掃描及連接的方法
- Android studio 實(shí)現(xiàn)手機(jī)掃描二維碼功能
- Android應(yīng)用中使用ContentProvider掃描本地圖片并顯示
- Android 二維碼掃描和生成二維碼功能
- 詳解Android 掃描條形碼(Zxing插件)
- Android自定義View實(shí)現(xiàn)掃描效果
相關(guān)文章
Android編程設(shè)計(jì)模式之模板方法模式詳解
這篇文章主要介紹了Android編程設(shè)計(jì)模式之模板方法模式,結(jié)合實(shí)例形式詳細(xì)分析了Android模板方法模式的概念、功能、使用場(chǎng)景、用法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2017-12-12
Android中關(guān)于屏幕的三個(gè)小眾知識(shí)(寬屏適配、禁止截屏和保持屏幕常亮)
這篇文章主要給大家介紹了Android中關(guān)于屏幕的三個(gè)小眾知識(shí),分別是寬屏適配、禁止截屏和保持屏幕常亮的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們可以參考學(xué)習(xí),下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-12-12
Android開發(fā)自定義實(shí)時(shí)圖表控件實(shí)現(xiàn)示例
這篇文章主要為大家介紹了Android自定義實(shí)時(shí)圖表控件實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
一文詳解Android IntentService的開發(fā)技巧
Android應(yīng)用開發(fā)中,執(zhí)行后臺(tái)任務(wù)是常見需求之一,其中,IntentService是一種強(qiáng)大的工具,可以輕松管理異步任務(wù),而無(wú)需擔(dān)心線程管理和生命周期問(wèn)題,本文將深入探討 IntentService 的各個(gè)方面,需要的朋友可以參考下2023-11-11
Android4.4下MediaProvider無(wú)法向外置SD卡中文件寫數(shù)據(jù)的解決方法
這篇文章主要介紹了Android4.4下MediaProvider無(wú)法向外置SD卡中文件寫數(shù)據(jù)的解決方法,實(shí)例分析了Android4.4下針對(duì)讀寫限制的修改技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10
Android學(xué)習(xí)筆記之應(yīng)用單元測(cè)試實(shí)例分析
這篇文章主要介紹了Android學(xué)習(xí)筆記之應(yīng)用單元測(cè)試,結(jié)合實(shí)例形式較為詳細(xì)的分析了Android單元測(cè)試的實(shí)現(xiàn)原理與具體步驟,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11

