当前位置: 代码迷 >> Android >> Android自定义相机照相、图片裁剪的实现
  详细解决方案

Android自定义相机照相、图片裁剪的实现

热度:86   发布时间:2016-04-27 22:27:34.0
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

  相关解决方案