android入门:zxing学习笔记(四)
Camera取景后显示于屏幕上,是个挺简单的过程,但这会出现各种意料不到的问题,例如之前说的屏幕横竖屏与预览图片之间的方向,图片拉伸,还有在Barcode Scanner中,简单的旋转了图片预览方向后,会出现特征点标记错位,等等。
第三篇简单的完成了相机的取景,还没有将取景的图片拍照存储下来。若想实现拍照的效果,则需要实现回调函数:Camera.PreviewCallback接口。接上一篇的代码,在此实现拍照的功能,将图片显示出来。之前一直在看Barcode Scanner的源码,并只是在其代码上修剪。当昨天自己来实现Camera的自动聚焦时,并遇到比较纠结的问题。在不出意外的情况下,Camera的使用还是挺简单的。
先在此贴出代码,最简单,代码经过了测试,正常运行,测试机是HTC MyTouch 3G slide。
需要的权限:
1 <uses-permission android:name="android.permission.CAMERA" />
2 <uses-feature android:name="android.hardware.camera" />
3 <uses-feature android:name="android.hardware.camera.autofocus" />
整个代码:
1 import java.io.ByteArrayOutputStream;
2 import java.io.IOException;
3 import java.util.Timer;
4 import java.util.TimerTask;
5
6 import android.app.Activity;
7 import android.content.Context;
8 import android.graphics.Bitmap;
9 import android.graphics.BitmapFactory;
10 import android.graphics.ImageFormat;
11 import android.graphics.Rect;
12 import android.graphics.YuvImage;
13 import android.hardware.Camera;
14 import android.os.Bundle;
15 import android.util.Log;
16 import android.view.Display;
17 import android.view.SurfaceHolder;
18 import android.view.SurfaceView;
19 import android.view.WindowManager;
20 import android.widget.ImageView;
21
22 public class CameraTestActivity extends Activity implements SurfaceHolder.Callback {
23 private static String TAG = CameraTestActivity.class.getSimpleName();
24 private SurfaceHolder surfaceHolder;
25 private Camera camera;
26 private ImageView imageView;
27 private Timer mTimer;
28 private TimerTask mTimerTask;
29
30 private Camera.AutoFocusCallback mAutoFocusCallBack;
31 private Camera.PreviewCallback previewCallback;
32
33 /** Called when the activity is first created. */
34 @Override
35 public void onCreate(Bundle savedInstanceState) {
36 super.onCreate(savedInstanceState);
37 setContentView(R.layout.main);
38 SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
39 imageView = (ImageView) findViewById(R.id.image_view);
40 surfaceHolder = surfaceView.getHolder();
41 surfaceHolder.addCallback(this);
42 surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
43 mAutoFocusCallBack = new Camera.AutoFocusCallback() {
44 @Override
45 public void onAutoFocus(boolean success, Camera camera) {
46 if (success) {
47 // isAutoFocus = true;
48 camera.setOneShotPreviewCallback(previewCallback);
49 Log.d(TAG, "onAutoFocus success");
50 }
51 }
52 };
53
54 previewCallback = new Camera.PreviewCallback() {
55 @Override
56 public void onPreviewFrame(byte[] data, Camera arg1) {
57 if (data != null)
58 {
59 Camera.Parameters parameters = camera.getParameters();
60 int imageFormat = parameters.getPreviewFormat();
61 Log.i("map", "Image Format: " + imageFormat);
62
63 Log.i("CameraPreviewCallback", "data length:" + data.length);
64 if (imageFormat == ImageFormat.NV21)
65 {
66 // get full picture
67 Bitmap image = null;
68 int w = parameters.getPreviewSize().width;
69 int h = parameters.getPreviewSize().height;
70
71 Rect rect = new Rect(0, 0, w, h);
72 YuvImage img = new YuvImage(data, ImageFormat.NV21, w, h, null);
73 ByteArrayOutputStream baos = new ByteArrayOutputStream();
74 if (img.compressToJpeg(rect, 100, baos))
75 {
76 image = BitmapFactory.decodeByteArray(baos.toByteArray(), 0, baos.size());
77 imageView.setImageBitmap(image);
78 }
79
80 }
81 }
82 }
83 };
84
85 mTimer = new Timer();
86 mTimerTask = new CameraTimerTask();
87 mTimer.schedule(mTimerTask, 0, 500);
88 }
89
90 @Override
91 public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
92 // TODO Auto-generated method stub
93 }
94
95 @Override
96 public void surfaceCreated(SurfaceHolder arg0) {
97 // TODO Auto-generated method stub
98 initCamera();
99 }
100
101 @Override
102 public void surfaceDestroyed(SurfaceHolder arg0) {
103 // TODO Auto-generated method stub
104 if (camera != null) {
105 camera.stopPreview();
106 camera.release();
107 camera = null;
108 }
109 previewCallback = null;
110 mAutoFocusCallBack = null;
111 }
112
113 public void initCamera() {
114 camera = Camera.open();
115 Camera.Parameters parameters = camera.getParameters();
116 WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE); // 获取当前屏幕管理器对象
117 Display display = wm.getDefaultDisplay(); // 获取屏幕信息的描述类
118 parameters.setPreviewSize(display.getWidth(), display.getHeight());
119 camera.setParameters(parameters);
120 try {
121 camera.setPreviewDisplay(surfaceHolder);
122 } catch (IOException e) {
123 System.out.println(e.getMessage());
124 }
125 camera.startPreview();
126 }
127
128 class CameraTimerTask extends TimerTask {
129 @Override
130 public void run() {
131 if (camera != null) {
132 camera.autoFocus(mAutoFocusCallBack);
133 }
134 }
135 }
136 }
与上一篇的简单预览相比,这篇增加了两个内容,一个是自动聚焦,一个是拍照。代码看上去很简单,没多少内容。但不亲自测试下,还会发现不少。
刚开始在Samsung S5570 galaxy mini上测试,总是不能成功的拍照。调试跟踪后,发现自动聚焦总是失败,聚焦失败就没有进行拍照操作。后面并尝试将自动聚焦代码注释掉,直接拍照,发现也是无法显示拍照的结果。之前的PreviewCallback的代码如下:
1 Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
这行代码返回的总是null,即bitmap没有成功生成。对这些代码本来就是拿来用,功能实现了,就行,对这些都只是简单的了解,当遇到bug后并百思不得其解。后来在网上几经查找发现原来是BitmapFactory.decodeByteArray只支持一定的格式,camara支持的previewformat格式为NV21,所以在获得bitmap时,需要进行转换。通过YuvImage类来转换成JPEG格式,再显示出来。具体讨论,请点这里。
解决照片的显示问题后,还有一个问题便是自动聚焦失败。上面特意强调了使用的事HTC 的手机测试成功,是因为之前在samsung s5570 上测试总是失败,拿到HTC的那款手机上立马成功。应该是三星的这款手机不支持自动聚焦。之前在这个三星手机上跑过Barcode Scanner,就自以为这手机能够自动聚焦,并一直在查找自己代码的原因。后面在仔细的读了Barcode Scanner的代码后,发现他得处理方式是:
1 CameraManager.get().requestPreviewFrame(decodeThread.getHandler(), R.id.decode);//实现拍照
2 CameraManager.get().requestAutoFocus(this, R.id.auto_focus);//实现聚焦
首先实现拍照,再是实现聚焦,并且重载的聚焦回调函数是隔一段时间再次发出聚焦的请求,实现不断的聚焦。
1 public void onAutoFocus(boolean success, Camera camera) {
2 if (autoFocusHandler != null) {
3 Message message = autoFocusHandler.obtainMessage(autoFocusMessage, success);
4 // Simulate continuous autofocus by sending a focus request every
5 // AUTOFOCUS_INTERVAL_MS milliseconds.
6 //Log.d(TAG, "Got auto-focus callback; requesting another");
7 autoFocusHandler.sendMessageDelayed(message, AUTOFOCUS_INTERVAL_MS);
8 autoFocusHandler = null;
9 } else {
10 Log.d(TAG, "Got auto-focus callback, but no handler for it");
11 }
12 }
聚焦于拍照之前没有先后的逻辑关系,聚焦为了拍照更清晰。这样,关于camera取景聚焦拍照的简单过程并如上了。
还有一个关键的点幷是回调函数。以前没有接触java代码,在看到很多接口监听处理的代码时,总是很困惑。譬如一段简单的button:
1 private final Button.OnClickListener addCardListener = new TextView.OnClickListener() {
2 @Override
3 public void onClick(View v) {
4 //在此实现button点击后的操作
5 }
6 };
如上的代码实现了点击监听,通过回调函数,当有点击操作时,并执行onClick函数。这就是一个简单的回调函数的使用。
关于回调函数请看这里,还有回调函数在android中得体现点这里。
大家的分享方便你我。