需求是万恶之源,今天我们来说说相机
调用系统相机
如果只是调起相机拍照,很简单就两步:
1.权限
2.创建意图跳转
Intent intent =new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivity(intent);
但是我们一般都会需要获取图片数据,Android也给我们提供了调用的方式
1.替换跳转方式
startActivityForResult(intent, REQUEST_CODE_CAPTURE_RAW);
2.监听返回
/**
* 图片拍照回调
* @param requestCode
* @param resultCode
* @param data
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//通过data获取图片信息
}
如果你使用过上面的方式你会发现得到图片质量并不高,这是由于现在相机的相机像素都很高,拍出来的照片所占用的内存(这里说的内存是bitmap占用的,不是图片在SD卡占用的,注意区分)都很高,容易oom,所以我们这里我们得到不是原图是缩略图
那如果需要原图怎么办了?这里也是有方式获取的
直接上代码
//拍照(返回原始图)
public void gotoCaptureRaw() {
imageFile = createImageFile(false);
if (imageFile != null) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//获取设备sdk版本
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri imgUri = FileProvider.getUriForFile(this, AUTHORITY, imageFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imgUri);
} else {//7.0 以下
//设置拍照图片保存路径
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(imageFile));
}
//设置图片保存格式
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
ComponentName componentName = intent.resolveActivity(getPackageManager());
//判断组件是否为null,避免崩溃
if (componentName != null) {
startActivityForResult(intent, REQUEST_CODE_CAPTURE_RAW);
}
}
}
想要获取原图,就得指定拍摄照片后图片的存储路径
关键代码
intent.putExtra(MediaStore.EXTRA_OUTPUT, imgUri);
注意7.0以上需要使用FileProvider,怎么使用这里就不多讲了,自行查阅资料
然后在onActivityResult回调中,通过imageFile获取图片,不过有一点需要注意,由于获取是原图,可能会导致oom,建议对图片压缩处理
简单拍个照,上面这些应该够用了.但有时系统相机并不满足实际需求,这时就需要自定义相机了
自定义相机
除非万不得已,千万不要自定义
除非万不得已,千万不要自定义
除非万不得已,千万不要自定义
为什么这样说,因为坑多呀
想要自定义相机主要用到两个类
Camera
SurfaceView
他们的关系你可以看成:
注意Android5.0 用Camera2 取代Camera1 ,这里Camera1已经可以满足大部分需求,如果要实现更复杂的功能,建议使用Camera2
下面我们就来自己开发一个自定义相机
- 添加相关权限
- 在相关xml中添加SurfaceView,用于显示预览界面
- 获取SurfaceHolder添加相关监听
- 在surfaceCreated回调处打开相机,设置相关参数,开启预览
- 拍照,获得图片
- 图片处理,保存
- 退出,释放资源
权限
在相关xml中添加SurfaceView,用于显示预览界面
获取SurfaceHolder添加相关监听
mSurfaceView = findViewById(R.id.sv);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
surfaceHolderCallBack = new SurfaceHolderCallBack();
mSurfaceHolder.addCallback(surfaceHolderCallBack);
class SurfaceHolderCallBack implements SurfaceHolder.Callback {
@Override
public void surfaceCreated(final SurfaceHolder surfaceHolder) {
new Thread(new Runnable() {//启动相机 特别耗时 需要开启线程处理
@Override
public void run() {
//打开相机操作
}
}).start();
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
//释放相机资源
}
}
在surfaceCreated回调处打开相机,设置相关参数,开启预览
//打开相机
Camera camera = Camera.open(//相机Id);
//设置预览界面
camera.setPreviewDisplay(//surfaceHolder);
//设置预览角度
//camera.setDisplayOrientation(//角度);
//开启预览
camera.startPreview();
完成上述几步,就可以看见预览界面了,不过会发现你的预览结果和你想象的应该不太一样,第一个坑出现了,这里你只需要知道camera.setDisplayOrientation(//角度)是处理这个问题的,后面会对出现的坑做统一的解释
预览完成了,接下来解释拍照了
拍照,获得图片
我采用的自动变焦的方式拍照,大概流程是:点击拍照后,开始自动变焦,变焦完成,相机开始拍照,拍照完成获取图片
/**
* 照相
*/
public void capture(Camera camera) {
if (camera != null) {
Camera.Parameters parameters = camera.getParameters();
//TODO 判断是否支持自动变焦
parameters.setFlashMode(Camera.Parameters.FOCUS_MODE_AUTO);
//需要设置图片的尺寸
// List supportedPictureSizes = parameters.getSupportedPictureSizes();
// for (Camera.Size supportedPictureSize : supportedPictureSizes) {
// if (supportedPictureSize.width == 1280) {//取景方向默认是横向的
// parameters.setPictureSize(supportedPictureSize.width, supportedPictureSize.height);
// }
// }
//TODO 一定注意保存
camera.setParameters(parameters);
camera.autoFocus(new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
if (success) {
camera.takePicture(null, null, pictureCallback);
}
}
});
}
}
/**
* 相机生成的图片数据
*/
Camera.PictureCallback pictureCallback = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] bytes, Camera camera) {
//TODO 可能需要一个线程去执行
saveImage(bytes, "temp1.png");
}
};
恩,拍照获取图片也完成了,细心的朋友会返现上面有一段注释掉的代码,这是为了第二坑,至于坑是什么后面说.
获取到图片我们就可以保存了
图片处理,保存
//保存图片
File rootFile = new File(rootFolderPath + File.separator + "capture" + File.separator + "temp1.jpg");
FileOutputStream os = null;
try {
os = new FileOutputStream(rootFile);
bitmap2.compress(Bitmap.CompressFormat.JPEG, 100, os);
} catch (Exception e) {
e.printStackTrace();
}
这样图片也保存了,看起来没啥问题,不过第三坑出现了,依旧后面解释.
最后
退出,释放资源
...
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
//释放相机资源
if (camera != null) {
camera.stopPreview();
camera.release();
}
}
...
一个简单的相机就完成了,试着运行一下,结果貌似有些出入,这就需要我们来解决上面提到的三个坑了,坑是什么,怎么解决,请看下篇文章