首先我们看下项目结构
(1) 首先我们从扫描二维码Activity MipcaActivityCapture.Java 类入手该类主要是调用相机预览拍照内容,处理扫描后的结果,扫码成功震动,及扫描音效等。
首先我们看关键模块,相机拍摄预览用到为View控件SurfaceView 改控件提供了一个专用绘图面,嵌入在视图层次结构中。你可以控制整个表面的格式,它的大小;SurfaceView负责屏幕上正确的位置显示。
SurfaceView提供了 SurfaceHolder接口来设置控件的表面大小和格式编辑表面像素等,SurfaceHolder提供了Android.view.SurfaceHolder.Callback 接口来处理SurfaceView显示,渲染,销毁等回调监听,下面看关键代码
- @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;
-
- }
接下来看下初始化媒体播放器,及震动模块代码,MediaPlayer 做过流媒体或音频相关开发都用过,这里是用文件流加载
raw目录下的文件。 Vibrator类 操作该设备上的振子的类,也就是让我们手机产生震动效果,请看一下代码块,注释有很多是自己理解和百度翻译。
接下来看相机初始化模块,及相机控制模块,这里用到了Activity生命周期函数,主要是关闭相机,终止线程等相关操作。
-
-
-
-
-
- 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
- protected void onPause() {
- super.onPause();
- if (handler != null) {
-
-
- handler.quitSynchronously();
-
- handler = null;
- }
-
-
- CameraManager.get().closeDriver();
- }
-
-
-
-
- @Override
- protected void onDestroy() {
-
-
-
-
-
-
-
-
- inactivityTimer.shutdown();
-
- super.onDestroy();
- }
最后我们来看看,如何处理扫描结果的,这里用到了 CaptureActivityHandler 这个类继承 Handler,类中封装了解码线程类DecodeThread 这里我们先看 当前扫描Activity如何处理扫描后处理结果的函数 public void handleDecode(Result result, Bitmap 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();
- }
(2)以上只是浅谈如何调用相机,以及处理扫描效果等,接线来深入分析扫描线程,及处理扫描效果 handler 回调等,刚才有讲到CaptureActivityHandler 类这个类处理Handler消息下面就看相关代码。
首先我们看下这个类的构造函数,在这个构造函数中,构造了解码线程类 DecodeThread类,这个类非常关键稍后会讲到,这里要注意,我们在构建中已经启用线程 decodeThread.start();
-
-
-
-
-
-
- public CaptureActivityHandler(MipcaActivityCapture activity,
- Vector decodeFormats, String characterSet) {
-
- this.activity = activity;
-
- decodeThread = new DecodeThread(activity, decodeFormats, characterSet,
- new ViewfinderResultPointCallback(activity.getViewfinderView()));
-
- decodeThread.start();
- state = State.SUCCESS;
-
-
- CameraManager.get().startPreview();
-
- restartPreviewAndDecode();
- }
看到了构造处理消息 Handler类代码块,那么还记得那个模块构造的该类对象不,我们刚才有讲到相机初始化模块请看一下代码。
-
-
-
-
-
- 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);
- }
- }
接下来分析这个类的关键模块 Handler消息处理模块 handleMessage()消息处理函数,这里肯定大家会对一些用到的Id感到好奇其实这里的Id是定义在 下图中xml文件中,大家可以详细看下。
接下来言归正传,还是继续分析 handleMessage(),这里有用到枚举类,用来标记状态,public void handleMessage(Message message)函数都有注释,这里就不详解了。
- private enum State {
- PREVIEW,
- SUCCESS,
- DONE
- }
- @Override
- public void handleMessage(Message message) {
- switch (message.what) {
- case R.id.auto_focus:
-
-
-
-
-
-
-
-
-
-
-
- if (state == State.PREVIEW) {
- CameraManager.get().requestAutoFocus(this, R.id.auto_focus);
- }
-
- break;
- case R.id.restart_preview:
- Log.d(TAG, "Got restart preview message");
-
-
- restartPreviewAndDecode();
- break;
- case R.id.decode_succeeded:
-
- Log.d(TAG, "Got decode succeeded message");
- state = State.SUCCESS;
- Bundle bundle = message.getData();
-
-
- Bitmap barcode = bundle == null ? null : (Bitmap) bundle
- .getParcelable(DecodeThread.BARCODE_BITMAP);
-
- activity.handleDecode((Result) message.obj, barcode);
-
- break;
- case R.id.decode_failed:
-
-
-
- state = State.PREVIEW;
- CameraManager.get().requestPreviewFrame(decodeThread.getHandler(),
- R.id.decode);
- break;
- case R.id.return_scan_result:
-
- Log.d(TAG, "返回扫描结果消息");
- activity.setResult(Activity.RESULT_OK, (Intent) message.obj);
- activity.finish();
- break;
- case R.id.launch_product_query:
-
- Log.d(TAG, "产品查询消息");
- String url = (String) message.obj;
- Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
- activity.startActivity(intent);
- break;
- }
- }
下面看下解码过程中,用到了两个关键函数 quitSynchronously()该函数主要是处理相机关闭相机预览帧,阻止线程,清空Handler消息队列。细心的同学会发现该函数是在 处理扫描的Activity 生命周期函数 onPause()函数中用到
-
-
-
- public void quitSynchronously() {
- state = State.DONE;
-
-
- CameraManager.get().stopPreview();
-
- Message quit = Message.obtain(decodeThread.getHandler(), R.id.quit);
-
-
-
-
-
- quit.sendToTarget();
- try {
-
-
-
-
-
-
-
-
-
-
- decodeThread.join();
- } catch (InterruptedException e) {
-
- }
-
-
- removeMessages(R.id.decode_succeeded);
- removeMessages(R.id.decode_failed);
- }
下面还有一个关键模块,主要是处理,重新启动预览和解码函数 restartPreviewAndDecode() 这里有用到 CameraManager类 该类是相机管理类稍后会讲到
-
-
-
- private void restartPreviewAndDecode() {
- if (state == State.SUCCESS) {
- state = State.PREVIEW;
- CameraManager.get().requestPreviewFrame(decodeThread.getHandler(),
- R.id.decode);
- CameraManager.get().requestAutoFocus(this, R.id.auto_focus);
- activity.drawViewfinder();
- }
- }
(3)讲完了Handler消息回传可能大家还是不明白如何加码过程,接下来深入分析解码线程类 DecodeThread类,首先我们来看下这个类的构造函数,这里用到了 CountDownLatch 类这个类可能大家也不常用,我也是第一次接触,这里可以参考博客
http://blog.csdn.NET/shihuacai/article/details/8856370 讲的很详细,
CountDownLatch,一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
构造函数中用到 Vector
decodeFormats 集合,该集合主要是封装了解码格式,用到了DecodeFormatManager 解码格式管理类,稍后会讲到改类。
-
-
-
-
-
-
-
- DecodeThread(MipcaActivityCapture activity,
- Vector decodeFormats, String characterSet,
- ResultPointCallback resultPointCallback) {
-
- this.activity = activity;
-
-
-
-
-
-
-
-
-
-
- handlerInitLatch = new CountDownLatch(1);
-
- hints = new Hashtable(3);
-
-
- if (decodeFormats == null || decodeFormats.isEmpty()) {
- decodeFormats = new Vector();
- decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS);
- decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
- decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);
- }
-
-
-
-
-
-
- hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
-
- if (characterSet != null) {
- hints.put(DecodeHintType.CHARACTER_SET, characterSet);
- }
-
- hints.put(DecodeHintType.NEED_RESULT_POINT_CALLBACK,
- resultPointCallback);
- }
下面我们就看下线程类最关键模块线程体,这里是在线程中构造 DecodeHandler 类Handler ,下面还有一个函数处理Handler
- @Override
- public void run() {
-
-
-
-
-
-
-
-
-
-
-
- Looper.prepare();
-
- handler = new DecodeHandler(activity, hints);
-
-
-
-
-
-
-
-
-
-
-
-
- handlerInitLatch.countDown();
-
-
-
-
-
-
- Looper.loop();
- }
- Handler getHandler() {
- try {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- handlerInitLatch.await();
- } catch (InterruptedException ie) {
-
- }
- return handler;
- }
(4)这些Handler和线程在哪里发挥它的价值呢,接下来请看 CameraManager 相机管理类,在CaptureActivityHandler 构造类中有提到CameraManager类的函数调用,接下了深入了解这个类,这个类被封装成了单例类,那么在哪里初始化的呢,请看代码 CameraManager.init(getApplication()); 这行代码肯定很熟悉,在扫描二维码Activity的 onCreate()函数中出现过。 CameraManager 的get()函数提供了当前类的对象。
构造函数中提供了三个类 CameraConfigurationManager相机配置管理器类和 AutoFocusCallback 类回调接口用来通知自动对焦完成,PreviewCallback类 用于提供预览帧的副本的回调接口,稍后会讲到这些类。
-
-
-
-
-
-
- public static void init(Context context) {
- if (cameraManager == null) {
- cameraManager = new CameraManager(context);
- }
- }
-
-
-
-
-
-
- public static CameraManager get() {
- return cameraManager;
- }
-
- private CameraManager(Context context) {
-
- this.context = context;
- this.configManager = new CameraConfigurationManager(context);
-
-
-
-
-
-
-
- useOneShotPreviewCallback = Integer.parseInt(Build.VERSION.SDK) > 3;
-
-
-
- previewCallback = new PreviewCallback(configManager,
- useOneShotPreviewCallback);
- autoFocusCallback = new AutoFocusCallback();
- }
接下来我们就看下如何打开相机的,SurfaceHolder这个接口肯定不陌生,这个函数在相机初始化函数中有调用
initCamera(SurfaceHolder surfaceHolder),其实相机说拍摄的到的东西都是在SurfaceView上呈现在你眼前的,这里对 SurfaceView最关键的操作类SurfaceHolder 。这里有用到 CameraConfigurationManager相机配置管理类对象,稍后会讲到。
-
-
-
-
-
-
-
-
-
-
- public void openDriver(SurfaceHolder holder) throws IOException {
- if (camera == null) {
- camera = Camera.open();
- if (camera == null) {
- throw new IOException();
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- camera.setPreviewDisplay(holder);
-
- if (!initialized) {
- initialized = true;
-
- configManager.initFromCameraParameters(camera);
- }
- configManager.setDesiredCameraParameters(camera);
-
-
-
-
-
-
-
-
-
- FlashlightManager.enableFlashlight();
- }
- }
-
-
-
- public void closeDriver() {
- if (camera != null) {
- FlashlightManager.disableFlashlight();
- camera.release();
- camera = null;
- }
- }
接下来看相机的启用和关闭,这里主要是对相机进行操作,相机的绘制预览帧,及监听等
-
-
-
- public void closeDriver() {
- if (camera != null) {
- FlashlightManager.disableFlashlight();
- camera.release();
- camera = null;
- }
- }
-
-
-
-
- public void startPreview() {
- if (camera != null && !previewing) {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- camera.startPreview();
- previewing = true;
- }
- }
-
-
-
-
- public void stopPreview() {
- if (camera != null && previewing) {
- if (!useOneShotPreviewCallback) {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- camera.setPreviewCallback(null);
- }
- camera.stopPreview();
-
- previewCallback.setHandler(null, 0);
- autoFocusCallback.setHandler(null, 0);
- previewing = false;
- }
- }
下面看相机,执行对焦等相关函数 requestPreviewFrame() 一个单独的预览框将返回给处理程序提供的处理。在CaptureActivityHandler类的handleMessage()函数和 restartPreviewAndDecode()函数中有调用,用户解码失败后的重新对焦,和重新启动预览和解码时有调用。
requestAutoFocus()请求相机的硬件来执行自动对焦。与requestPreviewFrame()出现的位置同样有调用。
这里有讲到两个重要的监听类 PreviewCallback类:用于提供预览帧的副本的回调接口,AutoFocusCallback类: 回调接口用来通知自动对焦完成,这两个类是相机回调监听接口,提供了设置Handler和,回调函数。requestPreviewFramerequestPreviewFramerequestPreviewFramerequestPreviewFrame
下面 下
AutoFocusCallback 相机监听接口,对焦完成发送Handler消息是通知
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- final class AutoFocusCallback implements Camera.AutoFocusCallback {
-
- private static final String TAG = AutoFocusCallback.class.getSimpleName();
-
-
- private static final long AUTOFOCUS_INTERVAL_MS = 1500L;
-
- private Handler autoFocusHandler;
- private int autoFocusMessage;
-
- void setHandler(Handler autoFocusHandler, int autoFocusMessage) {
- this.autoFocusHandler = autoFocusHandler;
- this.autoFocusMessage = autoFocusMessage;
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- @Override
- public void onAutoFocus(boolean success, Camera camera) {
- if (autoFocusHandler != null) {
-
-
- Message message = autoFocusHandler.obtainMessage(autoFocusMessage,
- success);
- autoFocusHandler.sendMessageDelayed(message, AUTOFOCUS_INTERVAL_MS);
- autoFocusHandler = null;
- } else {
- Log.d(TAG, "自动对焦回调,但没有处理程序");
- }
- }
-
- }
PreviewCallback类监听接口,onPreviewFrame()函数:预览帧显示,拿到相机捕捉的画面和返回的byte[]字节数据。
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- final class PreviewCallback implements Camera.PreviewCallback {
-
- private static final String TAG = PreviewCallback.class.getSimpleName();
-
- private final CameraConfigurationManager configManager;
-
-
- private final boolean useOneShotPreviewCallback;
- private Handler previewHandler;
- private int previewMessage;
-
- PreviewCallback(CameraConfigurationManager configManager,
- boolean useOneShotPreviewCallback) {
- this.configManager = configManager;
- this.useOneShotPreviewCallback = useOneShotPreviewCallback;
- }
-
-
-
-
-
-
-
-
- void setHandler(Handler previewHandler, int previewMessage) {
- this.previewHandler = previewHandler;
- this.previewMessage = previewMessage;
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- public void onPreviewFrame(byte[] data, Camera camera) {
-
-
- Point cameraResolution = configManager.getCameraResolution();
-
-
- if (!useOneShotPreviewCallback) {
- camera.setPreviewCallback(null);
- }
-
-
- if (previewHandler != null) {
- Message message = previewHandler.obtainMessage(previewMessage,
- cameraResolution.x, cameraResolution.y, data);
- message.sendToTarget();
- previewHandler = null;
- } else {
- Log.d(TAG, "预览回调,但没有处理程序");
- Log.d(TAG, "Got preview callback, but no handler for it");
- }
- }
-
- }
接下来可以通过相机拿到屏幕相关参数,来处理捕捉到的数据,getFramingRect()通过计算屏幕分辨率啦计算坐标位置,
getFramingRectInPreview()函数还是在计算坐标,buildLuminanceSource()函数非常重要,功能就是拿到YUV预览框宽高。在指定的Rect坐标内进行剪裁,拿到预览字符串判断剪裁大小,最后生成 PlanarYUVLuminanceSource
类对象,这个类会将结果生成 Bitmap,该函数在 DecodeHandler类中调用,用于计算要扫描成功后要捕捉的图片。
-
-
-
-
-
- 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;
- }
-
-
-
-
-
- public Rect getFramingRectInPreview() {
- if (framingRectInPreview == null) {
- Rect rect = new Rect(getFramingRect());
-
-
- Point cameraResolution = configManager.getCameraResolution();
-
-
- Point screenResolution = configManager.getScreenResolution();
-
- 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;
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
- public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data,
- int width, int height) {
- Rect rect = getFramingRectInPreview();
- int previewFormat = configManager.getPreviewFormat();
- String previewFormatString = configManager.getPreviewFormatString();
- switch (previewFormat) {
-
-
-
-
-
-
- case PixelFormat.YCbCr_420_SP:
-
-
-
-
-
-
-
-
- case PixelFormat.YCbCr_422_SP:
- return new PlanarYUVLuminanceSource(data, width, height, rect.left,
- rect.top, rect.width(), rect.height());
-
- default:
-
-
-
-
-
-
-
-
-
-
- if ("yuv420p".equals(previewFormatString)) {
- return new PlanarYUVLuminanceSource(data, width, height,
- rect.left, rect.top, rect.width(), rect.height());
- }
- }
- throw new IllegalArgumentException("Unsupported picture format: "
- + previewFormat + '/' + previewFormatString);
- }
(4)讲到这里可能还是没有明白到底是如何解码的,接下进入解码DecodeHandler类,该类主要是提供了捕捉,二维码截图后的矩形图像,生成Bitmap图像。首先来看下构造函数,在哪里构造的,可能你并未发现,我告诉你在解码线程DecodeThread类 run()方法体中构造的,那在哪里调用的呢,就要看getHandler()函数在哪里有调用,看下图会发现我们有三处用到它,接下来细看每个位置。
- DecodeHandler(MipcaActivityCapture activity,
- Hashtable hints) {
- multiFormatReader = new MultiFormatReader();
- multiFormatReader.setHints(hints);
- this.activity = activity;
- }
- @Override
- public void run() {
-
-
-
-
-
-
-
-
-
-
-
- Looper.prepare();
-
- handler = new DecodeHandler(activity, hints);
-
-
-
-
-
-
-
-
-
-
-
-
- handlerInitLatch.countDown();
-
-
-
-
-
-
- Looper.loop();
- }
- Handler getHandler() {
- try {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- handlerInitLatch.await();
- } catch (InterruptedException ie) {
-
- }
- return handler;
- }
(4.1)我们来看第一处调用 handleMessage(Message message),代码块,这里调用了 CameraManager.get().requestPreviewFrame()类函数,接下来进入这个函数,看到这个代码块是不是很惊讶发现这是之前看到的模块,现在知道这个Handler是被谁调用的,被谁发消息的了吧,被PreviewCallback类监听接口发出的消息。
- case R.id.decode_failed:
-
-
-
- state = State.PREVIEW;
- CameraManager.get().requestPreviewFrame(decodeThread.getHandler(),
- R.id.decode);
- break;
-
-
-
-
-
-
-
-
-
-
-
- public void requestPreviewFrame(Handler handler, int message) {
- if (camera != null && previewing) {
- previewCallback.setHandler(handler, message);
- if (useOneShotPreviewCallback) {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- camera.setOneShotPreviewCallback(previewCallback);
- } else {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- camera.setPreviewCallback(previewCallback);
- }
- }
- }
(4.2)我们来看第二处调用,quitSynchronously()这个函数也不陌生,这是退出时调用的
-
-
-
- public void quitSynchronously() {
- state = State.DONE;
-
-
- CameraManager.get().stopPreview();
-
- Message quit = Message.obtain(decodeThread.getHandler(), R.id.quit);
-
-
-
-
-
- quit.sendToTarget();
- try {
-
-
-
-
-
-
-
-
-
-
- decodeThread.join();
- } catch (InterruptedException e) {
-
- }
-
-
- removeMessages(R.id.decode_succeeded);
- removeMessages(R.id.decode_failed);
- }
(4.3)我们来看第三处调用,restartPreviewAndDecode()重新启动预览和解码函数,其实执行的代码还是,(4.1)中讲到的 PreviewCallback监听接口,发送的Handler消息。
-
-
-
- private void restartPreviewAndDecode() {
- if (state == State.SUCCESS) {
- state = State.PREVIEW;
- CameraManager.get().requestPreviewFrame(decodeThread.getHandler(),
- R.id.decode);
- CameraManager.get().requestAutoFocus(this, R.id.auto_focus);
- activity.drawViewfinder();
- }
- }
(5)看完了相机解码流程,接下来看解码格式管理类 DecodeFormatManager类,该类封装了常用的一些条形码,二维码,商品码等一些格式结合。该类的其他几个函数并未使用,这里不做讲解。
-
- private static final Pattern COMMA_PATTERN = Pattern.compile(",");
-
-
- static final Vector PRODUCT_FORMATS;
-
-
- static final Vector ONE_D_FORMATS;
-
-
- static final Vector QR_CODE_FORMATS;
-
-
- static final Vector DATA_MATRIX_FORMATS;
-
- static {
- PRODUCT_FORMATS = new Vector(5);
- PRODUCT_FORMATS.add(BarcodeFormat.UPC_A);
- PRODUCT_FORMATS.add(BarcodeFormat.UPC_E);
- PRODUCT_FORMATS.add(BarcodeFormat.EAN_13);
- PRODUCT_FORMATS.add(BarcodeFormat.EAN_8);
- PRODUCT_FORMATS.add(BarcodeFormat.RSS14);
-
- ONE_D_FORMATS = new Vector(PRODUCT_FORMATS.size() + 4);
- ONE_D_FORMATS.addAll(PRODUCT_FORMATS);
- ONE_D_FORMATS.add(BarcodeFormat.CODE_39);
- ONE_D_FORMATS.add(BarcodeFormat.CODE_93);
- ONE_D_FORMATS.add(BarcodeFormat.CODE_128);
- ONE_D_FORMATS.add(BarcodeFormat.ITF);
-
- QR_CODE_FORMATS = new Vector(1);
- QR_CODE_FORMATS.add(BarcodeFormat.QR_CODE);
-
- DATA_MATRIX_FORMATS = new Vector(1);
- DATA_MATRIX_FORMATS.add(BarcodeFormat.DATA_MATRIX);
- }
(6)最后讲下相机如何配置 CameraConfigurationManager 相机配置管理器,改类中用到相机服务类 Camera.Parameters,这个类提供了相机设置,变焦等相关功能,注意这里用到很多parameters.get()函数,因为Parameters类中封装了一个字典,用于配置相机相关数据。改类提供了诸多函数和算法,但是最主要的功能是对外提供了相机分辨率 getCameraResolution()函数,屏幕分辨率getScreenResolution() 函数,预览格式getPreviewFormat()函数,获取预览格式字符串 getPreviewFormatString()函数等。其余函数是相关算法可以看代码理解
-
-
-
-
-
-
-
-
-
-
-
- final class CameraConfigurationManager {
-
- private static final String TAG = CameraConfigurationManager.class
- .getSimpleName();
-
-
- private static final int TEN_DESIRED_ZOOM = 27;
-
-
- private static final int DESIRED_SHARPNESS = 30;
-
-
-
-
-
-
-
-
-
-
-
-
-
- private static final Pattern COMMA_PATTERN = Pattern.compile(",");
-
- private final Context context;
-
-
- private Point screenResolution;
-
-
- private Point cameraResolution;
-
-
- private int previewFormat;
-
-
- private String previewFormatString;
-
- CameraConfigurationManager(Context context) {
- this.context = context;
- }
-
-
-
-
- void initFromCameraParameters(Camera camera) {
-
-
-
-
-
-
-
- Camera.Parameters parameters = camera.getParameters();
-
-
-
-
-
-
-
-
- previewFormat = parameters.getPreviewFormat();
-
-
-
-
-
-
- previewFormatString = parameters.get("preview-format");
-
-
- Log.d(TAG, "Default preview format: " + previewFormat + '/'
- + previewFormatString);
-
-
- WindowManager manager = (WindowManager) context
- .getSystemService(Context.WINDOW_SERVICE);
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Display display = manager.getDefaultDisplay();
-
-
- screenResolution = new Point(display.getWidth(), display.getHeight());
-
-
- Log.d(TAG, "Screen resolution: " + screenResolution);
-
-
- cameraResolution = getCameraResolution(parameters, screenResolution);
-
-
- Log.d(TAG, "Camera resolution: " + screenResolution);
- }
-
-
-
-
-
-
-
-
-
-
-
-
- void setDesiredCameraParameters(Camera camera) {
-
-
-
-
-
-
-
-
-
-
- Camera.Parameters parameters = camera.getParameters();
-
-
- Log.d(TAG, "Setting preview size: " + cameraResolution);
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);
-
- setFlash(parameters);
- setZoom(parameters);
-
-
-
-
-
-
- setDisplayOrientation(camera, 90);
- camera.setParameters(parameters);
- }
-
-
-
-
-
-
- Point getCameraResolution() {
- return cameraResolution;
- }
-
-
-
-
-
-
-
- Point getScreenResolution() {
- return screenResolution;
- }
-
-
-
-
-
-
- int getPreviewFormat() {
- return previewFormat;
- }
-
-
-
-
-
-
- String getPreviewFormatString() {
- return previewFormatString;
- }
-
-
-
-
-
-
-
-
-
-
- private static Point getCameraResolution(Camera.Parameters parameters,
- Point screenResolution) {
-
-
- String previewSizeValueString = parameters.get("preview-size-values");
-
-
- if (previewSizeValueString == null) {
- previewSizeValueString = parameters.get("preview-size-value");
- }
-
-
- Point cameraResolution = null;
-
- if (previewSizeValueString != null) {
-
-
- Log.d(TAG, "preview-size-values parameter: "
- + previewSizeValueString);
-
-
- cameraResolution = findBestPreviewSizeValue(previewSizeValueString,
- screenResolution);
- }
-
- if (cameraResolution == null) {
-
-
-
- cameraResolution = new Point((screenResolution.x >> 3) << 3,
- (screenResolution.y >> 3) << 3);
- }
-
- return cameraResolution;
- }
-
-
-
-
-
-
-
-
-
-
- private static Point findBestPreviewSizeValue(
- CharSequence previewSizeValueString, Point screenResolution) {
-
- int bestX = 0;
- int bestY = 0;
- int diff = Integer.MAX_VALUE;
-
-
- for (String previewSize : COMMA_PATTERN.split(previewSizeValueString)) {
-
- previewSize = previewSize.trim();
-
-
-
-
-
-
- int dimPosition = previewSize.indexOf('x');
-
- if (dimPosition < 0) {
-
-
- Log.w(TAG, "Bad preview-size: " + previewSize);
- continue;
- }
-
- int newX;
- int newY;
- try {
-
- newX = Integer.parseInt(previewSize.substring(0, dimPosition));
- newY = Integer.parseInt(previewSize.substring(dimPosition + 1));
- } catch (NumberFormatException nfe) {
-
-
- Log.w(TAG, "Bad preview-size: " + previewSize);
- continue;
- }
-
-
-
-
-
-
- int newDiff = Math.abs(newX - screenResolution.x)
- + Math.abs(newY - screenResolution.y);
-
- if (newDiff == 0) {
- bestX = newX;
- bestY = newY;
- break;
- } else if (newDiff < diff) {
- bestX = newX;
- bestY = newY;
- diff = newDiff;
- }
-
- }
-
-
-
-
- if (bestX > 0 && bestY > 0) {
- return new Point(bestX, bestY);
- }
- return null;
- }
-
-
-
-
-
-
-
-
-
-
- private static int findBestMotZoomValue(CharSequence stringValues,
- int tenDesiredZoom) {
-
- int tenBestValue = 0;
-
-
- for (String stringValue : COMMA_PATTERN.split(stringValues)) {
-
- stringValue = stringValue.trim();
-
- double value;
- try {
-
-
- value = Double.parseDouble(stringValue);
-
- } catch (NumberFormatException nfe) {
- return tenDesiredZoom;
- }
-
-
- int tenValue = (int) (10.0 * value);
-
-
- if (Math.abs(tenDesiredZoom - value) < Math.abs(tenDesiredZoom
- - tenBestValue)) {
-
- tenBestValue = tenValue;
- }
- }
- return tenBestValue;
- }
-
-
-
-
-
-
-
- private void setFlash(Camera.Parameters parameters) {
-
-
-
-
-
-
-
- if (Build.MODEL.contains("Behold II") && CameraManager.SDK_INT == 3) {
-
-
- parameters.set("flash-value", 1);
- } else {
- parameters.set("flash-value", 2);
- }
-
-
-
-
- parameters.set("flash-mode", "off");
- }
-
-
-
-
-
-
-
- private void setZoom(Camera.Parameters parameters) {
-
-
- String zoomSupportedString = parameters.get("zoom-supported");
-
-
- if (zoomSupportedString != null
- && !Boolean.parseBoolean(zoomSupportedString)) {
- return;
- }
-
-
- int tenDesiredZoom = TEN_DESIRED_ZOOM;
-
-
- String maxZoomString = parameters.get("max-zoom");
-
- if (maxZoomString != null) {
- try {
-
-
- int tenMaxZoom = (int) (10.0 * Double
- .parseDouble(maxZoomString));
-
-
- if (tenDesiredZoom > tenMaxZoom) {
- tenDesiredZoom = tenMaxZoom;
- }
-
- } catch (NumberFormatException nfe) {
-
-
- Log.w(TAG, "Bad max-zoom: " + maxZoomString);
- }
- }
-
-
- String takingPictureZoomMaxString = parameters
- .get("taking-picture-zoom-max");
-
- if (takingPictureZoomMaxString != null) {
-
- try {
-
-
- int tenMaxZoom = Integer.parseInt(takingPictureZoomMaxString);
-
-
- if (tenDesiredZoom > tenMaxZoom) {
- tenDesiredZoom = tenMaxZoom;
- }
-
- } catch (NumberFormatException nfe) {
-
-
- Log.w(TAG, "Bad taking-picture-zoom-max: "
- + takingPictureZoomMaxString);
- }
- }
-
-
- String motZoomValuesString = parameters.get("mot-zoom-values");
-
- if (motZoomValuesString != null) {
-
- tenDesiredZoom = findBestMotZoomValue(motZoomValuesString,
- tenDesiredZoom);
- }
-
-
- String motZoomStepString = parameters.get("mot-zoom-step");
-
- if (motZoomStepString != null) {
- try {
-
-
- double motZoomStep = Double.parseDouble(motZoomStepString
- .trim());
-
- int tenZoomStep = (int) (10.0 * motZoomStep);
-
- if (tenZoomStep > 1) {
- tenDesiredZoom -= tenDesiredZoom % tenZoomStep;
- }
- } catch (NumberFormatException nfe) {
-
- }
- }
-
-
-
- if (maxZoomString != null || motZoomValuesString != null) {
- parameters.set("zoom", String.valueOf(tenDesiredZoom / 10.0));
- }
-
-
-
- if (takingPictureZoomMaxString != null) {
- parameters.set("taking-picture-zoom", tenDesiredZoom);
- }
- }
-
-
-
-
-
-
- public static int getDesiredSharpness() {
- return DESIRED_SHARPNESS;
- }
-
-
-
-
-
-
-
-
-
-
- protected void setDisplayOrientation(Camera camera, int angle) {
- Method downPolymorphic;
- try {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- downPolymorphic = camera.getClass().getMethod(
- "setDisplayOrientation", new Class[] { int.class });
-
- if (downPolymorphic != null)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- downPolymorphic.invoke(camera, new Object[] { angle });
-
- } catch (Exception e1) {
- }
- }
-
- }
(7)最后讲下相机中散光灯如何使用,FlashlightManager类控制散光灯,该类提供了多种反射技术,拿到封装的对象和方法,来实现硬件功能,请看相关注解
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- final class FlashlightManager {
-
- private static final String TAG = FlashlightManager.class.getSimpleName();
-
-
- private static final Object iHardwareService;
-
-
- private static final Method setFlashEnabledMethod;
-
- static {
- iHardwareService = getHardwareService();
- setFlashEnabledMethod = getSetFlashEnabledMethod(iHardwareService);
- if (iHardwareService == null) {
- Log.v(TAG, "该设备支持一个手电筒的控制");
- } else {
- Log.v(TAG, "该设备不支持控制手电筒");
- }
- }
-
- private FlashlightManager() {
- }
-
-
-
-
-
- static void enableFlashlight() {
- setFlashlight(false);
- }
-
-
-
-
-
- static void disableFlashlight() {
- setFlashlight(false);
- }
-
- private static Object getHardwareService() {
-
-
- Class> serviceManagerClass = maybeForName("android.os.ServiceManager");
- if (serviceManagerClass == null) {
- return null;
- }
-
- Method getServiceMethod = maybeGetMethod(serviceManagerClass,
- "getService", String.class);
- if (getServiceMethod == null) {
- return null;
- }
-
- Object hardwareService = invoke(getServiceMethod, null, "hardware");
- if (hardwareService == null) {
- return null;
- }
-
- Class> iHardwareServiceStubClass = maybeForName("android.os.IHardwareService$Stub");
- if (iHardwareServiceStubClass == null) {
- return null;
- }
-
- Method asInterfaceMethod = maybeGetMethod(iHardwareServiceStubClass,
- "asInterface", IBinder.class);
- if (asInterfaceMethod == null) {
- return null;
- }
-
- return invoke(asInterfaceMethod, null, hardwareService);
- }
-
- private static Method getSetFlashEnabledMethod(Object iHardwareService) {
- if (iHardwareService == null) {
- return null;
- }
- Class> proxyClass = iHardwareService.getClass();
- return maybeGetMethod(proxyClass, "setFlashlightEnabled", boolean.class);
- }
-
- private static Class> maybeForName(String name) {
- try {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- return Class.forName(name);
- } catch (ClassNotFoundException cnfe) {
-
- return null;
- } catch (RuntimeException re) {
- Log.w(TAG, "Unexpected error while finding class " + name, re);
- return null;
- }
- }
-
- private static Method maybeGetMethod(Class> clazz, String name,
- Class>... argClasses) {
- try {
- return clazz.getMethod(name, argClasses);
- } catch (NoSuchMethodException nsme) {
-
- return null;
- } catch (RuntimeException re) {
- Log.w(TAG, "Unexpected error while finding method " + name, re);
- return null;
- }
- }
-
- private static Object invoke(Method method, Object instance, Object... args) {
- try {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- return method.invoke(instance, args);
- } catch (IllegalAccessException e) {
- Log.w(TAG, "Unexpected error while invoking " + method, e);
- return null;
- } catch (InvocationTargetException e) {
- Log.w(TAG, "Unexpected error while invoking " + method,
- e.getCause());
- return null;
- } catch (RuntimeException re) {
- Log.w(TAG, "Unexpected error while invoking " + method, re);
- return null;
- }
- }
-
- private static void setFlashlight(boolean active) {
- if (iHardwareService != null) {
- invoke(setFlashEnabledMethod, iHardwareService, active);
- }
- }
-
- }
(8)最后你会想如何实现扫描效果那个识别二位码控件如何实现,请看 ViewfinderView类,该类继承了View类,提供了绘制扫描控件 onDraw(Canvas canvas)函数
- @Override
- public void onDraw(Canvas canvas) {
-
- Rect frame = CameraManager.get().getFramingRect();
- if (frame == null) {
- return;
- }
-
-
- if (!isFirst) {
- isFirst = true;
- slideTop = frame.top;
- slideBottom = frame.bottom;
- }
-
-
- int width = canvas.getWidth();
- int height = canvas.getHeight();
-
- paint.setColor(resultBitmap != null ? resultColor : maskColor);
-
-
-
- canvas.drawRect(0, 0, width, frame.top, paint);
- canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);
- canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1,
- paint);
- canvas.drawRect(0, frame.bottom + 1, width, height, paint);
-
- if (resultBitmap != null) {
-
- paint.setAlpha(OPAQUE);
- canvas.drawBitmap(resultBitmap, frame.left, frame.top, paint);
- } else {
-
-
- paint.setColor(Color.GREEN);
- canvas.drawRect(frame.left, frame.top, frame.left + ScreenRate,
- frame.top + CORNER_WIDTH, paint);
- canvas.drawRect(frame.left, frame.top, frame.left + CORNER_WIDTH,
- frame.top + ScreenRate, paint);
- canvas.drawRect(frame.right - ScreenRate, frame.top, frame.right,
- frame.top + CORNER_WIDTH, paint);
- canvas.drawRect(frame.right - CORNER_WIDTH, frame.top, frame.right,
- frame.top + ScreenRate, paint);
- canvas.drawRect(frame.left, frame.bottom - CORNER_WIDTH, frame.left
- + ScreenRate, frame.bottom, paint);
- canvas.drawRect(frame.left, frame.bottom - ScreenRate, frame.left
- + CORNER_WIDTH, frame.bottom, paint);
- canvas.drawRect(frame.right - ScreenRate, frame.bottom
- - CORNER_WIDTH, frame.right, frame.bottom, paint);
- canvas.drawRect(frame.right - CORNER_WIDTH, frame.bottom
- - ScreenRate, frame.right, frame.bottom, paint);
-
-
- slideTop += SPEEN_DISTANCE;
- if (slideTop >= frame.bottom) {
- slideTop = frame.top;
- }
- canvas.drawRect(frame.left + MIDDLE_LINE_PADDING, slideTop
- - MIDDLE_LINE_WIDTH / 2, frame.right - MIDDLE_LINE_PADDING,
- slideTop + MIDDLE_LINE_WIDTH / 2, paint);
-
-
- paint.setColor(Color.WHITE);
- paint.setTextSize(TEXT_SIZE * density);
- paint.setAlpha(0x40);
- paint.setTypeface(Typeface.create("System", Typeface.BOLD));
- canvas.drawText(
- getResources().getString(R.string.scan_text),
- frame.left,
- (float) (frame.bottom + (float) TEXT_PADDING_TOP * density),
- paint);
-
- Collection currentPossible = possibleResultPoints;
- Collection currentLast = lastPossibleResultPoints;
- if (currentPossible.isEmpty()) {
- lastPossibleResultPoints = null;
- } else {
- possibleResultPoints = new HashSet(5);
- lastPossibleResultPoints = currentPossible;
- paint.setAlpha(OPAQUE);
- paint.setColor(resultPointColor);
- for (ResultPoint point : currentPossible) {
- canvas.drawCircle(frame.left + point.getX(), frame.top
- + point.getY(), 6.0f, paint);
- }
- }
- if (currentLast != null) {
- paint.setAlpha(OPAQUE / 2);
- paint.setColor(resultPointColor);
- for (ResultPoint point : currentLast) {
- canvas.drawCircle(frame.left + point.getX(), frame.top
- + point.getY(), 3.0f, paint);
- }
- }
-
-
- postInvalidateDelayed(ANIMATION_DELAY, frame.left, frame.top,
- frame.right, frame.bottom);
-
- }
- }
(9)其中还有几个相关辅助类没有贴上,ViewfinderResultPointCallback类,PlanarYUVLuminanceSource类,InactivityTimer类,FinishListener类,Intents类 这些可以详细看代码