Android自定义Camera实现拍照功能
作者:忙碌的咖啡 发布时间:2021-09-22 09:18:58
标签:Android,camera,拍照
本文记录了用自定义Camera实现的简单拍照功能。
Camera类在5.0以后不推荐使用了,取而代之的是android.hardware.camera2包下的类,本文使用Camera。
我们首先自定义一个View去继承SurfaceView:
public class CameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Camera.AutoFocusCallback {
private SurfaceHolder mHolder;
private Camera mCamera;
private static final int ORIENTATION = 90;
private int mScreenWidth;
private int mScreenHeight;
private boolean isOpen;
public CameraSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
getScreenMatrix(context);
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
private void getScreenMatrix(Context context) {
WindowManager WM = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
WM.getDefaultDisplay().getMetrics(outMetrics);
mScreenWidth = outMetrics.widthPixels;
mScreenHeight = outMetrics.heightPixels;
}
public void takePicture(Camera.ShutterCallback mShutterCallback, Camera.PictureCallback rawPictureCallback, Camera.PictureCallback jpegPictureCallback) {
if (mCamera != null)
mCamera.takePicture(mShutterCallback, rawPictureCallback, jpegPictureCallback);
}
public void startPreview() {
mCamera.startPreview();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (!checkCameraHardware(getContext()))
return;
if (mCamera == null) {
isOpen = safeCameraOpen(Camera.CameraInfo.CAMERA_FACING_BACK);
}
if (!isOpen) {
return;
}
mCamera.setDisplayOrientation(ORIENTATION);
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (mCamera != null) {
setCameraParams(mScreenWidth, mScreenHeight);
mCamera.startPreview();
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
releaseCameraAndPreview();
}
private boolean safeCameraOpen(int id) {
boolean qOpened = false;
try {
releaseCameraAndPreview();
mCamera = Camera.open(id);
qOpened = (mCamera != null);
} catch (Exception e) {
e.printStackTrace();
}
return qOpened;
}
private void releaseCameraAndPreview() {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
return true;
} else {
return false;
}
}
@Override
public void onAutoFocus(boolean success, Camera camera) {
}
private void setCameraParams(int width, int height) {
Camera.Parameters parameters = mCamera.getParameters();
// 获取摄像头支持的PictureSize列表
List<Camera.Size> pictureSizeList = parameters.getSupportedPictureSizes();
/**从列表中选取合适的分辨率*/
Camera.Size picSize = getProperSize(pictureSizeList, ((float) height / width));
if (null == picSize) {
picSize = parameters.getPictureSize();
}
// 根据选出的PictureSize重新设置SurfaceView大小
float w = picSize.width;
float h = picSize.height;
parameters.setPictureSize(picSize.width, picSize.height);
this.setLayoutParams(new RelativeLayout.LayoutParams((int) (height * (h / w)), height));
// 获取摄像头支持的PreviewSize列表
List<Camera.Size> previewSizeList = parameters.getSupportedPreviewSizes();
Camera.Size preSize = getProperSize(previewSizeList, ((float) height) / width);
if (null != preSize) {
parameters.setPreviewSize(preSize.width, preSize.height);
}
parameters.setJpegQuality(100); // 设置照片质量
if (parameters.getSupportedFocusModes().contains(android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
parameters.setFocusMode(android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);// 连续对焦模式
}
mCamera.setDisplayOrientation(90);// 设置PreviewDisplay的方向,效果就是将捕获的画面旋转多少度显示
mCamera.setParameters(parameters);
}
/**
* 选取合适的分辨率
*/
private Camera.Size getProperSize(List<Camera.Size> pictureSizeList, float screenRatio) {
Camera.Size result = null;
for (Camera.Size size : pictureSizeList) {
float currentRatio = ((float) size.width) / size.height;
if (currentRatio - screenRatio == 0) {
result = size;
break;
}
}
if (null == result) {
for (Camera.Size size : pictureSizeList) {
float curRatio = ((float) size.width) / size.height;
if (curRatio == 4f / 3) {// 默认w:h = 4:3
result = size;
break;
}
}
}
return result;
}
}
代码没什么难度,在View创建的时候完成Camera的初始化,然后对Camera进行参数的设置(图片尺寸,质量之类的),最后别忘了在View销毁的时候对资源进行释放。
控件定义完了之后我们就要去使用它,在布局文件中添加就OK:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<com.padoon.cameratest.CameraSurfaceView
android:id="@+id/sv_camera"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="4"/>
<ImageView
android:id="@+id/img_take_photo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_gravity="bottom"
android:layout_marginBottom="10dp"
android:layout_marginRight="10dp"
android:src="@mipmap/icon_camera"/>
</RelativeLayout>
然后在Activity中去完成拍照功能:
public class CameraActivity extends AppCompatActivity {
private boolean isClick = true;
private static final String PATH_IMAGES = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "easy_check";
private CameraSurfaceView mCameraSurfaceView;
//拍照快门的回调
private Camera.ShutterCallback mShutterCallback = new Camera.ShutterCallback() {
@Override
public void onShutter() {
}
};
//拍照完成之后返回原始数据的回调
private Camera.PictureCallback rawPictureCallback = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
}
};
//拍照完成之后返回压缩数据的回调
private Camera.PictureCallback jpegPictureCallback = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
mCameraSurfaceView.startPreview();
saveFile(data);
Toast.makeText(CameraActivity.this, "拍照成功", Toast.LENGTH_SHORT).show();
isClick = true;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView img_take_photo = (ImageView) findViewById(R.id.img_take_photo);
mCameraSurfaceView = (CameraSurfaceView) findViewById(R.id.sv_camera);
img_take_photo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
takePhoto();
}
});
}
public void takePhoto() {
if (isClick) {
isClick = false;
mCameraSurfaceView.takePicture(mShutterCallback, rawPictureCallback, jpegPictureCallback);
}
}
//保存图片到硬盘
public void saveFile(byte[] data) {
String fileName = UUID.randomUUID().toString() + ".jpg";
FileOutputStream outputStream = null;
try {
File file = new File(PATH_IMAGES);
if (!file.exists()) {
file.mkdirs();
}
outputStream = new FileOutputStream(PATH_IMAGES + File.separator + fileName);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
bufferedOutputStream.write(data, 0, data.length);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
最后记得添加拍照跟磁盘操作权限:
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
到这一个非常简单的拍照Demo就完成了,只能当做Demo使用,离开发正式使用还有一段的距离,再次特地记录一下。
下载:源码
来源:https://blog.csdn.net/ww55555577/article/details/62041140


猜你喜欢
- 本文实例讲述了Android编程基于自定义View实现绚丽的圆形进度条功能。分享给大家供大家参考,具体如下:本文包含两个组件,首先上效果图:
- 本文探讨使用C# StringBuilder 的最佳实践,用于减少内存分配,提高字符串操作的性能。在 .NET 中,String 对象是不可
- 前言C#中提供了比较全面的字符串处理方法,很多函数都进行了封装为我们的编程工作提供了很大的便利。System.String是最常用的字符串操
- 自定义Repository接口要定义一个repository接口,你首先需要自定义一个实体类专用的Repository接口。该接口必须扩展
- Java 反射机制实例详解一、JAVA是动态语言吗?一般而言,说到动态言,都是指在程序运行时允许改变程序结构或者变量类型,从这个观点看,Ja
- SpringCloudStream配置以下配置摘自《SpringCloud微服务实战》,配置主要包括两大部分:Stream配置(基础配置、通
- 上一篇JavaMail入门第四篇 接收邮件中,控制台打印出的内容,我们无法阅读,其实,让我们自己来解析一封复杂的邮件是很不容易的,邮件里面格
- 过早提升(Premature Promotion)提升速率(promotion rate), 用于衡量单位时间内从年轻代提升到老年代的数据量
- JTable的排序是一个让人头疼的问题,Sun没有为排序这个最常用的功能提供类。但是近日翻看Sun官方java的tutorial,却发现其在
- b/s系统中对http请求数据的校验多数在客户端进行,这也是出于简单及用户体验性上考虑,但是在一些安全性要求高的系统中服务端校验是不可缺少的
- 可以用抽象工厂模式来造车。车的品牌有很多,而且车的属性也不少,比如车的类型、排量、门的数量,等等。可以提炼出有关车的一个抽象类:public
- java.util.concurrent.ScheduledThreadPoolExecutor 是JDK1 .6之后自带的包,功能强大,能
- Druid是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0、DBCP、PROXOOL等DB池的优点,同时加入了日志监控,可以很好的
- C#文件夹加锁小工具用C#语言实现一个文件夹锁的程序,网上类似的“xxx文件夹xxx”软件很多,但是基本上都是C/C++语言实现的,且都没有
- using System;using System.Collections.Generic;using System.IO;using Sy
- 最近研究OpenCV想用java进行开发,因此研究了一下怎么在Eclipse中配置基于java的Opencv.第一步:先到OpenCV官网下
- 什么是HystrixHystrix是Netflix针对微服务分布式系统的熔断保护中间件,当我们的客户端连接远程的微服务时,有两种情况需要考虑
- 本篇文章介绍自定义View配合属性动画来实现如下的效果实现思路挺简单:画一个半透明的圆实现两种动画效果,点击时扩散和不点击时扩散回收使用线程
- 通过javamail 实现发送邮件,供大家参考,具体内容如 * 意:服务器有些端口是没有开放的 需要去开放端口。 有些邮箱是需要开启对应授权服
- 目录1.堆空间的基本结构:2.空间分配担保机制3.如何判断一个对象已经无效4 不可达的对象并非“非死不可”5 如何判断一个常量是废弃常量?6