需求
最近三个月一直忙于@微卖客户端的开发,在开发过程中遇到各种各样的问题,虽问题一一解决但并没有很好的归类与整理,恰好遇到假期有时间由近及远的梳理一下,而条码与二维码的扫描则是近期遇到问题之一.好在android的迅速发展也带动了第三方开发者在此平台上的活跃度,从Github上我们几乎能找到各种常用的组件与工具包,而zxing也是解决条码与二维码的扫描的成熟方案之一.
zxing使用问题
- zxing最新版本是3.1.0,但是在实际使用过程中发现取景框与实际抓图高度存在偏差,导致经常不能正确识别条码,只是由于开发时间关系,我并没有直接去找原因,而是退而求其次选择了2.3.0的版本使用,果然问题消失了.
- 在zxing的使用过程中,官方自带的例子使用了很多我们不太需要的功能,而我的需求只是正确识别二维码与条码即可,当然能有开灯功能更好,而在使用这些功能完全可以将很多不用的模块剔除掉,从而关注我们自己的核心业务.
- zxing在例子中只提供了横屏解决方案,但我们的需求是需要竖屏使用,同样需要对zxing做点小改造.
改造方案
- 下载zxing-core-2.3.0.jar
-
创建MainActivity,代码类似这样
123456789101112131415161718192021222324252627@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.main_act);// 扫描findViewById(R.id.barcode_scan).setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent(MainActivity.this, CaptureActivity.class);startActivityForResult(intent, AppConst.RequestCode.BARCODE_SCAN);}});@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {switch (requestCode) {case AppConst.RequestCode.BARCODE_SCAN:if (resultCode == RESULT_OK) {// to do ...}break;}}}
-
然后添加CaptureActivity,代码如下
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190@SuppressWarnings("deprecation")public class CaptureActivity extends Activity implements SurfaceHolder.Callback {private static final String TAG = CaptureActivity.class.getName();private CameraManager cameraManager;private AmbientLightManager ambientLightManager;private CaptureActivityHandler handler;private ViewfinderView viewfinderView;private boolean hasSurface;private boolean flashLight;private TextView lightText;public CameraManager getCameraManager() {return cameraManager;}public Handler getHandler() {return handler;}public ViewfinderView getViewfinderView() {return viewfinderView;}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Window window = getWindow();window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.capture);// 闪光灯文字lightText = (TextView) findViewById(R.id.light_text);findViewById(R.id.flash_light).setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {if (!flashLight) {cameraManager.setTorch(true);} else {cameraManager.setTorch(false);}flashLight = !flashLight;if (flashLight) {lightText.setText("关灯");} else {lightText.setText("开灯");}}});hasSurface = false;ambientLightManager = new AmbientLightManager(this);PreferenceManager.setDefaultValues(this, R.xml.preferences, false);}@Overrideprotected void onResume() {super.onResume();cameraManager = new CameraManager(getApplication());viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);viewfinderView.setCameraManager(cameraManager);SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);SurfaceHolder surfaceHolder = surfaceView.getHolder();if (hasSurface) {// The activity was paused but not stopped, so the surface still exists. Therefore// surfaceCreated() won't be called, so init the camera here.initCamera(surfaceHolder);} else {// Install the callback and wait for surfaceCreated() to init the camera.surfaceHolder.addCallback(this);// android 2.3版本必须添加,否则camera无法启动surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);}ambientLightManager.start(cameraManager);}@Overrideprotected void onPause() {if (handler != null) {handler.quitSynchronously();handler = null;}ambientLightManager.stop();cameraManager.closeDriver();if (!hasSurface) {SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);SurfaceHolder surfaceHolder = surfaceView.getHolder();surfaceHolder.removeCallback(this);}super.onPause();}@Overrideprotected void onDestroy() {super.onDestroy();}public void handleDecode(Result rawResult, Bitmap barcode, float scaleFactor) {String result = rawResult.getText();if (!TextUtils.isEmpty(result)) {Intent intent = new Intent();intent.putExtra("scan_result", rawResult.getText());setResult(RESULT_OK, intent);finish();}}public void drawViewfinder() {viewfinderView.drawViewfinder();}@Overridepublic void surfaceCreated(SurfaceHolder holder) {if (holder == null) {Log.e(TAG, "*** WARNING *** surfaceCreated() gave us a null surface!");}if (!hasSurface) {hasSurface = true;initCamera(holder);}}private void initCamera(SurfaceHolder surfaceHolder) {if (surfaceHolder == null) {throw new IllegalStateException("No SurfaceHolder provided");}if (cameraManager.isOpen()) {Log.w(TAG, "initCamera() while already open -- late SurfaceView callback?");return;}try {cameraManager.openDriver(surfaceHolder);// Creating the handler starts the preview, which can also throw a RuntimeException.if (handler == null) {handler = new CaptureActivityHandler(this, null, null, null, cameraManager);}} catch (IOException ioe) {Log.w(TAG, "open camera error", ioe);displayFrameworkBugMessageAndExit();} catch (RuntimeException e) {// Barcode Scanner has seen crashes in the wild of this variety:// java.?lang.?RuntimeException: Fail to connect to camera serviceLog.w(TAG, "init camera error", e);displayFrameworkBugMessageAndExit();}}private void displayFrameworkBugMessageAndExit() {AlertDialog.Builder builder = new AlertDialog.Builder(this);builder.setTitle(getString(R.string.app_name));builder.setMessage(getString(R.string.msg_camera_framework_bug));builder.setPositiveButton(R.string.button_ok, new FinishListener(this));builder.setOnCancelListener(new FinishListener(this));builder.show();}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {hasSurface = false;}}
- 修改CameraManager,可设置取景框高度与宽度
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275public class CameraManager {private final static String TAG = CameraManager.class.getSimpleName();// 取景框高度与宽度基准 单位dpprivate final static int SCAN_FRAME_WIDTH = 268;private final static int SCAN_FRAME_HEIGHT = 268;private final Context context;private final CameraConfigurationManager configManager;private Camera camera;private AutoFocusManager autoFocusManager;private Rect framingRect;private Rect framingRectInPreview;private boolean initialized;private boolean previewing;private int requestedFramingRectWidth;private int requestedFramingRectHeight;private final PreviewCallback previewCallback;public CameraManager(Context context) {this.context = context;this.configManager = new CameraConfigurationManager(context);previewCallback = new PreviewCallback(configManager);}/*** Opens the camera driver and initializes the hardware parameters.** @param holder The surface object which the camera will draw preview frames into.* @throws IOException Indicates the camera driver failed to open.*/public synchronized void openDriver(SurfaceHolder holder) throws IOException {Camera theCamera = camera;if (theCamera == null) {theCamera = OpenCameraInterface.open();if (theCamera == null) {throw new IOException();}camera = theCamera;}theCamera.setPreviewDisplay(holder);if (!initialized) {initialized = true;configManager.initFromCameraParameters(theCamera);if (requestedFramingRectWidth > 0 && requestedFramingRectHeight > 0) {setManualFramingRect(requestedFramingRectWidth, requestedFramingRectHeight);requestedFramingRectWidth = 0;requestedFramingRectHeight = 0;}}Camera.Parameters parameters = theCamera.getParameters();String parametersFlattened = parameters == null ? null : parameters.flatten(); // Save these,// temporarilytry {configManager.setDesiredCameraParameters(theCamera, false);} catch (RuntimeException re) {// Driver failedLog.w(TAG, "Camera rejected parameters. Setting only minimal safe-mode parameters");Log.i(TAG, "Resetting to saved camera params: " + parametersFlattened);// Reset:if (parametersFlattened != null) {parameters = theCamera.getParameters();parameters.unflatten(parametersFlattened);try {theCamera.setParameters(parameters);configManager.setDesiredCameraParameters(theCamera, true);} catch (RuntimeException re2) {// Well, darn. Give upLog.w(TAG, "Camera rejected even safe-mode parameters! No configuration");}}}}public synchronized boolean isOpen() {return camera != null;}/*** Closes the camera driver if still in use.*/public synchronized void closeDriver() {if (camera != null) {camera.release();camera = null;// Make sure to clear these each time we close the camera, so that any scanning rect// requested by intent is forgotten.framingRect = null;framingRectInPreview = null;}}/*** Asks the camera hardware to begin drawing preview frames to the screen.*/public synchronized void startPreview() {Camera theCamera = camera;if (theCamera != null && !previewing) {theCamera.startPreview();previewing = true;autoFocusManager = new AutoFocusManager(context, camera);}}/*** Tells the camera to stop drawing preview frames.*/public synchronized void stopPreview() {if (autoFocusManager != null) {autoFocusManager.stop();autoFocusManager = null;}if (camera != null && previewing) {camera.stopPreview();previewCallback.setHandler(null, 0);previewing = false;}}/*** Convenience method for {@link com.google.zxing.client.android.CaptureActivity}*/public synchronized void setTorch(boolean newSetting) {if (newSetting != configManager.getTorchState(camera)) {if (camera != null) {if (autoFocusManager != null) {autoFocusManager.stop();}configManager.setTorch(camera, newSetting);if (autoFocusManager != null) {autoFocusManager.start();}}}}/*** A single preview frame will be returned to the handler supplied. The data will arrive as byte[]* in the message.obj field, with width and height encoded as message.arg1 and message.arg2,* respectively.** @param handler The handler to send the message to.* @param message The what field of the message to be sent.*/public synchronized void requestPreviewFrame(Handler handler, int message) {Camera theCamera = camera;if (theCamera != null && previewing) {previewCallback.setHandler(handler, message);theCamera.setOneShotPreviewCallback(previewCallback);}}/*** Calculates the framing rect which the UI should draw to show the user where to place the* barcode. This target helps with alignment as well as forces the user to hold the device far* enough away to ensure the image will be in focus.** @return The rectangle to draw on screen in window coordinates.*/public synchronized Rect getFramingRect() {if (framingRect == null) {if (camera == null) {return null;}Point screenResolution = configManager.getScreenResolution();if (screenResolution == null) {// Called early, before init even finishedreturn null;}int width = DensityUtil.dip2px(context, SCAN_FRAME_WIDTH);int height = DensityUtil.dip2px(context, SCAN_FRAME_HEIGHT);int leftOffset = (screenResolution.x - width) / 2;int topOffset = (screenResolution.y - height) / 2;// 真正的取景框大小framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height / 2);Log.d(TAG, "Calculated framing rect: " + framingRect);}return framingRect;}/*** Like {@link #getFramingRect} but coordinates are in terms of the preview frame, not UI /* screen.*/public synchronized Rect getFramingRectInPreview() {if (framingRectInPreview == null) {Rect framingRect = getFramingRect();if (framingRect == null) {return null;}Rect rect = new Rect(framingRect);Point cameraResolution = configManager.getCameraResolution();Point screenResolution = configManager.getScreenResolution();if (cameraResolution == null || screenResolution == null) {// Called early, before init even finishedreturn null;}// 竖屏宽高修改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;framingRectInPreview = rect;}return framingRectInPreview;}/*** Allows third party apps to specify the scanning rectangle dimensions, rather than determine* them automatically based on screen resolution.** @param width The width in pixels to scan.* @param height The height in pixels to scan.*/public synchronized void setManualFramingRect(int width, int height) {if (initialized) {Point screenResolution = configManager.getScreenResolution();if (width > screenResolution.x) {width = screenResolution.x;}if (height > screenResolution.y) {height = screenResolution.y;}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 manual framing rect: " + framingRect);framingRectInPreview = null;} else {requestedFramingRectWidth = width;requestedFramingRectHeight = height;}}/*** A factory method to build the appropriate LuminanceSource object based on the format of the* preview buffers, as described by Camera.Parameters.** @param data A preview frame.* @param width The width of the image.* @param height The height of the image.* @return A PlanarYUVLuminanceSource instance.*/public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {Rect rect = getFramingRectInPreview();if (rect == null) {return null;}// Go ahead and assume it's YUV rather than die.return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top, rect.width(),rect.height(), false);}}
- 修改CameraConfigurationManager,设置竖屏参数
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277package com.myshow.weimai.widget.barcode.camera;import java.util.ArrayList;import java.util.Collection;import java.util.Collections;import java.util.Comparator;import java.util.Iterator;import java.util.List;import com.myshow.weimai.utils.AppUtils;import com.myshow.weimai.widget.barcode.BarcodePreferences;import android.annotation.SuppressLint;import android.content.Context;import android.content.SharedPreferences;import android.graphics.Point;import android.hardware.Camera;import android.preference.PreferenceManager;import android.util.Log;import android.view.Display;import android.view.WindowManager;@SuppressLint("NewApi")@SuppressWarnings("deprecation")public class CameraConfigurationManager {private static final String TAG = CameraConfigurationManager.class.getName();// This is bigger than the size of a small screen, which is still supported. The routine// below will still select the default (presumably 320x240) size for these. This prevents// accidental selection of very low resolution on some devices.private static final int MIN_PREVIEW_PIXELS = 480 * 320;// private static final float MAX_EXPOSURE_COMPENSATION = 1.5f;// private static final float MIN_EXPOSURE_COMPENSATION = 0.0f;private static final double MAX_ASPECT_DISTORTION = 0.15;private final Context context;private Point screenResolution;private Point cameraResolution;CameraConfigurationManager(Context context) {this.context = context;}/*** Reads, one time, values from the camera that are needed by the app.*/void initFromCameraParameters(Camera camera) {Camera.Parameters parameters = camera.getParameters();WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);Display display = manager.getDefaultDisplay();if (AppUtils.getSDKVersion() >= android.os.Build.VERSION_CODES.HONEYCOMB_MR2) {screenResolution = new Point();display.getSize(screenResolution);} else {screenResolution = new Point(display.getWidth(), display.getHeight());}Log.i(TAG, "Screen resolution: " + screenResolution);cameraResolution = findBestPreviewSizeValue(parameters, screenResolution);Log.i(TAG, "Camera resolution: " + cameraResolution);}void setDesiredCameraParameters(Camera camera, boolean safeMode) {Camera.Parameters parameters = camera.getParameters();// 设置镜头竖屏camera.setDisplayOrientation(90);if (parameters == null) {Log.w(TAG,"Device error: no camera parameters are available. Proceeding without configuration.");return;}Log.i(TAG, "Initial camera parameters: " + parameters.flatten());if (safeMode) {Log.w(TAG, "In camera config safe mode -- most settings will not be honored");}SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);initializeTorch(parameters, prefs, safeMode);String focusMode = null;if (prefs.getBoolean(BarcodePreferences.KEY_AUTO_FOCUS, true)) {if (safeMode || prefs.getBoolean(BarcodePreferences.KEY_DISABLE_CONTINUOUS_FOCUS, false)) {focusMode =findSettableValue(parameters.getSupportedFocusModes(),Camera.Parameters.FOCUS_MODE_AUTO);} else {focusMode =findSettableValue(parameters.getSupportedFocusModes(),Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE,Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO, Camera.Parameters.FOCUS_MODE_AUTO);}}// Maybe selected auto-focus but not available, so fall through here:if (!safeMode && focusMode == null) {focusMode =findSettableValue(parameters.getSupportedFocusModes(),Camera.Parameters.FOCUS_MODE_MACRO, Camera.Parameters.FOCUS_MODE_EDOF);}if (focusMode != null) {parameters.setFocusMode(focusMode);}if (prefs.getBoolean(BarcodePreferences.KEY_INVERT_SCAN, false)) {String colorMode =findSettableValue(parameters.getSupportedColorEffects(),Camera.Parameters.EFFECT_NEGATIVE);if (colorMode != null) {parameters.setColorEffect(colorMode);}}parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);camera.setParameters(parameters);Camera.Parameters afterParameters = camera.getParameters();Camera.Size afterSize = afterParameters.getPreviewSize();if (afterSize != null&& (cameraResolution.x != afterSize.width || cameraResolution.y != afterSize.height)) {Log.w(TAG, "Camera said it supported preview size " + cameraResolution.x + 'x'+ cameraResolution.y + ", but after setting it, preview size is " + afterSize.width + 'x'+ afterSize.height);cameraResolution.x = afterSize.width;cameraResolution.y = afterSize.height;}}Point getCameraResolution() {return cameraResolution;}Point getScreenResolution() {return screenResolution;}boolean getTorchState(Camera camera) {if (camera != null) {Camera.Parameters parameters = camera.getParameters();if (parameters != null) {String flashMode = camera.getParameters().getFlashMode();return flashMode != null&& (Camera.Parameters.FLASH_MODE_ON.equals(flashMode) || Camera.Parameters.FLASH_MODE_TORCH.equals(flashMode));}}return false;}void setTorch(Camera camera, boolean newSetting) {Camera.Parameters parameters = camera.getParameters();doSetTorch(parameters, newSetting, false);camera.setParameters(parameters);}private void initializeTorch(Camera.Parameters parameters, SharedPreferences prefs,boolean safeMode) {boolean currentSetting = FrontLightMode.readPref(prefs) == FrontLightMode.ON;doSetTorch(parameters, currentSetting, safeMode);}private void doSetTorch(Camera.Parameters parameters, boolean newSetting, boolean safeMode) {String flashMode;if (newSetting) {flashMode =findSettableValue(parameters.getSupportedFlashModes(),Camera.Parameters.FLASH_MODE_TORCH, Camera.Parameters.FLASH_MODE_ON);} else {flashMode =findSettableValue(parameters.getSupportedFlashModes(), Camera.Parameters.FLASH_MODE_OFF);}if (flashMode != null) {parameters.setFlashMode(flashMode);}}private Point findBestPreviewSizeValue(Camera.Parameters parameters, Point screenResolution) {List<Camera.Size> rawSupportedSizes = parameters.getSupportedPreviewSizes();if (rawSupportedSizes == null) {Log.w(TAG, "Device returned no supported preview sizes; using default");Camera.Size defaultSize = parameters.getPreviewSize();return new Point(defaultSize.width, defaultSize.height);}// Sort by size, descendingList<Camera.Size> supportedPreviewSizes = new ArrayList<>(rawSupportedSizes);Collections.sort(supportedPreviewSizes, new Comparator<Camera.Size>() {@Overridepublic int compare(Camera.Size a, Camera.Size b) {int aPixels = a.height * a.width;int bPixels = b.height * b.width;if (bPixels < aPixels) {return -1;}if (bPixels > aPixels) {return 1;}return 0;}});double screenAspectRatio = (double) screenResolution.x / (double) screenResolution.y;// Remove sizes that are unsuitableIterator<Camera.Size> it = supportedPreviewSizes.iterator();while (it.hasNext()) {Camera.Size supportedPreviewSize = it.next();int realWidth = supportedPreviewSize.width;int realHeight = supportedPreviewSize.height;if (realWidth * realHeight < MIN_PREVIEW_PIXELS) {it.remove();continue;}boolean isCandidatePortrait = realWidth < realHeight;int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth;int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight;double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight;double distortion = Math.abs(aspectRatio - screenAspectRatio);if (distortion > MAX_ASPECT_DISTORTION) {it.remove();continue;}if (maybeFlippedWidth == screenResolution.x && maybeFlippedHeight == screenResolution.y) {Point exactPoint = new Point(realWidth, realHeight);Log.i(TAG, "Found preview size exactly matching screen size: " + exactPoint);return exactPoint;}}// If no exact match, use largest preview size. This was not a great idea on older devices// because// of the additional computation needed. We're likely to get here on newer Android 4+ devices,// where// the CPU is much more powerful.if (!supportedPreviewSizes.isEmpty()) {Camera.Size largestPreview = supportedPreviewSizes.get(0);Point largestSize = new Point(largestPreview.width, largestPreview.height);Log.i(TAG, "Using largest suitable preview size: " + largestSize);return largestSize;}// If there is nothing at all suitable, return current preview sizeCamera.Size defaultPreview = parameters.getPreviewSize();Point defaultSize = new Point(defaultPreview.width, defaultPreview.height);Log.i(TAG, "No suitable preview sizes, using default: " + defaultSize);return defaultSize;}private static String findSettableValue(Collection<String> supportedValues,String... desiredValues) {Log.i(TAG, "Supported values: " + supportedValues);String result = null;if (supportedValues != null) {for (String desiredValue : desiredValues) {if (supportedValues.contains(desiredValue)) {result = desiredValue;break;}}}Log.i(TAG, "Settable value: " + result);return result;}}
-
修改DecodeHandler,保证byte数组中顺序
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182final class DecodeHandler extends Handler {private static final String TAG = DecodeHandler.class.getSimpleName();private final CaptureActivity activity;private final MultiFormatReader multiFormatReader;private boolean running = true;DecodeHandler(CaptureActivity activity, Map<DecodeHintType, Object> hints) {multiFormatReader = new MultiFormatReader();multiFormatReader.setHints(hints);this.activity = activity;}@Overridepublic void handleMessage(Message message) {if (!running) {return;}switch (message.what) {case R.id.decode:decode((byte[]) message.obj, message.arg1, message.arg2);break;case R.id.quit:running = false;Looper.myLooper().quit();break;}}/*** Decode the data within the viewfinder rectangle, and time how long it took. For efficiency,* reuse the same reader objects from one decode to the next.** @param data The YUV preview frame.* @param width The width of the preview frame.* @param height The height of the preview frame.*/private void decode(byte[] data, int width, int height) {long start = System.currentTimeMillis();Result rawResult = null;// 重置databyte[] 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;PlanarYUVLuminanceSource source =activity.getCameraManager().buildLuminanceSource(rotatedData, width, height);if (source != null) {BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));try {rawResult = multiFormatReader.decodeWithState(bitmap);} catch (ReaderException re) {// continue} finally {multiFormatReader.reset();}}Handler handler = activity.getHandler();if (rawResult != null) {// Don't log the barcode contents for security.long end = System.currentTimeMillis();Log.d(TAG, "Found barcode in " + (end - start) + " ms");if (handler != null) {Message message = Message.obtain(handler, R.id.decode_succeeded, rawResult);message.sendToTarget();}} else {if (handler != null) {Message message = Message.obtain(handler, R.id.decode_failed);message.sendToTarget();}}}
-
最后附上capture.xml
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374<?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="fill_parent"android:layout_height="fill_parent" ><!-- title bar --><includeandroid:id="@id/title_bar"layout="@layout/fragment_title" /><FrameLayoutandroid:layout_width="fill_parent"android:layout_height="fill_parent"android:layout_below="@id/title_bar" ><SurfaceViewandroid:id="@id/preview_view"android:layout_width="fill_parent"android:layout_height="fill_parent" /><FrameLayoutandroid:layout_width="fill_parent"android:layout_height="fill_parent" ><com.google.zxing.client.android.ViewfinderViewandroid:id="@id/viewfinder_view"android:layout_width="fill_parent"android:layout_height="fill_parent" /><LinearLayoutandroid:layout_width="fill_parent"android:layout_height="wrap_content"android:layout_gravity="bottom"android:orientation="vertical" ><RelativeLayoutandroid:id="@id/flash_light"android:layout_width="wrap_content"android:layout_height="34dp"android:layout_gravity="center_horizontal"android:layout_marginBottom="25dp"android:background="@drawable/btn_qrcode_light_button_background"android:clickable="true"tools:ignore="UselessParent" ><TextViewandroid:id="@id/light_icon"android:layout_width="20dp"android:layout_height="20dp"android:layout_alignParentLeft="true"android:layout_centerVertical="true"android:layout_marginLeft="13dp"android:background="@drawable/qrcode_light_button_icon" /><TextViewandroid:id="@id/light_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true"android:layout_toRightOf="@id/light_icon"android:ellipsize="end"android:paddingRight="13dp"android:singleLine="true"android:text="开灯"android:textColor="#ffAAAAAA"android:textSize="14.0sp"tools:ignore="HardcodedText" /></RelativeLayout></LinearLayout></FrameLayout></FrameLayout></RelativeLayout>
- 基本上添加完这些类后再导入这些类的依赖包即可完成扫描功能,而其它代码均在官网找到,这里也不一一列举了
牛x!