Android開發(fā)實(shí)現(xiàn)模仿360二維碼掃描功能實(shí)例詳解
本文實(shí)例講述了Android開發(fā)實(shí)現(xiàn)模仿360二維碼掃描功能的方法。分享給大家供大家參考,具體如下:
一、效果圖:


二、框架搭建
1、首先,下載最新zxing開源項(xiàng)目。 下載地址:http://code.google.com/p/zxing/
或 點(diǎn)擊此處本站下載。
2、分析項(xiàng)目結(jié)構(gòu),明確掃描框架需求。在zxing中,有很多其他的功能,項(xiàng)目結(jié)構(gòu)比較復(fù)雜;針對(duì)二維碼QRCode掃描,我們需要幾個(gè)包:
(1)com.google.zxing.client.android.Camera 基于Camera調(diào)用以及參數(shù)配置,核心包
(2)DecodeFormatManager、DecodeThread、DecodeHandler 基于解碼格式、解碼線程、解碼結(jié)果處理的解碼類
(3)ViewfinderView、ViewfinderResultPointCallBack 基于取景框視圖定義的View類
(4)CaptureActivity、CaptureActivityHandler 基于掃描Activity以及掃描結(jié)果處理的Capture類
(5)InactivityTimer、BeepManager、FinishListener 基于休眠、聲音、退出的輔助管理類
(6)Intents、IntentSource、PrefrencesActivity 基于常量存儲(chǔ)的常量類
3、新建工程,添加如下權(quán)限permission:
<uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.FLASHLIGHT" />
4、 添加core.jar文件,并BuildPath;將上述類或包加入工程后,會(huì)報(bào)一系列錯(cuò)誤,原因有幾點(diǎn):
(1)資源文件缺乏,將zxing下需要的資源文件copy到新工程下
(2)版本兼容問題,zxing下很多技術(shù)都是使用4.0版本及以上,集成到低版本之后,須做相應(yīng)改動(dòng),詳情參照項(xiàng)目源碼
(3)包結(jié)構(gòu)引用問題,需要重新導(dǎo)入包引用
5、最后框架

三、具體實(shí)現(xiàn)
1、創(chuàng)建MainActivity用于跳轉(zhuǎn)到掃描頁(yè)面
/**
* 二維碼掃描
* @Project App_ZXing
* @Package com.android.scan
* @author chenlin
* @version 1.0
* @Date 2014年3月6日
*/
public class MainActivity extends Activity {
private Button mBtnScan;
private Button mBtnBack;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtnBack = (Button) findViewById(R.id.btn_back);
mBtnBack.setVisibility(View.GONE);
mBtnScan = (Button) findViewById(R.id.btn_scan);
mBtnScan.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, ScanActivity.class));
}
});
}
}
布局文件
<?xml version="1.0" encoding="utf-8"?>
<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/btn_scan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textColor="@color/white"
android:padding="20dp"
android:background="@drawable/btn_scan_result"
android:text="掃一掃" />
<include
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
layout="@layout/activity_scan_title" />
</RelativeLayout>
2、掃描頁(yè)面
/**
* 條形碼掃描
*
* @Project App_ZXing
* @Package com.android.scan
* @author chenlin
* @version 1.0
* @Date 2014年3月6日
*/
public class ScanActivity extends Activity implements Callback {
private static final float BEEP_VOLUME = 0.10f;
protected static final String RESULT_TEXT = "result";
protected static final String RESULT_BITMAP = "bitmap";
/** 掃描界面 */
private SurfaceView mSurfaceView;
/** 自定義的View,就是我們看見的拍攝時(shí)中間的框框了 */
private ViewfinderView mFindView;
/** 解碼處理類,負(fù)責(zé)調(diào)用另外的線程進(jìn)行解碼。 */
private CaptureActivityHandler mHandler;
/** 判斷是否創(chuàng)建了SurfaceView */
private boolean hasSurface;
/** decodeFormats 條形碼,二維碼等的集合 */
private Vector<BarcodeFormat> mDecodeFormats;
/** 字符編碼 */
private String characterSet;
/** 定時(shí)器 */
private InactivityTimer inactivityTimer;
/** 媒體播放器 */
private MediaPlayer mediaPlayer;
/** 是否播放聲音 */
private boolean playBeep;
/** 是否震動(dòng) */
private boolean vibrate;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scan);
init();
}
/**
* 初始化
*/
private void init() {
mSurfaceView = (SurfaceView) findViewById(R.id.preview_view);
mFindView = (ViewfinderView) findViewById(R.id.viewfinder_view);
CameraManager.init(getApplication());
hasSurface = false;
inactivityTimer = new InactivityTimer(this);
}
/**
* 暫停后恢復(fù)時(shí)處理內(nèi)容
*/
@SuppressWarnings("all")
@Override
protected void onResume() {
super.onResume();
// 先重新獲得holder
SurfaceHolder surfaceHolder = mSurfaceView.getHolder();
if (hasSurface) {
initCamera(surfaceHolder);
} else {
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
mDecodeFormats = null;
characterSet = null;
playBeep = true;
AudioManager audioService = (AudioManager) getSystemService(AUDIO_SERVICE);
// 如果當(dāng)前是鈴音模式,則繼續(xù)準(zhǔn)備下面的 蜂鳴提示音操作,如果是靜音或者震動(dòng)模式。就不要繼續(xù)了。因?yàn)橛脩暨x擇了無(wú)聲的模式,我們就也不要出聲了。
if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
playBeep = false;
}
// 初始化播放聲音
initBeepSound();
//播放
vibrate = true;
}
@Override
protected void onPause() {
super.onPause();
if (mHandler != null) {
mHandler.quitSynchronously();
mHandler = null;
}
// 關(guān)閉攝像頭信息
CameraManager.get().closeDriver();
}
@Override
protected void onDestroy() {
// 關(guān)閉定時(shí)器
inactivityTimer.shutdown();
super.onDestroy();
}
/**
* 處理掃描結(jié)果
*
* @param result
* @param barcode
*/
public void handleDecode(Result result, Bitmap barcode) {
// 添加定時(shí)器
inactivityTimer.onActivity();
// 響鈴和震動(dòng)
playBeepSoundAndVibrate();
String resultString = result.getText();
if (TextUtils.isEmpty(resultString)) {
ToastUtil.show(this, "掃描失敗");
ScanActivity.this.finish();
} else {
//掃描完成后傳遞結(jié)果
Intent resultIntent = new Intent();
resultIntent.setClass(ScanActivity.this, ScanResultActivity.class);
resultIntent.putExtra(RESULT_TEXT, resultString);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
barcode.compress(Bitmap.CompressFormat.PNG, 100, baos);
byte[] bitmapByte = baos.toByteArray();
resultIntent.putExtra(RESULT_BITMAP, bitmapByte);
startActivity(resultIntent);
overridePendingTransition(R.anim.activity_in_from_rigth, R.anim.activity_out_to_scale);
ScanActivity.this.finish();
}
}
private void initCamera(SurfaceHolder surfaceHolder) {
try {
CameraManager.get().openDriver(surfaceHolder);
} catch (IOException ioe) {
return;
} catch (RuntimeException e) {
return;
}
if (mHandler == null) {
mHandler = new CaptureActivityHandler(this, mDecodeFormats, characterSet);
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
/**
* 在視圖創(chuàng)建的時(shí)候初始化攝像頭
*/
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (!hasSurface) {
hasSurface = true;
initCamera(holder);
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
hasSurface = false;
}
public ViewfinderView getViewfinderView() {
return mFindView;
}
public Handler getHandler() {
return mHandler;
}
public void drawViewfinder() {
mFindView.drawViewfinder();
}
/**
* 初始化聲音資源
*/
private void initBeepSound() {
// 如果要播放聲音并且沒有播放器時(shí)
if (playBeep && mediaPlayer == null) {
// 設(shè)置聲道流格式為音樂
setVolumeControlStream(AudioManager.STREAM_MUSIC);
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
// 設(shè)置聲音完成后監(jiān)聽
mediaPlayer.setOnCompletionListener(beepListener);
// 設(shè)定數(shù)據(jù)源,并準(zhǔn)備播放
AssetFileDescriptor file = getResources().openRawResourceFd(R.raw.beep);
try {
mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength());
file.close();
mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);// 設(shè)置音量
mediaPlayer.prepare();
} catch (IOException e) {
mediaPlayer = null;
}
}
}
private static final long VIBRATE_DURATION = 200L;
/**
* 響鈴和震動(dòng)
*/
private void playBeepSoundAndVibrate() {
if (playBeep && mediaPlayer != null) {
mediaPlayer.start();
}
if (vibrate) {
Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
// 震動(dòng)一次
vibrator.vibrate(VIBRATE_DURATION);
// 第一個(gè)參數(shù),指代一個(gè)震動(dòng)的頻率數(shù)組。每?jī)蓚€(gè)為一組,每組的第一個(gè)為等待時(shí)間,第二個(gè)為震動(dòng)時(shí)間。
// 比如 [2000,500,100,400],會(huì)先等待2000毫秒,震動(dòng)500,再等待100,震動(dòng)400
// 第二個(gè)參數(shù),repest指代從 第幾個(gè)索引(第一個(gè)數(shù)組參數(shù)) 的位置開始循環(huán)震動(dòng)。
// 會(huì)一直保持循環(huán),我們需要用 vibrator.cancel()主動(dòng)終止
// vibrator.vibrate(new long[]{300,500},0);
}
}
/**
* 注冊(cè)事件。當(dāng)播放完畢一次后,重新指向流文件的開頭,以準(zhǔn)備下次播放。
*/
private final MediaPlayer.OnCompletionListener beepListener = new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.seekTo(0);
}
};
}
布局文件
<?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="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center" />
<com.android.scan.view.ViewfinderView
android:id="@+id/viewfinder_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<include
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
layout="@layout/activity_scan_title" />
</RelativeLayout>
</FrameLayout>
3、結(jié)果頁(yè)面
/**
*
* @Project App_ZXing
* @Package com.android.scan
* @author chenlin
* @version 1.0
* @Date 2014年3月6日
* @Note TODO
*/
public class ScanResultActivity extends Activity {
private EditText mEtScan;
private Button mBtnBack;
private Button mBtnCopy;
private ImageView mImageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scan_result);
initViews();
init();
}
private void init() {
final String result = getIntent().getStringExtra(ScanActivity.RESULT_TEXT);
mEtScan.setText(result);
final ClipboardManager cm = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
mBtnBack.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ScanResultActivity.this.finish();
}
});
mBtnCopy.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (result!=null) {
ClipData myClip = ClipData.newPlainText("text", result);
cm.setPrimaryClip(myClip);
}
}
});
//設(shè)置圖片信息
byte[] imgByte = getIntent().getByteArrayExtra(ScanActivity.RESULT_BITMAP);
if (imgByte != null) {
Drawable drawable = BitmapUtil.byte2Drawable(imgByte);
if (drawable != null) {
mImageView.setImageDrawable(drawable);
}
}
}
// public void paste(View view){
// ClipData abc = myClipboard.getPrimaryClip();
// ClipData.Item item = abc.getItemAt(0);
// String text = item.getText().toString();
// pasteField.setText(text);
// Toast.makeText(getApplicationContext(), "Text Pasted",
// Toast.LENGTH_SHORT).show();
// }
private void initViews() {
mEtScan = (EditText) findViewById(R.id.et_scan_result);
mBtnBack = (Button) findViewById(R.id.btn_back);
mBtnCopy = (Button) findViewById(R.id.btn_copy);
}
}
四、代碼下載
完整實(shí)例代碼點(diǎn)擊此處本站下載。
PS:這里再為大家推薦一款二維碼在線生成工具供大家參考使用:
在線生成二維碼工具(加強(qiáng)版)
http://tools.jb51.net/transcoding/jb51qrcode
更多關(guān)于Android相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Android開發(fā)入門與進(jìn)階教程》、《Android數(shù)據(jù)庫(kù)操作技巧總結(jié)》、《Android編程之a(chǎn)ctivity操作技巧總結(jié)》、《Android文件操作技巧匯總》、《Android資源操作技巧匯總》、《Android視圖View技巧總結(jié)》及《Android控件用法總結(jié)》
希望本文所述對(duì)大家Android程序設(shè)計(jì)有所幫助。
相關(guān)文章
Android金額輸入框只允許輸入小數(shù)點(diǎn)后兩位效果
實(shí)現(xiàn)android 金額輸入框輸入小數(shù)點(diǎn)后兩位的效果也不是很復(fù)雜,只需要設(shè)置輸入框輸入的字符類型、設(shè)置InputFilter、設(shè)置輸入變化監(jiān)聽即可。這篇文章主要介紹了Android金額輸入框只允許輸入小數(shù)點(diǎn)后兩位 ,需要的朋友可以參考下2017-05-05
Flutter Http分塊下載與斷點(diǎn)續(xù)傳的實(shí)現(xiàn)
這篇文章主要介紹了Flutter Http分塊下載與斷點(diǎn)續(xù)傳的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03
功能強(qiáng)大的Android滾動(dòng)控件RecyclerView
這篇文章主要為大家詳細(xì)介紹了功能強(qiáng)大的Android滾動(dòng)控件RecyclerView,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08
Android開發(fā)中計(jì)算器的sin、cos及tan值計(jì)算問題分析
這篇文章主要介紹了Android開發(fā)中計(jì)算器的sin、cos及tan值計(jì)算問題,結(jié)合實(shí)例形式分析了Android三角函數(shù)運(yùn)算中的弧度與角度計(jì)算問題與相關(guān)解決方法,需要的朋友可以參考下2017-11-11
Android幀率監(jiān)測(cè)與優(yōu)化技巧
Android 應(yīng)用的性能優(yōu)化是開發(fā)過程中至關(guān)重要的一環(huán),而幀率(Frame Rate)是評(píng)估應(yīng)用性能的一個(gè)關(guān)鍵指標(biāo),在本文中,我們將深入探討如何監(jiān)測(cè) Android 應(yīng)用的幀率,以及如何通過代碼示例來(lái)優(yōu)化應(yīng)用的性能,需要的朋友可以參考下2023-10-10
Android 實(shí)現(xiàn)列表倒計(jì)時(shí)功能
這篇文章主要介紹了Android 實(shí)現(xiàn)列表倒計(jì)時(shí)功能,代碼很簡(jiǎn)單,沒有任何難度,使用RecyclerView+BaseRecyclerViewAdapterHelper列表實(shí)現(xiàn),需要的朋友可以參考下2020-03-03
Android中使用開源框架eventbus3.0實(shí)現(xiàn)fragment之間的通信交互
本文主要介紹了Android中使用開源框架eventbus3.0實(shí)現(xiàn)fragment之間的通信交互的方法,具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-02-02
Android實(shí)現(xiàn)今日頭條訂閱頻道效果
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)今日頭條訂閱頻道效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01

