最近项目里面又要加一个拍照搜题的功能,也就是用户对着不会做的题目拍一张照片,将照片的文字使用ocr识别出来,再调用题库搜索接口搜索出来展示给用户,类似于小猿搜题、学霸君等app。
其实Android提供Intent让我们打开系统的相机,但是系统相机跟自己app风格不搭,而且用起来体验不好。所以我使用了SDK提供的camera API自定义了一个相机,并且在相机界面上面添加了参考线,有助于用户将题目拍正,提高ocr的识别率。
1、绘制参考线的代码
1 public class ReferenceLine extends View {
2
3 private Paint mLinePaint;
4
5 public ReferenceLine(Context context) {
6 super(context);
7 init();
8 }
9
10 public ReferenceLine(Context context, AttributeSet attrs) {
11 super(context, attrs);
12 init();
13 }
14
15 public ReferenceLine(Context context, AttributeSet attrs, int defStyleAttr) {
16 super(context, attrs, defStyleAttr);
17 init();
18 }
19
20 private void init() {
21 mLinePaint = new Paint();
22 mLinePaint.setAntiAlias(true);
23 mLinePaint.setColor(Color.parseColor(“#45e0e0e0”));
24 mLinePaint.setStrokeWidth(1);
25 }
26
27
28
29 @Override
30 protected void onDraw(Canvas canvas) {
31 int screenWidth = Utils.getScreenWH(getContext()).widthPixels;
32 int screenHeight = Utils.getScreenWH(getContext()).heightPixels;
33
34 int width = screenWidth/3;
35 int height = screenHeight/3;
36
37 for (int i = width, j = 0;i < screenWidth && j<2;i +="width," j++) {
38 canvas.drawLine(i, 0, i, screenHeight, mLinePaint);
39 }
40 for (int j = height,i = 0;j < screenHeight && i < 2;j += height,i++) {
41 canvas.drawLine(0, j, screenWidth, j, mLinePaint);
42 }
43 }
44
45
46 }
2、自定义相机代码
这里主要是要创建一个SurfaceView,将摄像头的预览界面放到SurfaceView中显示。
1 package com.bbk.lling.camerademo.camare;
2
3 import android.content.Context;
4 import android.content.res.Configuration;
5 import android.graphics.PixelFormat;
6 import android.graphics.Rect;
7 import android.hardware.Camera;
8 import android.hardware.Camera.AutoFocusCallback;
9 import android.hardware.Camera.PictureCallback;
10 import android.util.AttributeSet;
11 import android.util.Log;
12 import android.view.MotionEvent;
13 import android.view.SurfaceHolder;
14 import android.view.SurfaceView;
15 import android.view.View;
16 import android.widget.RelativeLayout;
17 import android.widget.Toast;
18
19 import com.bbk.lling.camerademo.utils.Utils;
20
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.Date;
24 import java.util.List;
25
26 /
27 @Class: CameraPreview
28 @Description: 自定义相机
29 @author: lling(www.cnblogs.com/liuling)
30 @Date: 2015/10/25
31 */
32 public class CameraPreview extends SurfaceView implements
33 SurfaceHolder.Callback, AutoFocusCallback {
34 private static final String TAG = “CameraPreview”;
35
36 private int viewWidth = 0;
37 private int viewHeight = 0;
38
39 / 监听接口 /
40 private OnCameraStatusListener listener;
41
42 private SurfaceHolder holder;
43 private Camera camera;
44 private FocusView mFocusView;
45
46 //创建一个PictureCallback对象,并实现其中的onPictureTaken方法
47 private PictureCallback pictureCallback = new PictureCallback() {
48
49 // 该方法用于处理拍摄后的照片数据
50 @Override
51 public void onPictureTaken(byte[] data, Camera camera) {
52 // 停止照片拍摄
53 try {
54 camera.stopPreview();
55 } catch (Exception e) {
56 }
57 // 调用结束事件
58 if (null != listener) {
59 listener.onCameraStopped(data);
60 }
61 }
62 };
63
64 // Preview类的构造方法
65 public CameraPreview(Context context, AttributeSet attrs) {
66 super(context, attrs);
67 // 获得SurfaceHolder对象
68 holder = getHolder();
69 // 指定用于捕捉拍照事件的SurfaceHolder.Callback对象
70 holder.addCallback(this);
71 // 设置SurfaceHolder对象的类型
72 holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
73 setOnTouchListener(onTouchListener);
74 }
75
76 // 在surface创建时激发
77 public void surfaceCreated(SurfaceHolder holder) {
78 Log.e(TAG, “==surfaceCreated==”);
79 if(!Utils.checkCameraHardware(getContext())) {
80 Toast.makeText(getContext(), “摄像头打开失败!”, Toast.LENGTH_SHORT).show();
81 return;
82 }
83 // 获得Camera对象
84 camera = getCameraInstance();
85 try {
86 // 设置用于显示拍照摄像的SurfaceHolder对象
87 camera.setPreviewDisplay(holder);
88 } catch (IOException e) {
89 e.printStackTrace();
90 // 释放手机摄像头
91 camera.release();
92 camera = null;
93 }
94 updateCameraParameters();
95 if (camera != null) {
96 camera.startPreview();
97 }
98 setFocus();
99 }
100
101 // 在surface销毁时激发
102 public void surfaceDestroyed(SurfaceHolder holder) {
103 Log.e(TAG, “==surfaceDestroyed==”);
104 // 释放手机摄像头
105 camera.release();
106 camera = null;
107 }
108
109 // 在surface的大小发生改变时激发
110 public void surfaceChanged(final SurfaceHolder holder, int format, int w,
111 int h) {
112 // stop preview before making changes
113 try {
114 camera.stopPreview();
115 } catch (Exception e){
116 // ignore: tried to stop a non-existent preview
117 }
118 // set preview size and make any resize, rotate or
119 // reformatting changes here
120 updateCameraParameters();
121 // start preview with new settings
122 try {
123 camera.setPreviewDisplay(holder);
124 camera.startPreview();
125
126 } catch (Exception e){
127 Log.d(TAG, “Error starting camera preview: “ + e.getMessage());
128 }
129 setFocus();
130 }
131
132 /**
133 点击显示焦点区域
134 /
135 OnTouchListener onTouchListener = new OnTouchListener() {
136 @SuppressWarnings(“deprecation”)
137 @Override
138 public boolean onTouch(View v, MotionEvent event) {
139 if (event.getAction() == MotionEvent.ACTION_DOWN) {
140 int width = mFocusView.getWidth();
141 int height = mFocusView.getHeight();
142 mFocusView.setX(event.getX() - (width / 2));
143 mFocusView.setY(event.getY() - (height / 2));
144 mFocusView.beginFocus();
145 } else if (event.getAction() == MotionEvent.ACTION_UP) {
146 focusOnTouch(event);
147 }
148 return true;
149 }
150 };
151
152 /**
153 获取摄像头实例
154 @return
155 /
156 private Camera getCameraInstance() {
157 Camera c = null;
158 try {
159 int cameraCount = 0;
160 Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
161 cameraCount = Camera.getNumberOfCameras(); // get cameras number
162
163 for (int camIdx = 0; camIdx < cameraCount; camIdx++) {
164 Camera.getCameraInfo(camIdx, cameraInfo); // get camerainfo
165 // 代表摄像头的方位,目前有定义值两个分别为CAMERA_FACING_FRONT前置和CAMERA_FACING_BACK后置
166 if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
167 try {
168 c = Camera.open(camIdx); //打开后置摄像头
169 } catch (RuntimeException e) {
170 Toast.makeText(getContext(), “摄像头打开失败!”, Toast.LENGTH_SHORT).show();
171 }
172 }
173 }
174 if (c == null) {
175 c = Camera.open(0); // attempt to get a Camera instance
176 }
177 } catch (Exception e) {
178 Toast.makeText(getContext(), “摄像头打开失败!”, Toast.LENGTH_SHORT).show();
179 }
180 return c;
181 }
182
183 private void updateCameraParameters() {
184 if (camera != null) {
185 Camera.Parameters p = camera.getParameters();
186
187 setParameters(p);
188
189 try {
190 camera.setParameters(p);
191 } catch (Exception e) {
192 Camera.Size previewSize = findBestPreviewSize(p);
193 p.setPreviewSize(previewSize.width, previewSize.height);
194 p.setPictureSize(previewSize.width, previewSize.height);
195 camera.setParameters(p);
196 }
197 }
198 }
199
200 /
201 @param p
202 /
203 private void setParameters(Camera.Parameters p) {
204 List focusModes = p.getSupportedFocusModes();
205 if (focusModes
206 .contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
207 p.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
208 }
209
210 long time = new Date().getTime();
211 p.setGpsTimestamp(time);
212 // 设置照片格式
213 p.setPictureFormat(PixelFormat.JPEG);
214 Camera.Size previewSize = findPreviewSizeByScreen(p);
215 p.setPreviewSize(previewSize.width, previewSize.height);
216 p.setPictureSize(previewSize.width, previewSize.height);
217 p.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
218 if (getContext().getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {
219 camera.setDisplayOrientation(90);
220 p.setRotation(90);
221 }
222 }
223
224 // 进行拍照,并将拍摄的照片传入PictureCallback接口的onPictureTaken方法
225 public void takePicture() {
226 if (camera != null) {
227 try {
228 camera.takePicture(null, null, pictureCallback);
229 } catch (Exception e) {
230 e.printStackTrace();
231 }
232 }
233 }
234
235 // 设置监听事件
236 public void setOnCameraStatusListener(OnCameraStatusListener listener) {
237 this.listener = listener;
238 }
239
240 @Override
241 public void onAutoFocus(boolean success, Camera camera) {
242
243 }
244
245 public void start() {
246 if (camera != null) {
247 camera.startPreview();
248 }
249 }
250
251 public void stop() {
252 if (camera != null) {
253 camera.stopPreview();
254 }
255 }
256
257 /
258 相机拍照监听接口
259 /
260 public interface OnCameraStatusListener {
261 // 相机拍照结束事件
262 void onCameraStopped(byte[] data);
263 }
264
265 @Override
266 protected void onMeasure(int widthSpec, int heightSpec) {
267 viewWidth = MeasureSpec.getSize(widthSpec);
268 viewHeight = MeasureSpec.getSize(heightSpec);
269 super.onMeasure(
270 MeasureSpec.makeMeasureSpec(viewWidth, MeasureSpec.EXACTLY),
271 MeasureSpec.makeMeasureSpec(viewHeight, MeasureSpec.EXACTLY));
272 }
273
274 /
275 将预览大小设置为屏幕大小
276 @param parameters
277 @return
278 /
279 private Camera.Size findPreviewSizeByScreen(Camera.Parameters parameters) {
280 if (viewWidth != 0 && viewHeight != 0) {
281 return camera.new Size(Math.max(viewWidth, viewHeight),
282 Math.min(viewWidth, viewHeight));
283 } else {
284 return camera.new Size(Utils.getScreenWH(getContext()).heightPixels,
285 Utils.getScreenWH(getContext()).widthPixels);
286 }
287 }
288
289 /
290 找到最合适的显示分辨率 (防止预览图像变形)
291 @param parameters
292 @return
293 /
294 private Camera.Size findBestPreviewSize(Camera.Parameters parameters) {
295
296 // 系统支持的所有预览分辨率
297 String previewSizeValueString = null;
298 previewSizeValueString = parameters.get(“preview-size-values”);
299
300 if (previewSizeValueString == null) {
301 previewSizeValueString = parameters.get(“preview-size-value”);
302 }
303
304 if (previewSizeValueString == null) { // 有些手机例如m9获取不到支持的预览大小 就直接返回屏幕大小
305 return camera.new Size(Utils.getScreenWH(getContext()).widthPixels,
306 Utils.getScreenWH(getContext()).heightPixels);
307 }
308 float bestX = 0;
309 float bestY = 0;
310
311 float tmpRadio = 0;
312 float viewRadio = 0;
313
314 if (viewWidth != 0 && viewHeight != 0) {
315 viewRadio = Math.min((float) viewWidth, (float) viewHeight)
316 / Math.max((float) viewWidth, (float) viewHeight);
317 }
318
319 String[] COMMA_PATTERN = previewSizeValueString.split(“,”);
320 for (String prewsizeString : COMMA_PATTERN) {
321 prewsizeString = prewsizeString.trim();
322
323 int dimPosition = prewsizeString.indexOf(‘x’);
324 if (dimPosition == -1) {
325 continue;
326 }
327
328 float newX = 0;
329 float newY = 0;
330
331 try {
332 newX = Float.parseFloat(prewsizeString.substring(0, dimPosition));
333 newY = Float.parseFloat(prewsizeString.substring(dimPosition + 1));
334 } catch (NumberFormatException e) {
335 continue;
336 }
337
338 float radio = Math.min(newX, newY) / Math.max(newX, newY);
339 if (tmpRadio == 0) {
340 tmpRadio = radio;
341 bestX = newX;
342 bestY = newY;
343 } else if (tmpRadio != 0 && (Math.abs(radio - viewRadio)) < (Math.abs(tmpRadio - viewRadio))) {
344 tmpRadio = radio;
345 bestX = newX;
346 bestY = newY;
347 }
348 }
349
350 if (bestX > 0 && bestY > 0) {
351 return camera.new Size((int) bestX, (int) bestY);
352 }
353 return null;
354 }
355
356 /
357 设置焦点和测光区域
358
359 @param event
360 /
361 public void focusOnTouch(MotionEvent event) {
362
363 int[] location = new int[2];
364 RelativeLayout relativeLayout = (RelativeLayout)getParent();
365 relativeLayout.getLocationOnScreen(location);
366
367 Rect focusRect = Utils.calculateTapArea(mFocusView.getWidth(),
368 mFocusView.getHeight(), 1f, event.getRawX(), event.getRawY(),
369 location[0], location[0] + relativeLayout.getWidth(), location[1],
370 location[1] + relativeLayout.getHeight());
371 Rect meteringRect = Utils.calculateTapArea(mFocusView.getWidth(),
372 mFocusView.getHeight(), 1.5f, event.getRawX(), event.getRawY(),
373 location[0], location[0] + relativeLayout.getWidth(), location[1],
374 location[1] + relativeLayout.getHeight());
375
376 Camera.Parameters parameters = camera.getParameters();
377 parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
378
379 if (parameters.getMaxNumFocusAreas() > 0) {
380 List focusAreas = new ArrayList();
381 focusAreas.add(new Camera.Area(focusRect, 1000));
382
383 parameters.setFocusAreas(focusAreas);
384 }
385
386 if (parameters.getMaxNumMeteringAreas() > 0) {
387 List meteringAreas = new ArrayList();
388 meteringAreas.add(new Camera.Area(meteringRect, 1000));
389
390 parameters.setMeteringAreas(meteringAreas);
391 }
392
393 try {
394 camera.setParameters(parameters);
395 } catch (Exception e) {
396 }
397 camera.autoFocus(this);
398 }
399
400 /
401 设置聚焦的图片
402 @param focusView
403 /
404 public void setFocusView(FocusView focusView) {
405 this.mFocusView = focusView;
406 }
407
408 /**
409 设置自动聚焦,并且聚焦的圈圈显示在屏幕中间位置
410 */
411 public void setFocus() {
412 if(!mFocusView.isFocusing()) {
413 try {
414 camera.autoFocus(this);
415 mFocusView.setX((Utils.getWidthInPx(getContext())-mFocusView.getWidth()) / 2);
416 mFocusView.setY((Utils.getHeightInPx(getContext())-mFocusView.getHeight()) / 2);
417 mFocusView.beginFocus();
418 } catch (Exception e) {
419 }
420 }
421 }
422
423 }
3、Activity中使用自定义相机
1 public class TakePhoteActivity extends Activity implements CameraPreview.OnCameraStatusListener,
2 SensorEventListener {
3 private static final String TAG = “TakePhoteActivity”;
4 public static final Uri IMAGE_URI = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
5 public static final String PATH = Environment.getExternalStorageDirectory()
6 .toString() + “/AndroidMedia/“;
7 CameraPreview mCameraPreview;
8 CropImageView mCropImageView;
9 RelativeLayout mTakePhotoLayout;
10 LinearLayout mCropperLayout;
11 @Override
12 protected void onCreate(Bundle savedInstanceState) {
13 super.onCreate(savedInstanceState);
14 // 设置横屏
15 // setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
16 // 设置全屏
17 requestWindowFeature(Window.FEATURE_NO_TITLE);
18 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
19 WindowManager.LayoutParams.FLAG_FULLSCREEN);
20 setContentView(R.layout.activity_take_phote);
21 // Initialize components of the app
22 mCropImageView = (CropImageView) findViewById(R.id.CropImageView);
23 mCameraPreview = (CameraPreview) findViewById(R.id.cameraPreview);
24 FocusView focusView = (FocusView) findViewById(R.id.view_focus);
25 mTakePhotoLayout = (RelativeLayout) findViewById(R.id.take_photo_layout);
26 mCropperLayout = (LinearLayout) findViewById(R.id.cropper_layout);
27
28 mCameraPreview.setFocusView(focusView);
29 mCameraPreview.setOnCameraStatusListener(this);
30 mCropImageView.setGuidelines(2);
31
32 mSensorManager = (SensorManager) getSystemService(Context.
33 SENSOR_SERVICE);
34 mAccel = mSensorManager.getDefaultSensor(Sensor.
35 TYPE_ACCELEROMETER);
36
37 }
38
39 boolean isRotated = false;
40
41 @Override
42 protected void onResume() {
43 super.onResume();
44 if(!isRotated) {
45 TextView hint_tv = (TextView) findViewById(R.id.hint);
46 ObjectAnimator animator = ObjectAnimator.ofFloat(hint_tv, “rotation”, 0f, 90f);
47 animator.setStartDelay(800);
48 animator.setDuration(1000);
49 animator.setInterpolator(new LinearInterpolator());
50 animator.start();
51 View view = findViewById(R.id.crop_hint);
52 AnimatorSet animSet = new AnimatorSet();
53 ObjectAnimator animator1 = ObjectAnimator.ofFloat(view, “rotation”, 0f, 90f);
54 ObjectAnimator moveIn = ObjectAnimator.ofFloat(view, “translationX”, 0f, -50f);
55 animSet.play(animator1).before(moveIn);
56 animSet.setDuration(10);
57 animSet.start();
58 isRotated = true;
59 }
60 mSensorManager.registerListener(this, mAccel, SensorManager.SENSOR_DELAY_UI);
61 }
62
63 @Override
64 protected void onPause() {
65 super.onPause();
66 mSensorManager.unregisterListener(this);
67 }
68
69 @Override
70 public void onConfigurationChanged(Configuration newConfig) {
71 Log.e(TAG, “onConfigurationChanged”);
72 super.onConfigurationChanged(newConfig);
73 }
74
75 public void takePhoto(View view) {
76 if(mCameraPreview != null) {
77 mCameraPreview.takePicture();
78 }
79 }
80
81 public void close(View view) {
82 finish();
83 }
84
85 /
86 关闭截图界面
87 @param view
88 */
89 public void closeCropper(View view) {
90 showTakePhotoLayout();
91 }
92
93 /
94 开始截图,并保存图片
95 @param view
96 /
97 public void startCropper(View view) {
98 //获取截图并旋转90度
99 CropperImage cropperImage = mCropImageView.getCroppedImage();
100 Log.e(TAG, cropperImage.getX() + “,” + cropperImage.getY());
101 Log.e(TAG, cropperImage.getWidth() + “,” + cropperImage.getHeight());
102 Bitmap bitmap = Utils.rotate(cropperImage.getBitmap(), -90);
103 // Bitmap bitmap = mCropImageView.getCroppedImage();
104 // 系统时间
105 long dateTaken = System.currentTimeMillis();
106 // 图像名称
107 String filename = DateFormat.format(“yyyy-MM-dd kk.mm.ss”, dateTaken)
108 .toString() + “.jpg”;
109 Uri uri = insertImage(getContentResolver(), filename, dateTaken, PATH,
110 filename, bitmap, null);
111 cropperImage.getBitmap().recycle();
112 cropperImage.setBitmap(null);
113 Intent intent = new Intent(this, ShowCropperedActivity.class);
114 intent.setData(uri);
115 intent.putExtra(“path”, PATH + filename);
116 intent.putExtra(“width”, bitmap.getWidth());
117 intent.putExtra(“height”, bitmap.getHeight());
118 intent.putExtra(“cropperImage”, cropperImage);
119 startActivity(intent);
120 bitmap.recycle();
121 finish();
122 super.overridePendingTransition(R.anim.fade_in,
123 R.anim.fade_out);
124 // doAnimation(cropperImage);
125 }
126
127 private void doAnimation(CropperImage cropperImage) {
128 ImageView imageView = new ImageView(this);
129 View view = LayoutInflater.from(this).inflate(
130 R.layout.image_view_layout, null);
131 ((RelativeLayout) view.findViewById(R.id.root_layout)).addView(imageView);
132 RelativeLayout relativeLayout = ((RelativeLayout) findViewById(R.id.root_layout));
133 // relativeLayout.addView(imageView);
134 imageView.setX(cropperImage.getX());
135 imageView.setY(cropperImage.getY());
136 ViewGroup.LayoutParams lp = imageView.getLayoutParams();
137 lp.width = (int)cropperImage.getWidth();
138 lp.height = (int) cropperImage.getHeight();
139 imageView.setLayoutParams(lp);
140 imageView.setImageBitmap(cropperImage.getBitmap());
141 try {
142 getWindow().addContentView(view, lp);
143 } catch (Exception e) {
144 e.printStackTrace();
145 }
146 /AnimatorSet animSet = new AnimatorSet();
147 ObjectAnimator translationX = ObjectAnimator.ofFloat(this, “translationX”, cropperImage.getX(), 0);
148 ObjectAnimator translationY = ObjectAnimator.ofFloat(this, “translationY”, cropperImage.getY(), 0);/
149
150 TranslateAnimation translateAnimation = new TranslateAnimation(
151 0, -cropperImage.getX(), 0, -(Math.abs(cropperImage.getHeight() - cropperImage.getY())));// 当前位置移动到指定位置
152 RotateAnimation rotateAnimation = new RotateAnimation(0, -90,
153 Animation.ABSOLUTE, cropperImage.getX() ,Animation.ABSOLUTE, cropperImage.getY());
154 AnimationSet animationSet = new AnimationSet(true);
155 animationSet.addAnimation(translateAnimation);
156 animationSet.addAnimation(rotateAnimation);
157 animationSet.setFillAfter(true);
158 animationSet.setDuration(2000L);
159 imageView.startAnimation(animationSet);
160 // finish();
161 }
162
163 /**
164 拍照成功后回调
165 存储图片并显示截图界面
166 @param data
167 /
168 @Override
169 public void onCameraStopped(byte[] data) {
170 Log.i(“TAG”, “==onCameraStopped==”);
171 // 创建图像
172 Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
173 // 系统时间
174 long dateTaken = System.currentTimeMillis();
175 // 图像名称
176 String filename = DateFormat.format(“yyyy-MM-dd kk.mm.ss”, dateTaken)
177 .toString() + “.jpg”;
178 // 存储图像(PATH目录)
179 Uri source = insertImage(getContentResolver(), filename, dateTaken, PATH,
180 filename, bitmap, data);
181 //准备截图
182 try {
183 mCropImageView.setImageBitmap(MediaStore.Images.Media.getBitmap(this.getContentResolver(), source));
184 // mCropImageView.rotateImage(90);
185 } catch (IOException e) {
186 Log.e(TAG, e.getMessage());
187 }
188 showCropperLayout();
189 }
190
191 /**
192 存储图像并将信息添加入媒体数据库
193 */
194 private Uri insertImage(ContentResolver cr, String name, long dateTaken,
195 String directory, String filename, Bitmap source, byte[] jpegData) {
196 OutputStream outputStream = null;
197 String filePath = directory + filename;
198 try {
199 File dir = new File(directory);
200 if (!dir.exists()) {
201 dir.mkdirs();
202 }
203 File file = new File(directory, filename);
204 if (file.createNewFile()) {
205 outputStream = new FileOutputStream(file);
206 if (source != null) {
207 source.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
208 } else {
209 outputStream.write(jpegData);
210 }
211 }
212 } catch (FileNotFoundException e) {
213 Log.e(TAG, e.getMessage());
214 return null;
215 } catch (IOException e) {
216 Log.e(TAG, e.getMessage());
217 return null;
218 } finally {
219 if (outputStream != null) {
220 try {
221 outputStream.close();
222 } catch (Throwable t) {
223 }
224 }
225 }
226 ContentValues values = new ContentValues(7);
227 values.put(MediaStore.Images.Media.TITLE, name);
228 values.put(MediaStore.Images.Media.DISPLAY_NAME, filename);
229 values.put(MediaStore.Images.Media.DATE_TAKEN, dateTaken);
230 values.put(MediaStore.Images.Media.MIME_TYPE, “image/jpeg”);
231 values.put(MediaStore.Images.Media.DATA, filePath);
232 return cr.insert(IMAGE_URI, values);
233 }
234
235 private void showTakePhotoLayout() {
236 mTakePhotoLayout.setVisibility(View.VISIBLE);
237 mCropperLayout.setVisibility(View.GONE);
238 }
239
240 private void showCropperLayout() {
241 mTakePhotoLayout.setVisibility(View.GONE);
242 mCropperLayout.setVisibility(View.VISIBLE);
243 mCameraPreview.start(); //继续启动摄像头
244 }
245
246
247 private float mLastX = 0;
248 private float mLastY = 0;
249 private float mLastZ = 0;
250 private boolean mInitialized = false;
251 private SensorManager mSensorManager;
252 private Sensor mAccel;
253 @Override
254 public void onSensorChanged(SensorEvent event) {
255
256 float x = event.values[0];
257 float y = event.values[1];
258 float z = event.values[2];
259 if (!mInitialized){
260 mLastX = x;
261 mLastY = y;
262 mLastZ = z;
263 mInitialized = true;
264 }
265 float deltaX = Math.abs(mLastX - x);
266 float deltaY = Math.abs(mLastY - y);
267 float deltaZ = Math.abs(mLastZ - z);
268
269 if(deltaX > 0.8 || deltaY > 0.8 || deltaZ > 0.8){
270 mCameraPreview.setFocus();
271 }
272 mLastX = x;
273 mLastY = y;
274 mLastZ = z;
275 }
276
277 @Override
278 public void onAccuracyChanged(Sensor sensor, int accuracy) {
279 }
280 }
actiity中注册了SensorEventListener,也就是使用传感器监听用户手机的移动,如果有一定距离的移动,则自动聚焦,这样体验好一点。
我对比了一下小猿搜题和学霸君两款app的拍照功能,个人感觉小猿搜题的体验
要好一些,因为从主界面进入拍照界面,连个界面没有一个旋转的过渡,而学霸君就有一个过渡,有一丝丝的影响体验。也就是说学霸君的拍照界面是横屏的,在
activity的onCreate方法里面调用了setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)来设置全屏,而切换界面的时候又从竖屏切换为横屏,就会有个过渡的效果,影响了体验。
个人猜测小猿搜题是将拍照界面的activity设置为竖屏,而将摄像头直接旋转90度,这样就强制用户横屏拍摄,当然,拍完之后还要将图片旋转回来。所以我参考小猿搜题来实现的,毕竟体验为王嘛。
如上图(其实是竖屏),红色圈起来的其实是放到底部,然后将屏幕中间的文字旋转90度(带有动画,起了提示用户横屏拍照的作用),就给人的感觉是横屏的。了。
还有一点就是小猿搜题拍完照到截图过渡的很自然,感觉很流畅,估计是拍照和截
图放在同一个activity中的,如果是两个activty,涉及到界面切换,肯定不会那么自然。所以我也将拍照和截图放在一个界面,拍照完就将自定义
相机隐藏,将截图界面显示出来,这样切换就很流畅了。
项目中截图的功能我是从github上面找的一个开源库cropper:https://github.com/edmodo/cropper
因为ocr图片识别的代码是公司的,所以识别的功能没有添加到demo里面去。
Demo源码下载:https://github.com/liuling07/CustomCameraDemo