前段时间写了关于一篇关于调用系统相机的博客,如果需要调用系统相机和截图可以看一看这篇博客:Android学习之调用系统相机拍照、截图并保存最近发现不同手机,调用系统相机效果不太好,,所以学习Android 的相机原理,自定义了一个Android相机。看了这篇博客,相信大家都会写一个自己的相机。

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

项目地址:https://github.com/Terrybthvi/MyCamera

这个项目仿照小猿搜题app,首先我们将相机布局最低层是显示摄像头获取到的图像,上层是网格线,聚焦图标等。

效果图如下所示:

 

1、绘制网格参考线

绘制网格参考线就是绘制拍照时屏幕中的竖线和直线,代码如下所示:

2、自定义相机代码

这里我们要创建一个SurfaceView,将摄像头获取到的图像放到SurfsaceView中显示。主要方法如下:

1、继承SurfaceView,定义变量,初始化类。

2、实现三个方法对sufarceView监听。

  • surfaceCreated();
  • surfaceChanged();
  • surfaceDestroyed();

3、获取相机实例

4、设置相机监听

5、增加自动聚焦 和点击屏幕聚焦

6、拍照以及设置图片格式

3、为相机添加聚焦功能

4、Activity调用相机

actiity中注册了SensorEventListener,也就是使用传感器监听用户手机的移动,如果有一定距离的移动,则自动聚焦,这样体验好一点。
<span style="font-family:SimHei;font-size:18px;">package com.example.terry.mycamera;import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.text.format.DateFormat;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.LinearInterpolator;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;import com.example.terry.mycamera.Crop.CropImageView;
import com.example.terry.mycamera.Crop.CropperImage;
import com.example.terry.mycamera.camare.CameraPreview;
import com.example.terry.mycamera.camare.FocusView;import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;/*** @Class: TakePhotoActivity* @Description: 拍照界面* @author: leiqi(http://blog.csdn.net/u013132758)* @Date: 2016/3/15*/
public class TakePhotoActivity 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;@Overrideprotected 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_photo);// Initialize components of the appmCropImageView = (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;@TargetApi(Build.VERSION_CODES.HONEYCOMB)@Overrideprotected 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);}@Overrideprotected void onPause() {super.onPause();mSensorManager.unregisterListener(this);}@Overridepublic void onConfigurationChanged(Configuration newConfig) {Log.e(TAG, "onConfigurationChanged");super.onConfigurationChanged(newConfig);}public void takePhoto(View view) {if(mCameraPreview != null) {mCameraPreview.takePicture();}}public void openlight(View view){if (mCameraPreview != null){mCameraPreview.openLight();view.setVisibility(View.GONE);View v = findViewById(R.id.nolight);v.setVisibility(View.VISIBLE);}}
public void offlight(View v)
{if (mCameraPreview != null){mCameraPreview.offLight();v.setVisibility(View.GONE);View view = findViewById(R.id.light);view.setVisibility(View.VISIBLE);}
}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, PreviewActivity.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);}/*** 拍照成功后回调* 存储图片并显示截图界面* @param data*/@Overridepublic 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;@Overridepublic 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;}@Overridepublic void onAccuracyChanged(Sensor sensor, int accuracy) {}
}</span>

5、添加计算焦点及测光区域、拍照后图片旋转以及检测摄像头是否可用

<span style="font-family:SimHei;font-size:18px;">package com.example.terry.mycamera;import android.content.Context;
/*** Created by leiqi on 15/9/29.*/
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Environment;
import android.util.DisplayMetrics;import java.io.File;/*** @Class:* @Description:* @author: leiqi(http://blog.csdn.net/u013132758)* @Date: 2016/3/15*/
public class Utils {public static DisplayMetrics getScreenWH(Context context) {DisplayMetrics dMetrics = new DisplayMetrics();dMetrics = context.getResources().getDisplayMetrics();return dMetrics;}/*** 计算焦点及测光区域** @param focusWidth* @param focusHeight* @param areaMultiple* @param x* @param y* @param previewleft* @param previewRight* @param previewTop* @param previewBottom* @return Rect(left,top,right,bottom) : left、top、right、bottom是以显示区域中心为原点的坐标*/public static Rect calculateTapArea(int focusWidth, int focusHeight,float areaMultiple, float x, float y, int previewleft,int previewRight, int previewTop, int previewBottom) {int areaWidth = (int) (focusWidth * areaMultiple);int areaHeight = (int) (focusHeight * areaMultiple);int centerX = (previewleft + previewRight) / 2;int centerY = (previewTop + previewBottom) / 2;double unitx = ((double) previewRight - (double) previewleft) / 2000;double unity = ((double) previewBottom - (double) previewTop) / 2000;int left = clamp((int) (((x - areaWidth / 2) - centerX) / unitx),-1000, 1000);int top = clamp((int) (((y - areaHeight / 2) - centerY) / unity),-1000, 1000);int right = clamp((int) (left + areaWidth / unitx), -1000, 1000);int bottom = clamp((int) (top + areaHeight / unity), -1000, 1000);return new Rect(left, top, right, bottom);}public static int clamp(int x, int min, int max) {if (x > max)return max;if (x < min)return min;return x;}/*** 检测摄像头设备是否可用* Check if this device has a camera* @param context* @return*/public static boolean checkCameraHardware(Context context) {if (context != null && context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {// this device has a camerareturn true;} else {// no camera on this devicereturn false;}}/*** @param context* @return app_cache_path/dirName*/public static String getDBDir(Context context) {String path = null;if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {path = Environment.getExternalStorageDirectory().getAbsolutePath() +File.separator + "bbk" + File.separator + "cloudteacher" + File.separator + "db";File externalCacheDir = context.getExternalCacheDir();if (externalCacheDir != null) {path = externalCacheDir.getPath();}}if (path == null) {File cacheDir = context.getCacheDir();if (cacheDir != null && cacheDir.exists()) {path = cacheDir.getPath();}}return path;}/*** bitmap旋转* @param b* @param degrees* @return*/public static Bitmap rotate(Bitmap b, int degrees) {if (degrees != 0 && b != null) {Matrix m = new Matrix();m.setRotate(degrees, (float) b.getWidth() / 2, (float) b.getHeight() / 2);try {Bitmap b2 = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), m, true);if (b != b2) {b.recycle();  //Android开发再次提示Bitmap操作完应该显示的释放b = b2;}} catch (OutOfMemoryError ex) {// Android123建议大家如何出现了内存不足异常,最好return 原始的bitmap对象。.}}return b;}public static final int getHeightInPx(Context context) {final int height = context.getResources().getDisplayMetrics().heightPixels;return height;}public static final int getWidthInPx(Context context) {final int width = context.getResources().getDisplayMetrics().widthPixels;return width;}}</span>

源码下载地址:https://github.com/Terrybthvi/MyCamera

【Android 开发】SufaceView自定义相机拍照相关推荐

  1. Android开发之调用相机拍照与本地图库选择图片

    引用链接 Android开发之调用相机拍照与本地图库选择图片 Android调用相机实现拍照功能 部分截图 引言 小项目有一个访问相册的需求,在网上查找得到两位大神博客指点,但博客发布时间过旧,难免因 ...

  2. Android之应用自定义相机拍照并且对拍照文字(英文)进行识别

    本程序的内容如题所示,可以通过实际程况对识别出来的英文进行翻译,参照我另外的英文词典翻译例子.另外,由于导入了外部的包,使得项目比较大,这里我只是上传了代码,而对于项目的实体配置则没有.如果有需要整个 ...

  3. Android开发 调用系统相机相册图片功能,解决小米手机拍照或者图片横竖相反问题,及小米手机相册图片路径问题

    Android开发 调用系统相机相册图片功能,解决小米手机拍照或者图片横竖相反问题,及小米手机相册图片路径问题 1.调用相机,兼容7.0 AndroidManifest配置 <providera ...

  4. android自定义相机拍照

     Android中开发相机的两种方式: Android系统提供了两种使用手机相机资源实现拍摄功能的方法,一种是直接通过Intent调用系统相机组件,这种方法快速方便,适用于直接获得照片的场景,如上传相 ...

  5. android 自定义拍照模糊,Android自定义相机拍照模糊处理

    问题分析:随着用户对于拍照清晰度的需求,android手机对于摄像头也是一升再升,这就导致了作为android开发工程师对于兼容性维护的继续跟进以及问题处理. 针对于自定义相机拍照模糊的问题,经过几天 ...

  6. uni-app 自定义相机拍照录像,可设置分辨率、支持横竖屏(ios、android)

    插件市场:uni-app 自定义相机拍照录像,可设置分辨率.支持横竖屏(ios.android)

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

    原文:Android自定义相机拍照.图片裁剪的实现 最近项目里面又要加一个拍照搜题的功能,也就是用户对着不会做的题目拍一张照片,将照片的文字使用ocr识别出来,再调用题库搜索接口搜索出来展示给用户,类 ...

  8. web自定义相机拍照

    背景 我们平常可能有需求是要自定义拍照,比如在相机界面加上一些自定义的辅助线.遮罩等等,但是前端不像原生开发可以自定义相机,我们只能另辟蹊径了. 常用拍照方案 首先总结一下常用的拍照方案,这里只说 w ...

  9. Android通知怎么实现?Android开发如何操作相机和相册?

    Android通知怎么实现?Android开发如何操作相机和相册? 前言 八.Android通知怎么实现?Android开发如何操作相机和相册? 8.1 通知介绍 8.2 通知的基本用法 8.3 An ...

最新文章

  1. JSONObject没有fromObject方法(Json lib 库的使用)
  2. Python,OpenCV骨架化图像并显示(skeletonize)
  3. 初学Python——文件操作第三篇
  4. JS数组方法(forEach()、every()、reduce())
  5. free技术详解 lock_lock free的理解
  6. SAP MM/FI 自动过账实现 OBYC 接口执行
  7. monkey 真机测试步骤
  8. 用js如何获取file是否存在
  9. WPF Popup 相关内容
  10. java tts引擎_让Java说话-用Java实现语音引擎
  11. 分布式事务框架-TX-LCN
  12. 使用ByteArrayOutputStream解决IO乱码问题的踩坑记录
  13. 【干货】如何打造高质量的NLP数据集
  14. php beanstalkd使用,PHP使用Beanstalkd实例详解
  15. Win10安装MySql步骤
  16. C++变量初始化问题
  17. 旧iPhone手机钱包中公交卡 银行卡 转移到新手机iPhone11上
  18. matlab中功率因数怎样测量,如何测量功率因数?功率因数测量方法
  19. 百度网盘下载速度太慢,百度网盘下载慢怎么解决
  20. Python 实现Mac 屏幕截图

热门文章

  1. 万物联网的原则:从这瓶可乐“真智能”到这瓶可乐“真贴心”
  2. Skyline 7 版本TerraExplorer Pro二次开发快速入门
  3. https的FTP文件下载
  4. 怎么提速Mac苹果笔记本电脑运行速度?
  5. Android 12 “致命”崩溃解决之路
  6. Asp.Net微信登录-手机网站APP应用
  7. Arcmap高级标注(通过表达式设置颜色/字体/换行等)
  8. 数据结构与算法(陈越版)第五讲 (树下)树的应用——集合及其运算
  9. Kali beef-xss实现Xss详细教程。
  10. Java中的escape,unescape方法