Android开发实现模仿360二维码扫描功能实例详解
作者:CharlinGod
本文实例讲述了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.jb51.net/transcoding/jb51qrcode
更多关于Android相关内容感兴趣的读者可查看本站专题:《Android开发入门与进阶教程》、《Android数据库操作技巧总结》、《Android编程之activity操作技巧总结》、《Android文件操作技巧汇总》、《Android资源操作技巧汇总》、《Android视图View技巧总结》及《Android控件用法总结》
希望本文所述对大家Android程序设计有所帮助。