这篇文章总结使用Camera API实现自定义相机
之前文章 Android 拍照功能详解介绍了调用系统相机应用完成拍照功能。
但很多时候,应用需要自定义实现相机拍照功能,Android5.0以后推出了Camera2.0来替代之前的Camera API,但是很多时候我们还是需要使用Camera API来实现相机的自定义。
关键知识点
- SurfaceView的使用,相机预览界面是通过SurfaceView实现的;
- Camera API的使用;
- 拍照生成的文件的保存与读取。
权限申请
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-feature android:name="android.hardware.camera"/>
相机权限,文件存储权限。
相机界面
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SurfaceView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/surfaceview"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="拍照"
android:layout_gravity="center|bottom"
android:onClick="takePhoto"/>
</FrameLayout>
这里为什么要使用SurfaceView,因为普通的View无法实现高效的拍照页面视图渲染。
Camera API使用
Camera初始化
/**
* 初始化相机对象
* @return
*/
private Camera initCamera(){
Camera camera;
try {
camera = Camera.open();
} catch (Exception e) {
camera = null;
e.printStackTrace();
}
return camera;
}
通过Camera.open()方法得到Camera对象。
SurfaceHolder 初始化
@Override
protected void preInitData() {
mFile = new File(getIntent().getStringExtra("file"));
mHolder = mPreview.getHolder();
mHolder.addCallback(this);
}
SurfaceHolder是SurfaceView的关键方法,通过SurfaceView获取到SurfaceHolder,然后实现其回调方法。
SurfaceHolder的回调方法实现
@Override
public void surfaceCreated(SurfaceHolder holder) {
startPreview(mCamera,holder);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
//先关闭,再开启
mCamera.stopPreview();
startPreview(mCamera,holder);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
releaseCamera();
}
其中startPreview(mCamera,holder)是我实现的预览相机内容的方法,releaseCamera()方法是释放相机资源的方法。
预览相机内容
/**
* 开始预览相机内容
*/
private void startPreview(Camera camera ,SurfaceHolder holder){
try {
camera.setPreviewDisplay(holder);
//将Camera预览角度进行调整
camera.setDisplayOrientation(90);
camera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
通过Camera跟SurfaceHolder产生关联,就可以将Camera的预览内容展现在SurfaceView中。
释放相机资源
/**
* 释放相机资源
*/
private void releaseCamera(){
if(mCamera != null){
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
获得拍照生成的照片
/**
* 拍照按钮点击事件
* @param view
*/
public void takePhoto(View view){
setParameters();
mCamera.autoFocus(new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
if(success){
//前两个参数可以为空,第三个方法是照片的回调
mCamera.takePicture(null,null,mPictureCallback);
}
}
});
}
我们这里拿到回调的字节流,然后生成文件保存,最后将文件数据放入Intent中,关闭当前页面。
其中mPictureCallback方法是相机拍照结果的回调方法:
private Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
try {
FileOutputStream fos = new FileOutputStream(mFile);
fos.write(data);
fos.close();
Intent intent = new Intent();
intent.putExtra("file",mFile.getAbsolutePath());
setResult(0,intent);
finish();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
};
setParameters()方法是我们对Camera一些参数的设置:
/**
* 开始预览相机内容
*/
private void startPreview(Camera camera ,SurfaceHolder holder){
try {
camera.setPreviewDisplay(holder);
//将Camera预览角度进行调整
camera.setDisplayOrientation(90);
camera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
关键的方法基本上都介绍完了,下面我将CameraActivity界面的所有代码都放出来,方便大家的动手操作。
public class Camera1Activity extends BaseActivity implements SurfaceHolder.Callback{
@BindView(R.id.surfaceview)SurfaceView mPreview;
private File mFile;
private Camera mCamera;
private SurfaceHolder mHolder;
private Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
try {
FileOutputStream fos = new FileOutputStream(mFile);
fos.write(data);
fos.close();
Intent intent = new Intent();
intent.putExtra("file",mFile.getAbsolutePath());
setResult(0,intent);
finish();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
};
@Override
protected int getContentViewResId() {
return R.layout.camera1;
}
@Override
protected void onResume() {
super.onResume();
if(mCamera == null){
mCamera = initCamera();
if(mHolder != null){
startPreview(mCamera,mHolder);
}
}
}
@Override
protected void onPause() {
super.onPause();
releaseCamera();
}
@Override
protected void preInitData() {
mFile = new File(getIntent().getStringExtra("file"));
mHolder = mPreview.getHolder();
mHolder.addCallback(this);
}
@OnClick(R.id.surfaceview)
public void onSurfaceviewClick(){
mCamera.autoFocus(null);
}
/**
* 拍照按钮点击事件
* @param view
*/
public void takePhoto(View view){
setParameters();
mCamera.autoFocus(new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
if(success){
//前两个参数可以为空,第三个方法是照片的回调
mCamera.takePicture(null,null,mPictureCallback);
}
}
});
}
/**
* 初始化相机对象
* @return
*/
private Camera initCamera(){
Camera camera;
try {
camera = Camera.open();
} catch (Exception e) {
camera = null;
e.printStackTrace();
}
return camera;
}
/**
* 开始预览相机内容
*/
private void startPreview(Camera camera ,SurfaceHolder holder){
try {
camera.setPreviewDisplay(holder);
//将Camera预览角度进行调整
camera.setDisplayOrientation(90);
camera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 释放相机资源
*/
private void releaseCamera(){
if(mCamera != null){
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
/**
* 设置相机参数
*/
private void setParameters(){
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPictureFormat(ImageFormat.JPEG);
parameters.setPreviewSize(800,400);
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
startPreview(mCamera,holder);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
//先关闭,再开启
mCamera.stopPreview();
startPreview(mCamera,holder);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
releaseCamera();
}
}
拍照功能实现了,接下来我们处理拍照后相片的显示操作。仔细阅读文章的同学会发现,我们在跳转至CameraActivity中的时候传入了照片文件的路径。
public void photo(View view){
Intent intent = new Intent(this,Camera1Activity.class);
intent.putExtra("file",createImageFile("mFile").toString());
startActivityForResult(intent,TAKE_PHOTO_CUSTOM);
}
private File createImageFile(String fileName){
File dir = new File(getCacheDir(),"images");
if(!dir.exists())
dir.mkdirs();
return new File(dir,fileName);
}
同时,我们将拍照后的相片路径放入了Intent中。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == TAKE_PHOTO_CUSTOM){
if(data != null){
// Bitmap resultBitmap = BitmapFactory.decodeFile(filePath);
// mImageView.setImageBitmap(resultBitmap);
try {
String filePath = data.getStringExtra("file");
FileInputStream fis = new FileInputStream(filePath);
Bitmap bitmap = BitmapFactory.decodeStream(fis);
Matrix matrix = new Matrix();
matrix.setRotate(90);
bitmap = Bitmap.createBitmap(bitmap,0,0,
bitmap.getWidth(),bitmap.getHeight(),matrix,true);
mImageView.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
}
因为我直接将相片拿出来显示的话,照片显示效果是旋转了90度,至于为什么如此,仔细的同学可能会发现在相机Camera API使用的时候我们对Camera进行了90度的旋转,因为Camera默认相片是横屏显示的。因此我在OnActivityResult中对照片显示的imageview进行了90度的旋转,这样照片就可以正确显示在ImageView中。
Camera的使用简答介绍到这里,实现了简单拍照功能。至于项目中对自定义拍照功能的应用,相信在此基础上进行一定的完善就可以满足一般的功能需求。
感觉有帮助的同学欢迎留言交流,点赞关注!谢谢。