Android自由行之走进zxing,轻松实现二维码扫描

 

     现在很多App都集成了扫一扫功能,最常用的微信、QQ、手机助手等。二维码也使得生活变得更加简洁,扫一扫订餐、扫一扫下载等等。那么,说到二维码,我们不得不提Google一个开源的扫码框架:zxing。

     开源下载:http://code.google.com/p/zxing/

     zxing是基于多种1D/2D条码处理的开源库,是一个完整的项目。它可以通过手机摄像头实现条码的扫描以及解码,功能及其强大。那么如果要实现二维码的扫描以及解码,我们需要在该开源项目的基础上进行简化,并修改。让我们来看一下

二维码扫描结果      

    上图是仿照QQ的扫一扫进行修改的zxing项目,以zxing项目为基础,结合实际应用,这里作了三点改变:

   (1)竖屏扫描

   (2)自定义取景框

   (3)重新定义扫描结果处理

 

     一、第一步:下载zxing项目,并简化出扫描框架

     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、新建工程,添加如下权限:  

<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" />

 

permission

        添加core.jar文件,并BuildPath;将上述类或包加入工程后,会报一系列错误,原因有几点:

       (1)资源文件缺乏,将zxing下需要的资源文件copy到新工程下

       (2)版本兼容问题,zxing下很多技术都是使用4.0版本及以上,集成到低版本之后,须做相应改动,详情参照项目源码

       (3)包结构引用问题,需要重新导入包引用

   4、简化CapyureActivity, camera包以及decode各类异常解决以后,即可对CaptureActivity进行代码简化 ,首先看一下capture.xml布局 

 <merge xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools" >

     <!-- 整体透明画布 -->
     <SurfaceView
     android:id="@+id/preview_view"
     android:layout_width="fill_parent" android:layout_height="fill_parent" />

     <!-- 扫描取景框 -->
    <com.karics.library.zxing.view.ViewfinderView
    android:id="@+id/viewfinder_view"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" />

    <!-- 标题栏 -->
    <RelativeLayout
    android:layout_width="fill_parent"
    android:layout_height="50dp"
    android:layout_gravity="top"
    android:background="#99000000">

    <ImageButton
    android:id="@+id/capture_imageview_back"
    android:layout_width="42dp"
    android:layout_height="42dp"
    android:layout_centerVertical="true"
    android:background="@drawable/selector_capture_back"/>

    <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:textColor="#ffffffff"
    android:textSize="20sp"
    android:text="扫一扫"/>

    </RelativeLayout>

</merge>

        capture.xml布局去掉结果显示,添加标题栏。那么captureActivity中,onCreate(),onPause(),onResume(),onDestroy涉及到Camera的初始化或销毁  

 @Override
 public void onCreate(Bundle icicle) {
	super.onCreate(icicle);
	// 保持Activity处于唤醒状态
	Window window = getWindow();
	window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
	setContentView(R.layout.capture);
	hasSurface = false;
	inactivityTimer = new InactivityTimer(this);
	beepManager = new BeepManager(this);

	imageButton_back = (ImageButton) findViewById(R.id.capture_imageview_back);
	imageButton_back.setOnClickListener(new View.OnClickListener() {
		@Override
		public void onClick(View v) {
			finish();
		}
	});
 }

 @Override
 protected void onResume() {
	super.onResume();

	// CameraManager必须在这里初始化,而不是在onCreate()中。
	// 这是必须的,因为当我们第一次进入时需要显示帮助页,我们并不想打开Camera,测量屏幕大小
	// 当扫描框的尺寸不正确时会出现bug
	cameraManager = new CameraManager(getApplication());

	viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);
	viewfinderView.setCameraManager(cameraManager);

	handler = null;

	SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
	SurfaceHolder surfaceHolder = surfaceView.getHolder();
	if (hasSurface) {
		// activity在paused时但不会stopped,因此surface仍旧存在;
		// surfaceCreated()不会调用,因此在这里初始化camera
		initCamera(surfaceHolder);
	} else {
		// 重置callback,等待surfaceCreated()来初始化camera
		surfaceHolder.addCallback(this);
	}

	beepManager.updatePrefs();
	inactivityTimer.onResume();

	source = IntentSource.NONE;
	decodeFormats = null;
	characterSet = null;
 }

 @Override
 protected void onPause() {
	if (handler != null) {
		handler.quitSynchronously();
		handler = null;
	}
	inactivityTimer.onPause();
	beepManager.close();
	cameraManager.closeDriver();
	if (!hasSurface) {
		SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
		SurfaceHolder surfaceHolder = surfaceView.getHolder();
		surfaceHolder.removeCallback(this);
	}
	super.onPause();
 }

 @Override
 protected void onDestroy() {
	inactivityTimer.shutdown();
	super.onDestroy();
 }

        surfaceview是基于Camera而实现,surfaceview的使用需要实现SurfaceHolder.Callback接口,在开启屏幕surfaceview的时候初始化camera

 @Override
 public void surfaceCreated(SurfaceHolder holder) {

     if (!hasSurface) {
         hasSurface = true;
         initCamera(holder);

         }
     }

     @Override
     public void surfaceDestroyed(SurfaceHolder holder) {
         hasSurface = false;
     }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

     }

 

        接下来看如何初始化Camera,代码简化之后如下

 private void initCamera(SurfaceHolder surfaceHolder) {
	if (surfaceHolder == null) {
		hrow new IllegalStateException("No SurfaceHolder provided");
	}
	if (cameraManager.isOpen()) {
		return;
	}
	try {
		// 打开Camera硬件设备
		cameraManager.openDriver(surfaceHolder);
		// 创建一个handler来打开预览,并抛出一个运行时异常
		if (handler == null) {
			handler = new CaptureActivityHandler(this, decodeFormats,
			decodeHints, characterSet, cameraManager);
		}
	} catch (IOException ioe) {
		Log.w(TAG, ioe);
		displayFrameworkBugMessageAndExit();
	} catch (RuntimeException e) {
		Log.w(TAG, "Unexpected error initializing camera", e);
		displayFrameworkBugMessageAndExit();
	}
 }

       在CaptureActivity中,有一个核心方法,用来返回并处理解码结果,也即扫描结果。handleDecode(),如果需要对解码后的内容进行自己的处理,需要对该方法进行改动,这里修改        为将解码的bitmap以及内容回传到开启扫描的Activiity进行处理。

 public void handleDecode(Result rawResult, Bitmap barcode, float scaleFactor) {
	inactivityTimer.onActivity();

	boolean fromLiveScan = barcode != null;
	//这里处理解码完成后的结果,此处将参数回传到Activity处理
	if (fromLiveScan) {
		beepManager.playBeepSoundAndVibrate();
		Toast.makeText(this, "扫描成功", Toast.LENGTH_SHORT).show();
		Intent intent = getIntent();
		intent.putExtra("codedContent", rawResult.getText());
		intent.putExtra("codedBitmap", barcode);
		setResult(RESULT_OK, intent);
		finish();
	}

 }

   5、将指定Url生成二维码

 /**
 * 生成QRCode(二维码)
 *
 * @param str
 * @return
 * @throws WriterException
 */
 public static Bitmap createQRCode(String url) throws WriterException {

	if (url == null || url.equals("")) {
	return null;
	}

	// 生成二维矩阵,编码时指定大小,不要生成了图片以后再进行缩放,这样会模糊导致识别失败
	BitMatrix matrix = new MultiFormatWriter().encode(url,
	BarcodeFormat.QR_CODE, 300, 300);

	int width = matrix.getWidth();
	int height = matrix.getHeight();

	// 二维矩阵转为一维像素数组,也就是一直横着排了
	int[] pixels = new int[width * height];

	for (int y = 0; y < height; y++) {
		for (int x = 0; x < width; x++) {
			if (matrix.get(x, y)) {
				pixels[y * width + x] = 0xff000000;
			}

		}
	}

	Bitmap bitmap = Bitmap.createBitmap(width, height,
	Bitmap.Config.ARGB_8888);
	bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
	return bitmap;
 }

       通过以上四步,zxing项目的简化工作基本完成。至于一些类需要进行小修小改,希望可以对着源码进行,这里不再赘述。二维码扫描的整体构架主要包含三部分:

       1、定义取景框,也即扫描的View,通过surfaceview进行绘制

       2、Camera, 扫描的核心在于camera的配置使用,包括预览,自动聚焦,打开设备等处理

       3、Decode解码,扫描完成后整个工程的核心

       除去以上三个模块,需要明确的就是CaptureActivitiy中handleDeCode()方法做自己的处理。

   

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值