你的位置:首页 > 操作系统

[操作系统]Android自定义相机拍照、图片裁剪的实现


  最近项目里面又要加一个拍照搜题的功能,也就是用户对着不会做的题目拍一张照片,将照片的文字使用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   @Override30   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 changes113     try {114       camera.stopPreview();115     } catch (Exception e){116       // ignore: tried to stop a non-existent preview117     }118     // set preview size and make any resize, rotate or119     // reformatting changes here120     updateCameraParameters();121     // start preview with new settings122     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     @Override138     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    * @return155   */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 number162 163       for (int camIdx = 0; camIdx < cameraCount; camIdx++) {164         Camera.getCameraInfo(camIdx, cameraInfo); // get camerainfo165         // 代表摄像头的方位,目前有定义值两个分别为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 instance176       }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 p202   */203   private void setParameters(Camera.Parameters p) {204     List<String> focusModes = p.getSupportedFocusModes();205     if (focusModes206         .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   @Override241   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   @Override266   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 parameters277    * @return278   */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 parameters292    * @return293   */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 event360   */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<Camera.Area> focusAreas = new ArrayList<Camera.Area>();381       focusAreas.add(new Camera.Area(focusRect, 1000));382 383       parameters.setFocusAreas(focusAreas);384     }385 386     if (parameters.getMaxNumMeteringAreas() > 0) {387       List<Camera.Area> meteringAreas = new ArrayList<Camera.Area>();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 focusView403   */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 data167   */168   @Override169   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   @Override254   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   @Override278   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