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

时间:2021-04-29 15:28:09

  最近项目里面又要加一个拍照搜题的功能,也就是用户对着不会做的题目拍一张照片,将照片的文字使用ocr识别出来,再调用题库搜索接口搜索出来展示给用户,类似于小猿搜题、学霸君等app。

  其实Android提供Intent让我们打开系统的相机,但是系统相机跟自己app风格不搭,而且用起来体验不好。所以我使用了SDK提供的camera API自定义了一个相机,并且在相机界面上面添加了参考线,有助于用户将题目拍正,提高ocr的识别率。

  1、绘制参考线的代码

 public class ReferenceLine extends View {

     private Paint mLinePaint;

     public ReferenceLine(Context context) {
super(context);
init();
} public ReferenceLine(Context context, AttributeSet attrs) {
super(context, attrs);
init();
} public ReferenceLine(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
} private void init() {
mLinePaint = new Paint();
mLinePaint.setAntiAlias(true);
mLinePaint.setColor(Color.parseColor("#45e0e0e0"));
mLinePaint.setStrokeWidth(1);
} @Override
protected void onDraw(Canvas canvas) {
int screenWidth = Utils.getScreenWH(getContext()).widthPixels;
int screenHeight = Utils.getScreenWH(getContext()).heightPixels; int width = screenWidth/3;
int height = screenHeight/3; for (int i = width, j = 0;i < screenWidth && j<2;i += width, j++) {
canvas.drawLine(i, 0, i, screenHeight, mLinePaint);
}
for (int j = height,i = 0;j < screenHeight && i < 2;j += height,i++) {
canvas.drawLine(0, j, screenWidth, j, mLinePaint);
}
} }

  2、自定义相机代码

  这里主要是要创建一个SurfaceView,将摄像头的预览界面放到SurfaceView中显示。

 package com.bbk.lling.camerademo.camare;

 import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.PictureCallback;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.Toast; import com.bbk.lling.camerademo.utils.Utils; import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List; /**
* @Class: CameraPreview
* @Description: 自定义相机
* @author: lling(www.cnblogs.com/liuling)
* @Date: 2015/10/25
*/
public class CameraPreview extends SurfaceView implements
SurfaceHolder.Callback, AutoFocusCallback {
private static final String TAG = "CameraPreview"; private int viewWidth = 0;
private int viewHeight = 0; /** 监听接口 */
private OnCameraStatusListener listener; private SurfaceHolder holder;
private Camera camera;
private FocusView mFocusView; //创建一个PictureCallback对象,并实现其中的onPictureTaken方法
private PictureCallback pictureCallback = new PictureCallback() { // 该方法用于处理拍摄后的照片数据
@Override
public void onPictureTaken(byte[] data, Camera camera) {
// 停止照片拍摄
try {
camera.stopPreview();
} catch (Exception e) {
}
// 调用结束事件
if (null != listener) {
listener.onCameraStopped(data);
}
}
}; // Preview类的构造方法
public CameraPreview(Context context, AttributeSet attrs) {
super(context, attrs);
// 获得SurfaceHolder对象
holder = getHolder();
// 指定用于捕捉拍照事件的SurfaceHolder.Callback对象
holder.addCallback(this);
// 设置SurfaceHolder对象的类型
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
setOnTouchListener(onTouchListener);
} // 在surface创建时激发
public void surfaceCreated(SurfaceHolder holder) {
Log.e(TAG, "==surfaceCreated==");
if(!Utils.checkCameraHardware(getContext())) {
Toast.makeText(getContext(), "摄像头打开失败!", Toast.LENGTH_SHORT).show();
return;
}
// 获得Camera对象
camera = getCameraInstance();
try {
// 设置用于显示拍照摄像的SurfaceHolder对象
camera.setPreviewDisplay(holder);
} catch (IOException e) {
e.printStackTrace();
// 释放手机摄像头
camera.release();
camera = null;
}
updateCameraParameters();
if (camera != null) {
camera.startPreview();
}
setFocus();
} // 在surface销毁时激发
public void surfaceDestroyed(SurfaceHolder holder) {
Log.e(TAG, "==surfaceDestroyed==");
// 释放手机摄像头
camera.release();
camera = null;
} // 在surface的大小发生改变时激发
public void surfaceChanged(final SurfaceHolder holder, int format, int w,
int h) {
// stop preview before making changes
try {
camera.stopPreview();
} catch (Exception e){
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or
// reformatting changes here
updateCameraParameters();
// start preview with new settings
try {
camera.setPreviewDisplay(holder);
camera.startPreview(); } catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
setFocus();
} /**
* 点击显示焦点区域
*/
OnTouchListener onTouchListener = new OnTouchListener() {
@SuppressWarnings("deprecation")
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
int width = mFocusView.getWidth();
int height = mFocusView.getHeight();
mFocusView.setX(event.getX() - (width / 2));
mFocusView.setY(event.getY() - (height / 2));
mFocusView.beginFocus();
} else if (event.getAction() == MotionEvent.ACTION_UP) {
focusOnTouch(event);
}
return true;
}
}; /**
* 获取摄像头实例
* @return
*/
private Camera getCameraInstance() {
Camera c = null;
try {
int cameraCount = 0;
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
cameraCount = Camera.getNumberOfCameras(); // get cameras number for (int camIdx = 0; camIdx < cameraCount; camIdx++) {
Camera.getCameraInfo(camIdx, cameraInfo); // get camerainfo
// 代表摄像头的方位,目前有定义值两个分别为CAMERA_FACING_FRONT前置和CAMERA_FACING_BACK后置
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
try {
c = Camera.open(camIdx); //打开后置摄像头
} catch (RuntimeException e) {
Toast.makeText(getContext(), "摄像头打开失败!", Toast.LENGTH_SHORT).show();
}
}
}
if (c == null) {
c = Camera.open(0); // attempt to get a Camera instance
}
} catch (Exception e) {
Toast.makeText(getContext(), "摄像头打开失败!", Toast.LENGTH_SHORT).show();
}
return c;
} private void updateCameraParameters() {
if (camera != null) {
Camera.Parameters p = camera.getParameters(); setParameters(p); try {
camera.setParameters(p);
} catch (Exception e) {
Camera.Size previewSize = findBestPreviewSize(p);
p.setPreviewSize(previewSize.width, previewSize.height);
p.setPictureSize(previewSize.width, previewSize.height);
camera.setParameters(p);
}
}
} /**
* @param p
*/
private void setParameters(Camera.Parameters p) {
List<String> focusModes = p.getSupportedFocusModes();
if (focusModes
.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
p.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
} long time = new Date().getTime();
p.setGpsTimestamp(time);
// 设置照片格式
p.setPictureFormat(PixelFormat.JPEG);
Camera.Size previewSize = findPreviewSizeByScreen(p);
p.setPreviewSize(previewSize.width, previewSize.height);
p.setPictureSize(previewSize.width, previewSize.height);
p.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
if (getContext().getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {
camera.setDisplayOrientation(90);
p.setRotation(90);
}
} // 进行拍照,并将拍摄的照片传入PictureCallback接口的onPictureTaken方法
public void takePicture() {
if (camera != null) {
try {
camera.takePicture(null, null, pictureCallback);
} catch (Exception e) {
e.printStackTrace();
}
}
} // 设置监听事件
public void setOnCameraStatusListener(OnCameraStatusListener listener) {
this.listener = listener;
} @Override
public void onAutoFocus(boolean success, Camera camera) { } public void start() {
if (camera != null) {
camera.startPreview();
}
} public void stop() {
if (camera != null) {
camera.stopPreview();
}
} /**
* 相机拍照监听接口
*/
public interface OnCameraStatusListener {
// 相机拍照结束事件
void onCameraStopped(byte[] data);
} @Override
protected void onMeasure(int widthSpec, int heightSpec) {
viewWidth = MeasureSpec.getSize(widthSpec);
viewHeight = MeasureSpec.getSize(heightSpec);
super.onMeasure(
MeasureSpec.makeMeasureSpec(viewWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(viewHeight, MeasureSpec.EXACTLY));
} /**
* 将预览大小设置为屏幕大小
* @param parameters
* @return
*/
private Camera.Size findPreviewSizeByScreen(Camera.Parameters parameters) {
if (viewWidth != 0 && viewHeight != 0) {
return camera.new Size(Math.max(viewWidth, viewHeight),
Math.min(viewWidth, viewHeight));
} else {
return camera.new Size(Utils.getScreenWH(getContext()).heightPixels,
Utils.getScreenWH(getContext()).widthPixels);
}
} /**
* 找到最合适的显示分辨率 (防止预览图像变形)
* @param parameters
* @return
*/
private Camera.Size findBestPreviewSize(Camera.Parameters parameters) { // 系统支持的所有预览分辨率
String previewSizeValueString = null;
previewSizeValueString = parameters.get("preview-size-values"); if (previewSizeValueString == null) {
previewSizeValueString = parameters.get("preview-size-value");
} if (previewSizeValueString == null) { // 有些手机例如m9获取不到支持的预览大小 就直接返回屏幕大小
return camera.new Size(Utils.getScreenWH(getContext()).widthPixels,
Utils.getScreenWH(getContext()).heightPixels);
}
float bestX = 0;
float bestY = 0; float tmpRadio = 0;
float viewRadio = 0; if (viewWidth != 0 && viewHeight != 0) {
viewRadio = Math.min((float) viewWidth, (float) viewHeight)
/ Math.max((float) viewWidth, (float) viewHeight);
} String[] COMMA_PATTERN = previewSizeValueString.split(",");
for (String prewsizeString : COMMA_PATTERN) {
prewsizeString = prewsizeString.trim(); int dimPosition = prewsizeString.indexOf('x');
if (dimPosition == -1) {
continue;
} float newX = 0;
float newY = 0; try {
newX = Float.parseFloat(prewsizeString.substring(0, dimPosition));
newY = Float.parseFloat(prewsizeString.substring(dimPosition + 1));
} catch (NumberFormatException e) {
continue;
} float radio = Math.min(newX, newY) / Math.max(newX, newY);
if (tmpRadio == 0) {
tmpRadio = radio;
bestX = newX;
bestY = newY;
} else if (tmpRadio != 0 && (Math.abs(radio - viewRadio)) < (Math.abs(tmpRadio - viewRadio))) {
tmpRadio = radio;
bestX = newX;
bestY = newY;
}
} if (bestX > 0 && bestY > 0) {
return camera.new Size((int) bestX, (int) bestY);
}
return null;
} /**
* 设置焦点和测光区域
*
* @param event
*/
public void focusOnTouch(MotionEvent event) { int[] location = new int[2];
RelativeLayout relativeLayout = (RelativeLayout)getParent();
relativeLayout.getLocationOnScreen(location); Rect focusRect = Utils.calculateTapArea(mFocusView.getWidth(),
mFocusView.getHeight(), 1f, event.getRawX(), event.getRawY(),
location[0], location[0] + relativeLayout.getWidth(), location[1],
location[1] + relativeLayout.getHeight());
Rect meteringRect = Utils.calculateTapArea(mFocusView.getWidth(),
mFocusView.getHeight(), 1.5f, event.getRawX(), event.getRawY(),
location[0], location[0] + relativeLayout.getWidth(), location[1],
location[1] + relativeLayout.getHeight()); Camera.Parameters parameters = camera.getParameters();
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); if (parameters.getMaxNumFocusAreas() > 0) {
List<Camera.Area> focusAreas = new ArrayList<Camera.Area>();
focusAreas.add(new Camera.Area(focusRect, 1000)); parameters.setFocusAreas(focusAreas);
} if (parameters.getMaxNumMeteringAreas() > 0) {
List<Camera.Area> meteringAreas = new ArrayList<Camera.Area>();
meteringAreas.add(new Camera.Area(meteringRect, 1000)); parameters.setMeteringAreas(meteringAreas);
} try {
camera.setParameters(parameters);
} catch (Exception e) {
}
camera.autoFocus(this);
} /**
* 设置聚焦的图片
* @param focusView
*/
public void setFocusView(FocusView focusView) {
this.mFocusView = focusView;
} /**
* 设置自动聚焦,并且聚焦的圈圈显示在屏幕中间位置
*/
public void setFocus() {
if(!mFocusView.isFocusing()) {
try {
camera.autoFocus(this);
mFocusView.setX((Utils.getWidthInPx(getContext())-mFocusView.getWidth()) / 2);
mFocusView.setY((Utils.getHeightInPx(getContext())-mFocusView.getHeight()) / 2);
mFocusView.beginFocus();
} catch (Exception e) {
}
}
} }

  3、Activity中使用自定义相机

 public class TakePhoteActivity extends Activity implements CameraPreview.OnCameraStatusListener,
SensorEventListener {
private static final String TAG = "TakePhoteActivity";
public static final Uri IMAGE_URI = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
public static final String PATH = Environment.getExternalStorageDirectory()
.toString() + "/AndroidMedia/";
CameraPreview mCameraPreview;
CropImageView mCropImageView;
RelativeLayout mTakePhotoLayout;
LinearLayout mCropperLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置横屏
// setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
// 设置全屏
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_take_phote);
// Initialize components of the app
mCropImageView = (CropImageView) findViewById(R.id.CropImageView);
mCameraPreview = (CameraPreview) findViewById(R.id.cameraPreview);
FocusView focusView = (FocusView) findViewById(R.id.view_focus);
mTakePhotoLayout = (RelativeLayout) findViewById(R.id.take_photo_layout);
mCropperLayout = (LinearLayout) findViewById(R.id.cropper_layout); mCameraPreview.setFocusView(focusView);
mCameraPreview.setOnCameraStatusListener(this);
mCropImageView.setGuidelines(2); mSensorManager = (SensorManager) getSystemService(Context.
SENSOR_SERVICE);
mAccel = mSensorManager.getDefaultSensor(Sensor.
TYPE_ACCELEROMETER); } boolean isRotated = false; @Override
protected void onResume() {
super.onResume();
if(!isRotated) {
TextView hint_tv = (TextView) findViewById(R.id.hint);
ObjectAnimator animator = ObjectAnimator.ofFloat(hint_tv, "rotation", 0f, 90f);
animator.setStartDelay(800);
animator.setDuration(1000);
animator.setInterpolator(new LinearInterpolator());
animator.start();
View view = findViewById(R.id.crop_hint);
AnimatorSet animSet = new AnimatorSet();
ObjectAnimator animator1 = ObjectAnimator.ofFloat(view, "rotation", 0f, 90f);
ObjectAnimator moveIn = ObjectAnimator.ofFloat(view, "translationX", 0f, -50f);
animSet.play(animator1).before(moveIn);
animSet.setDuration(10);
animSet.start();
isRotated = true;
}
mSensorManager.registerListener(this, mAccel, SensorManager.SENSOR_DELAY_UI);
} @Override
protected void onPause() {
super.onPause();
mSensorManager.unregisterListener(this);
} @Override
public void onConfigurationChanged(Configuration newConfig) {
Log.e(TAG, "onConfigurationChanged");
super.onConfigurationChanged(newConfig);
} public void takePhoto(View view) {
if(mCameraPreview != null) {
mCameraPreview.takePicture();
}
} public void close(View view) {
finish();
} /**
* 关闭截图界面
* @param view
*/
public void closeCropper(View view) {
showTakePhotoLayout();
} /**
* 开始截图,并保存图片
* @param view
*/
public void startCropper(View view) {
//获取截图并旋转90度
CropperImage cropperImage = mCropImageView.getCroppedImage();
Log.e(TAG, cropperImage.getX() + "," + cropperImage.getY());
Log.e(TAG, cropperImage.getWidth() + "," + cropperImage.getHeight());
Bitmap bitmap = Utils.rotate(cropperImage.getBitmap(), -90);
// Bitmap bitmap = mCropImageView.getCroppedImage();
// 系统时间
long dateTaken = System.currentTimeMillis();
// 图像名称
String filename = DateFormat.format("yyyy-MM-dd kk.mm.ss", dateTaken)
.toString() + ".jpg";
Uri uri = insertImage(getContentResolver(), filename, dateTaken, PATH,
filename, bitmap, null);
cropperImage.getBitmap().recycle();
cropperImage.setBitmap(null);
Intent intent = new Intent(this, ShowCropperedActivity.class);
intent.setData(uri);
intent.putExtra("path", PATH + filename);
intent.putExtra("width", bitmap.getWidth());
intent.putExtra("height", bitmap.getHeight());
intent.putExtra("cropperImage", cropperImage);
startActivity(intent);
bitmap.recycle();
finish();
super.overridePendingTransition(R.anim.fade_in,
R.anim.fade_out);
// doAnimation(cropperImage);
} private void doAnimation(CropperImage cropperImage) {
ImageView imageView = new ImageView(this);
View view = LayoutInflater.from(this).inflate(
R.layout.image_view_layout, null);
((RelativeLayout) view.findViewById(R.id.root_layout)).addView(imageView);
RelativeLayout relativeLayout = ((RelativeLayout) findViewById(R.id.root_layout));
// relativeLayout.addView(imageView);
imageView.setX(cropperImage.getX());
imageView.setY(cropperImage.getY());
ViewGroup.LayoutParams lp = imageView.getLayoutParams();
lp.width = (int)cropperImage.getWidth();
lp.height = (int) cropperImage.getHeight();
imageView.setLayoutParams(lp);
imageView.setImageBitmap(cropperImage.getBitmap());
try {
getWindow().addContentView(view, lp);
} catch (Exception e) {
e.printStackTrace();
}
/*AnimatorSet animSet = new AnimatorSet();
ObjectAnimator translationX = ObjectAnimator.ofFloat(this, "translationX", cropperImage.getX(), 0);
ObjectAnimator translationY = ObjectAnimator.ofFloat(this, "translationY", cropperImage.getY(), 0);*/ TranslateAnimation translateAnimation = new TranslateAnimation(
0, -cropperImage.getX(), 0, -(Math.abs(cropperImage.getHeight() - cropperImage.getY())));// 当前位置移动到指定位置
RotateAnimation rotateAnimation = new RotateAnimation(0, -90,
Animation.ABSOLUTE, cropperImage.getX() ,Animation.ABSOLUTE, cropperImage.getY());
AnimationSet animationSet = new AnimationSet(true);
animationSet.addAnimation(translateAnimation);
animationSet.addAnimation(rotateAnimation);
animationSet.setFillAfter(true);
animationSet.setDuration(2000L);
imageView.startAnimation(animationSet);
// finish();
} /**
* 拍照成功后回调
* 存储图片并显示截图界面
* @param data
*/
@Override
public void onCameraStopped(byte[] data) {
Log.i("TAG", "==onCameraStopped==");
// 创建图像
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
// 系统时间
long dateTaken = System.currentTimeMillis();
// 图像名称
String filename = DateFormat.format("yyyy-MM-dd kk.mm.ss", dateTaken)
.toString() + ".jpg";
// 存储图像(PATH目录)
Uri source = insertImage(getContentResolver(), filename, dateTaken, PATH,
filename, bitmap, data);
//准备截图
try {
mCropImageView.setImageBitmap(MediaStore.Images.Media.getBitmap(this.getContentResolver(), source));
// mCropImageView.rotateImage(90);
} catch (IOException e) {
Log.e(TAG, e.getMessage());
}
showCropperLayout();
} /**
* 存储图像并将信息添加入媒体数据库
*/
private Uri insertImage(ContentResolver cr, String name, long dateTaken,
String directory, String filename, Bitmap source, byte[] jpegData) {
OutputStream outputStream = null;
String filePath = directory + filename;
try {
File dir = new File(directory);
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File(directory, filename);
if (file.createNewFile()) {
outputStream = new FileOutputStream(file);
if (source != null) {
source.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
} else {
outputStream.write(jpegData);
}
}
} catch (FileNotFoundException e) {
Log.e(TAG, e.getMessage());
return null;
} catch (IOException e) {
Log.e(TAG, e.getMessage());
return null;
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (Throwable t) {
}
}
}
ContentValues values = new ContentValues(7);
values.put(MediaStore.Images.Media.TITLE, name);
values.put(MediaStore.Images.Media.DISPLAY_NAME, filename);
values.put(MediaStore.Images.Media.DATE_TAKEN, dateTaken);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
values.put(MediaStore.Images.Media.DATA, filePath);
return cr.insert(IMAGE_URI, values);
} private void showTakePhotoLayout() {
mTakePhotoLayout.setVisibility(View.VISIBLE);
mCropperLayout.setVisibility(View.GONE);
} private void showCropperLayout() {
mTakePhotoLayout.setVisibility(View.GONE);
mCropperLayout.setVisibility(View.VISIBLE);
mCameraPreview.start(); //继续启动摄像头
} private float mLastX = 0;
private float mLastY = 0;
private float mLastZ = 0;
private boolean mInitialized = false;
private SensorManager mSensorManager;
private Sensor mAccel;
@Override
public void onSensorChanged(SensorEvent event) { float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
if (!mInitialized){
mLastX = x;
mLastY = y;
mLastZ = z;
mInitialized = true;
}
float deltaX = Math.abs(mLastX - x);
float deltaY = Math.abs(mLastY - y);
float deltaZ = Math.abs(mLastZ - z); if(deltaX > 0.8 || deltaY > 0.8 || deltaZ > 0.8){
mCameraPreview.setFocus();
}
mLastX = x;
mLastY = y;
mLastZ = z;
} @Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}

  actiity中注册了SensorEventListener,也就是使用传感器监听用户手机的移动,如果有一定距离的移动,则自动聚焦,这样体验好一点。

  我对比了一下小猿搜题和学霸君两款app的拍照功能,个人感觉小猿搜题的体验要好一些,因为从主界面进入拍照界面,连个界面没有一个旋转的过渡,而学霸君就有一个过渡,有一丝丝的影响体验。也就是说学霸君的拍照界面是横屏的,在activity的onCreate方法里面调用了setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)来设置全屏,而切换界面的时候又从竖屏切换为横屏,就会有个过渡的效果,影响了体验。

  个人猜测小猿搜题是将拍照界面的activity设置为竖屏,而将摄像头直接旋转90度,这样就强制用户横屏拍摄,当然,拍完之后还要将图片旋转回来。所以我参考小猿搜题来实现的,毕竟体验为王嘛。

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

  如上图(其实是竖屏),红色圈起来的其实是放到底部,然后将屏幕中间的文字旋转90度(带有动画,起了提示用户横屏拍照的作用),就给人的感觉是横屏的。了。

  还有一点就是小猿搜题拍完照到截图过渡的很自然,感觉很流畅,估计是拍照和截图放在同一个activity中的,如果是两个activty,涉及到界面切换,肯定不会那么自然。所以我也将拍照和截图放在一个界面,拍照完就将自定义相机隐藏,将截图界面显示出来,这样切换就很流畅了。

  项目中截图的功能我是从github上面找的一个开源库cropper:https://github.com/edmodo/cropper

    因为ocr图片识别的代码是公司的,所以识别的功能没有添加到demo里面去。

  Demo源码下载:https://github.com/liuling07/CustomCameraDemo