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


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

三、具體實現
1、創建MainActivity用于跳轉到掃描頁面
/** * 二維碼掃描 * @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、掃描頁面
/** * 條形碼掃描 * * @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,就是我們看見的拍攝時中間的框框了 */ private ViewfinderView mFindView; /** 解碼處理類,負責調用另外的線程進行解碼。 */ private CaptureActivityHandler mHandler; /** 判斷是否創建了SurfaceView */ private boolean hasSurface; /** decodeFormats 條形碼,二維碼等的集合 */ private Vector<BarcodeFormat> mDecodeFormats; /** 字符編碼 */ private String characterSet; /** 定時器 */ private InactivityTimer inactivityTimer; /** 媒體播放器 */ private MediaPlayer mediaPlayer; /** 是否播放聲音 */ private boolean playBeep; /** 是否震動 */ 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); } /** * 暫停后恢復時處理內容 */ @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); // 如果當前是鈴音模式,則繼續準備下面的 蜂鳴提示音操作,如果是靜音或者震動模式。就不要繼續了。因為用戶選擇了無聲的模式,我們就也不要出聲了。 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; } // 關閉攝像頭信息 CameraManager.get().closeDriver(); } @Override protected void onDestroy() { // 關閉定時器 inactivityTimer.shutdown(); super.onDestroy(); } /** * 處理掃描結果 * * @param result * @param barcode */ public void handleDecode(Result result, Bitmap barcode) { // 添加定時器 inactivityTimer.onActivity(); // 響鈴和震動 playBeepSoundAndVibrate(); String resultString = result.getText(); if (TextUtils.isEmpty(resultString)) { ToastUtil.show(this, "掃描失敗"); ScanActivity.this.finish(); } else { //掃描完成后傳遞結果 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) { } /** * 在視圖創建的時候初始化攝像頭 */ @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() { // 如果要播放聲音并且沒有播放器時 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); // 第一個參數,指代一個震動的頻率數組。每兩個為一組,每組的第一個為等待時間,第二個為震動時間。 // 比如 [2000,500,100,400],會先等待2000毫秒,震動500,再等待100,震動400 // 第二個參數,repest指代從 第幾個索引(第一個數組參數) 的位置開始循環震動。 // 會一直保持循環,我們需要用 vibrator.cancel()主動終止 // vibrator.vibrate(new long[]{300,500},0); } } /** * 注冊事件。當播放完畢一次后,重新指向流文件的開頭,以準備下次播放。 */ 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、結果頁面
/** * * @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); } } }); //設置圖片信息 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); }}四、代碼下載
完整實例代碼點擊此處本站下載。
PS:這里再為大家推薦一款二維碼在線生成工具供大家參考使用:
在線生成二維碼工具(加強版)
http://tools.VeVB.COm/transcoding/jb51qrcode
更多關于Android相關內容感興趣的讀者可查看本站專題:《Android開發入門與進階教程》、《Android數據庫操作技巧總結》、《Android編程之activity操作技巧總結》、《Android文件操作技巧匯總》、《Android資源操作技巧匯總》、《Android視圖View技巧總結》及《Android控件用法總結》
希望本文所述對大家Android程序設計有所幫助。
新聞熱點
疑難解答