ZXing 二維碼應用

一、導入zxing代碼和包php

二、下面的類是解析二維碼的主要類。html

package com.gaint.nebula.interaction.ui.zxing;

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.Bundle;
import android.os.Handler;
import android.os.Vibrator;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

import com.gaint.nebula.interaction.R;
import com.gaint.nebula.interaction.ui.BaseActivity;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.Result;
import com.mining.app.zxing.camera.CameraManager;
import com.mining.app.zxing.decoding.CaptureActivityHandler;
import com.mining.app.zxing.decoding.InactivityTimer;
import com.mining.app.zxing.view.ViewfinderView;
/**
 * Initial the camera
 * @author Ryan.Tang
 */
public class MipcaActivityCapture extends BaseActivity implements Callback {

	private CaptureActivityHandler handler;
	private ViewfinderView viewfinderView;
	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 = 0.10f;
	private boolean vibrate;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_capture);
		//ViewUtil.addTopView(getApplicationContext(), this, R.string.scan_card);
		CameraManager.init(getApplication());
		viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);
		
		Button mButtonBack = (Button) findViewById(R.id.button_back);
		mButtonBack.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				MipcaActivityCapture.this.finish();
				
			}
		});
		hasSurface = false;
		inactivityTimer = new InactivityTimer(this);
	}

	@Override
	protected void onResume() {
		super.onResume();
		SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
		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();
	}
	
	/**
	 * ����ɨ����
	 * @param result
	 * @param barcode
	 */
	public void handleDecode(Result result, Bitmap barcode) {
		inactivityTimer.onActivity();
		playBeepSoundAndVibrate();
		String resultString = result.getText();
		if (resultString.equals("")) {
			Toast.makeText(MipcaActivityCapture.this, "Scan failed!", Toast.LENGTH_SHORT).show();
		}else {
			Intent resultIntent = new Intent();
			Bundle bundle = new Bundle();
			bundle.putString("result", resultString);
			bundle.putParcelable("bitmap", barcode);
			resultIntent.putExtras(bundle);
			this.setResult(RESULT_OK, resultIntent);
		}
		MipcaActivityCapture.this.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);
		}
	}

	@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 viewfinderView;
	}

	public Handler getHandler() {
		return handler;
	}

	public void drawViewfinder() {
		viewfinderView.drawViewfinder();

	}

	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 = 200L;

	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(0);
		}
	};

}
三、調用此類:

Intent intent = new Intent();
intent.setClass(BaseActivity.this, MipcaActivityCapture.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivityForResult(intent, SCANNIN_GREQUEST_CODE);

四、回傳信息,能夠看到二維碼圖片和內容。

 @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    	if (requestCode == SCANNIN_GREQUEST_CODE) {
    		if (data==null) {
				return;
			}
    		Bitmap mBitmap = data.getParcelableExtra("bitmap");
    		final String result = data.getStringExtra("result");
    		Logger.getLogger().i(result+" -- "+mBitmap);
    		View view = LayoutInflater.from(this).inflate(R.layout.scan, null);
    		ImageView icon = (ImageView) view.findViewById(R.id.iv_scan_icon);
    		TextView textView = (TextView) view.findViewById(R.id.tv_scan_title);
    		icon.setImageBitmap(mBitmap);
    		textView.setText(result);
    		AlertDialog.Builder dialog = new AlertDialog.Builder(this);
    		dialog.setTitle("掃描結果");
    		dialog.setView(view);
    		dialog.setMessage(data.getDataString());
    		dialog.setNegativeButton("肯定", new DialogInterface.OnClickListener() {
    			@Override
    			public void onClick(DialogInterface dialog, int which) {
    				// 用默認瀏覽器打開掃描獲得的地址
    				Intent intent = new Intent();
    				intent.setAction("android.intent.action.VIEW");
    				Uri content_url = Uri.parse(result);
    				intent.setData(content_url);
    				startActivity(intent);
    				dialog.dismiss();
    			}
    		});
    		dialog.setPositiveButton("取消", new DialogInterface.OnClickListener() {
    			@Override
    			public void onClick(DialogInterface dialog, int which) {
    				 dialog.dismiss();
    			}
    		});
    		dialog.create().show();
		}
    	super.onActivityResult(requestCode, resultCode, data);
    }


五、生成二維碼

/**
 * @類功能說明: 生成二維碼圖片示例
 */
public class CreateQRImageTest {
	private ImageView sweepIV;
	private int QR_WIDTH = 200, QR_HEIGHT = 200;

	/**
	 * @方法功能說明: 生成二維碼圖片,實際使用時要初始化sweepIV,否則會報空指針錯誤
	 * @參數: @param url 要轉換的地址或字符串,能夠是中文
	 * @return void
	 * @throws
	 */

	// 要轉換的地址或字符串,能夠是中文
	public void createQRImage(String url) {
		try {
			// 判斷URL合法性
			if (url == null || "".equals(url) || url.length() < 1) {
				return;
			}
			Hashtable<EncodeHintType, String> hints = new Hashtable<EncodeHintType, String>();
			hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
			// 圖像數據轉換,使用了矩陣轉換
			BitMatrix bitMatrix = new QRCodeWriter().encode(url,
					BarcodeFormat.QR_CODE, QR_WIDTH, QR_HEIGHT, hints);
			int[] pixels = new int[QR_WIDTH * QR_HEIGHT];
			// 下面這裏按照二維碼的算法,逐個生成二維碼的圖片,
			// 兩個for循環是圖片橫列掃描的結果
			for (int y = 0; y < QR_HEIGHT; y++) {
				for (int x = 0; x < QR_WIDTH; x++) {
					if (bitMatrix.get(x, y)) {
						pixels[y * QR_WIDTH + x] = 0xff000000;
					} else {
						pixels[y * QR_WIDTH + x] = 0xffffffff;
					}
				}
			}
			// 生成二維碼圖片的格式,使用ARGB_8888
			Bitmap bitmap = Bitmap.createBitmap(QR_WIDTH, QR_HEIGHT,
					Bitmap.Config.ARGB_8888);
			bitmap.setPixels(pixels, 0, QR_WIDTH, 0, 0, QR_WIDTH, QR_HEIGHT);
			// 顯示到一個ImageView上面
			sweepIV.setImageBitmap(bitmap);
		} catch (WriterException e) {
			e.printStackTrace();
		}
	}
}

六、zxing包

http://www.cnblogs.com/weixing/archive/2013/08/28/3287120.html
java

ZXing開源項目Google Code地址:https://code.google.com/p/zxing/android

ZXingDemo下載:ZXingDemo2013-8-25.rar 算法

 

2、修改二維碼代碼canvas

一、是掃描二維碼的文字居中。瀏覽器

找到類ViewfinderView,其中widthPixels是屏幕的寬度。微信

paint.setColor(Color.WHITE);
paint.setTextSize(TEXT_SIZE * density);
paint.setAlpha(0x40);
paint.setTypeface(Typeface.create("System", Typeface.BOLD));
//獲取文字寬度,讓其居中顯示
float wtext = paint.measureText(getResources().getString(R.string.scan_text));
canvas.drawText(getResources().getString(R.string.scan_text), (widthPixels-wtext)/2, (float) (frame.bottom + (float)TEXT_PADDING_TOP *density), paint);

二、修改二維碼窗口大小。默認狀況下,窗口的大小是屏幕的四分之三,有最小值和最大值。

找到CameraManager類的getFramingRect方法,就能夠看到。app

public Rect getFramingRect() {
    Point screenResolution = configManager.getScreenResolution();
    if (framingRect == null) {
      if (camera == null) {
        return null;
      }
      int width = screenResolution.x * 3 / 4;
      if (width < MIN_FRAME_WIDTH) {
        width = MIN_FRAME_WIDTH;
      } else if (width > MAX_FRAME_WIDTH) {
        width = MAX_FRAME_WIDTH;
      }
      int height = screenResolution.y * 3 / 4;
      if (height < MIN_FRAME_HEIGHT) {
        height = MIN_FRAME_HEIGHT;
      } else if (height > MAX_FRAME_HEIGHT) {
        height = MAX_FRAME_HEIGHT;
      }
      int leftOffset = (screenResolution.x - width) / 2;
      int topOffset = (screenResolution.y - height) / 2;
      framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);
      Log.d(TAG, "Calculated framing rect: " + framingRect);
    }
    return framingRect;
  }


最近沒事作了下二維碼掃描,用的是ZXing的開源代碼,官方源碼地址:http://code.google.com/p/zxing/downloads/list;
我是在ZXing2.2基礎上作的,所以只下載了ZXing-2.2.zip;
此外還須要ZXing的核心Jar包,下載地址:http://repo1.maven.org/maven2/com/google/zxing/core/2.2/,只需下載core-2.2.jar就行;

將下載的ZXing-2.2.zip解壓出來,咱們只須要用到android目錄中的示例項目,如圖:

 
將android項目導入eclipse,同時別忘了將core-2.2.jar導入libs,此時該示例項目應該能夠運行了,不過該項目不少功能咱們不須要,而且其掃描界面爲橫向的,所以需對其修改。

接下去咱們來將該示例項目簡化:
第一步:拷貝必要的包和類
新建本身的項目並導入core-2.2.jar,將示例項目中的必要代碼所有拷貝到本身的項目中,至於示例項目中各個包和類的功能此處就不作解釋的,有興趣能夠本身去研究下;
我本身對包結構作了一點改動,導入完成後會有不少紅叉,大都和包的訪問權限有關,由於示例代碼中不少類是final型的,咱們將其public就行;
此外還須要res下一些關聯的文件(values下的color.xml、ids.xml、strings.xml,raw下的beep.ogg)。

初步調整後包結構以下:

 

第二步:PreferencesActivity和CaptureActivity修改
示例項目用到了大量的配置,所以不少地方都用到了
PreferencesActivity這個類,其實留着它也無所謂,但別忘了將示例項目中res下一些關聯文件拷貝過來(preferences.xml、arrays.xml);
不過PreferencesActivity徹底是多餘的,看着也礙眼,所以我將其去掉,須要將用到PreferencesActivity的類都修改,就是剩餘那些報紅叉的類,咱們須要將一些配置固定化,多餘的設置判斷去掉,此處我就不貼代碼了;
一樣CaptureActivity中也有不少方法是咱們不須要的,大都是關於解碼成功後的處理,若是要保留的話則須要額外拷貝不少類,所以將其去掉,此處也不貼代碼了,附件源碼中都有。


第三部:修改成豎屏
通過上面兩步,咱們本身的項目應該能夠運行了(別忘了加權限),固然此時是橫屏的,所以咱們須要修改幾處地方將其修改成豎屏:
1.CameraConfigurationManager類的initFromCameraParameters()方法中將如下代碼註釋掉:

代碼片斷,雙擊複製
01
02
03
04
05
06
if (width < height) {
    Log.i(TAG, "Display reports portrait orientation; assuming this is incorrect" );
    int temp = width;
    width = height;
      height = temp;
    }


2. CameraConfigurationManager 類的 setDesiredCameraParameters() 方法中在camera.setParameters(parameters)以前加入如下代碼:
代碼片斷,雙擊複製
01
camera.setDisplayOrientation( 90 );


3. CameraManager 類的 getFramingRectInPreview() 方法中將如下代碼替換:
代碼片斷,雙擊複製
01
02
03
04
rect.left = rect.left * cameraResolution.x / screenResolution.x;
rect.right = rect.right * cameraResolution.x / screenResolution.x;
rect.top = rect.top * cameraResolution.y / screenResolution.y;
rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;

替換爲
代碼片斷,雙擊複製
01
02
03
04
rect.left = rect.left * cameraResolution.y / screenResolution.x;
rect.right = rect.right * cameraResolution.y / screenResolution.x;
rect.top = rect.top * cameraResolution.x / screenResolution.y;
rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;


4. DecodeHandler 類的 decode 方法中在activity.getCameraManager().buildLuminanceSource()以前添加如下代碼:
代碼片斷,雙擊複製
01
02
03
04
05
06
07
08
09
byte [] rotatedData = new byte [data.length];
for ( int y = 0 ; y < height; y++) {
    for ( int x = 0 ; x < width; x++)
         rotatedData[x * height + height - y - 1 ] = data[x + y * width];
         }
  int tmp = width;
  width = height;
  height = tmp;
  data = rotatedData;


5.很關鍵的一步,解決豎屏後圖像拉伸問題。CameraConfigurationManager類的initFromCameraParameters()方法中:
在Log.i(TAG, "Screen resolution: " + screenResolution);以後添加如下代碼:
代碼片斷,雙擊複製
01
02
03
04
05
06
07
Point screenResolutionForCamera = new Point();
screenResolutionForCamera.x = screenResolution.x;
screenResolutionForCamera.y = screenResolution.y;
if (screenResolution.x < screenResolution.y) {
    screenResolutionForCamera.x = screenResolution.y;
    screenResolutionForCamera.y = screenResolution.x;
    }

同時修改下一句爲cameraResolution = findBestPreviewSizeValue(parameters,screenResolutionForCamera);

此外 manifest 中別忘了設置android:screenOrientation="portrait",至此豎屏修改完畢。

第四步 :掃描框位置和大小修改
此時的掃描框是豎直拉伸的矩形,很難看,咱們能夠將其修改成正方形或扁平型的。
CameraManager 類的 getFramingRect() 方法中替換如下代碼:
代碼片斷,雙擊複製
01
02
int width = findDesiredDimensionInRange(screenResolution.x, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH);
int height = findDesiredDimensionInRange(screenResolution.y,MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT);

替換爲
代碼片斷,雙擊複製
01
02
03
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
int width = ( int ) (metrics.widthPixels * 0.6 );
int height = ( int ) (width * 0.9 );

此處咱們根據屏幕分辨率來定掃描框大小更靈活一點,同時將偏移量topOffset修改成(screenResolution.y - height)/4

第五步 :掃描框四個角和掃描線條修改
示例代碼中的線條是居中且不動的,咱們能夠將其修改成上下移動的掃描線,且能夠改變線條的樣式。
在自定義掃描佈局ViewfinderView類中的onDraw()方法中繪製 四個角 ,關鍵代碼以下:
代碼片斷,雙擊複製
01
02
03
04
05
06
07
08
09
10
11
12
13
14
// 畫出四個角
paint.setColor(getResources().getColor(R.color.green));
// 左上角
canvas.drawRect(frame.left, frame.top, frame.left + 15 ,frame.top + 5 , paint);
canvas.drawRect(frame.left, frame.top, frame.left + 5 ,frame.top + 15 , paint);
// 右上角
canvas.drawRect(frame.right - 15 , frame.top, frame.right,frame.top + 5 , paint);
canvas.drawRect(frame.right - 5 , frame.top, frame.right,frame.top + 15 , paint);
// 左下角
canvas.drawRect(frame.left, frame.bottom - 5 , frame.left + 15 ,frame.bottom, paint);
canvas.drawRect(frame.left, frame.bottom - 15 , frame.left + 5 ,frame.bottom, paint);
// 右下角
canvas.drawRect(frame.right - 15 , frame.bottom - 5 , frame.right,frame.bottom, paint);
canvas.drawRect(frame.right - 5 , frame.bottom - 15 , frame.right,frame.bottom, paint);


此外將掃描線條修改成 上下掃描 的線,關鍵代碼以下:
代碼片斷,雙擊複製
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
if ((i += 5 ) < frame.bottom - frame.top) {
         /* 如下爲用漸變線條做爲掃描線 */
         // 漸變圖爲矩形
         // mDrawable.setShape(GradientDrawable.RECTANGLE);
         // 漸變圖爲線型
         // mDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);
         // 線型矩形的四個圓角半徑
         // // mDrawable
         // // .setCornerRadii(new float[] { 8, 8, 8, 8, 8, 8, 8, 8 });
         // 位置邊界
         // mRect.set(frame.left + 10, frame.top + i, frame.right - 10,
         // frame.top + 1 + i);
         // 設置漸變圖填充邊界
         // mDrawable.setBounds(mRect);
         // 畫出漸變線條
         // mDrawable.draw(canvas);
 
         /* 如下爲圖片做爲掃描線 */
         mRect.set(frame.left - 6 , frame.top + i - 6 , frame.right + 6 ,frame.top + 6 + i);
         lineDrawable.setBounds(mRect);
         lineDrawable.draw(canvas);
 
         // 刷新
         invalidate();
       } else {
             i = 0 ;
       }

此處採用了兩種線條樣式,一種是漸變線條,還有一種是相似微信的圖片掃描線。
詳細代碼請看附件源碼。

運行截圖以下:
 此爲漸變線條               此爲圖片線條,用的是微信的圖片,不過微信掃描用的應該是動畫很平滑,此處用的是屢次繪製

另外,掃描成功後的手機震動和提示音在BeepManager中修改,裏面我額外放了兩種提示音文件


示例源碼工程,core包和我修改簡化後的源碼放都附件中

  ZXing示例源碼和核心core包.rar (1010.76 KB, 下載次數: 36) 
  簡化後源碼.rar (2.38 MB, 下載次數: 293) 


http://blog.csdn.net/xinchen200/article/details/18036695
eclipse

相關文章
相關標籤/搜索