• 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     @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<String> 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<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 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

  • 相关阅读:
    常用 SQL 语句
    Matalab IFS分形算法
    波粒二象性
    动态下载 Yahoo 网络数据存入 Microsoft SQL Server 再 Matlab 调用的一个完整例子
    4.交易测试
    协议 protocol
    iOS开发使用半透明模糊效果方法整理
    iOS App集成Apple Pay教程(附示例代码)
    iOS中四种实例变量的范围类型@private@protected@public@package
    隐藏TabBar的一些方法小结(适用与各种情况)
  • 原文地址:https://www.cnblogs.com/liuling/p/2015-10-28-01.html
Copyright © 2020-2023  润新知