API1的方法较少、命名规则等都比较简单,如果是针对目前市面上的手机,API1是足够而且使用起来非常方便,尤其是它的setParameter方法,相较于API2的要自己去填key和value来说,它不仅很容易能找到相机支持的(使用parameter.getSupportedXXX可以直接获得相应参数)尺寸特效白平衡等(key),value值也是比较一目了然的;而API2不仅类多,方法多,名字之间很相似,状态之间调来调去,需要花费较多时间去了解才能正确使用。
但API2也是有好处的,我以为最大的好处就是内部封装了一个HandlerThread,从而在开启相机、预览、尤其是处理图片的时候速度会快很多。话不多说,这篇文主要是API1.
Camera1这个貌似过时,实际大家都在用,用法超级简单,在Activity onCreate的时候初始化SurfaceView或者TextureView,如果使用SurfaceView要得到它的holder,并设置holder的Type,然后在onCreate或者onResume方法中打开并初始化相机参数,如对焦模式等,监听SurfaceView Holder或Textureview的状态变化回调方法,都是在surfaceCreated或者onSurfaceTextureAvaliable方法中设置相机的previewSurface或者previewTexture,再调用相机的startPreview方法即可实现实时预览。这里首先记住要开启相机的权限,另外,如果是急性子要多次运行看结果的,记得现在就在onPauce或者onDestory方法中释放相机资源,免得下次打开不了相机。
//设置全屏显示
private void setFullScreen() {
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN;
getWindow().setFlags(flag,flag);
}
//开启相机
private void initCamera() {
cameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
if(camera==null){
camera = Camera.open(cameraId);
}
}
或者最简单的一个不传参,默认开启后摄像头
private void initCamera() {
if(camera==null){
camera = Camera.open();
}
}
// 设置初始相机参数
@Override
protected void onResume() {
super.onResume();
setCameraDisplayOrientation();
param = camera.getParameters();
param.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
param.setPictureFormat(ImageFormat.JPEG);
param.setJpegQuality(100);
param.setPreviewSize(param.getSupportedPreviewSizes().get(0).width,param.getSupportedPreviewSizes().get(0).height);
Log.e(TAG,”param.getSupportedPreviewSizes()”+param.getSupportedPreviewSizes().get(0).width+” X “+param.getSupportedPreviewSizes().get(0).height);
param.setPictureSize(param.getSupportedPictureSizes().get(0).width,param.getSupportedPictureSizes().get(0).height);
camera.setParameters(param);
}
预览时最好是设置对焦模式为Camera.Parameters.FOCUS_MODE_CONTINOUS_PICTURE,这样一般的拍摄字体之类的都会比较清晰而且会持续对焦;如果有拍照需求,在布局文件添加button实现点击监听方法中直接调用camera.takePciture(null,null,pictureCallBack)方法,第一个null是拍摄声音为静音,第二个拍照为raw格式,这里也为null,第三个是要重写回调方法,里面直接调用takePicture方法有参数为byte[]data,这就是传递过来的照片数据,可以直接写入文件保存得到照片,也可以用bitmapFactory得到照片并显示。这里存储要开启sdcard的写入权限。
这时候有了照片了,但有的相机可能因为没有设置previewsize和picturesize图片非常的小和模糊,但如果是随便设置size有可能会导致黑屏不显示预览。Camera1里有一系列的parameter.getSupportXXX方法,可以得到相机支持的各种参数,然后再设置进去。
同样,放大缩小方法,直接调用parameter.setZoom(zoomValue)即可,还有各种特效、白平衡等,最后一定记得要camera.setParameter(parameter)把设置好了的各种参数再传递给相机才能生效。
另外还有镜头翻转的问题,可以直接camera.setDisplayOrientation(90);粗暴解决,可以适合一部分机型,这里还有比较稳妥的解决预览角度不对问题的代码、以及设置合适previewSize、合适pictureSize、放大缩小、特效白平衡、保存图片的代码,可以直接放在合适的地方使用。
//设置相机预览展示正确角度
private void setCameraDisplayOrientation() {
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
int rotation = getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0: degrees = 0; break;
case Surface.ROTATION_90: degrees = 90; break;
case Surface.ROTATION_180: degrees = 180; break;
case Surface.ROTATION_270: degrees = 270; break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees) % 360;
}
camera.setDisplayOrientation(result);
}
SurfaceView以及它的回调方法也在MainActivity中了,逻辑较少,所以耦合比较高,后面会上传分开的代码:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setFullScreen();
setContentView(R.layout.activity_main);
initCamera();
initView();
}
private void initView() {
surview = findViewById(R.id.surview);
holder = surview.getHolder();
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
btn_zoom = findViewById(R.id.btn_zoom);
btn_effect = findViewById(R.id.btn_effect);
btn_capture = findViewById(R.id.btn_capture);
}
private void initCamera() {
cameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
if(camera==null){
camera = Camera.open(cameraId);
}
}
private void setCameraDisplayOrientation() {
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
int rotation = getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0: degrees = 0; break;
case Surface.ROTATION_90: degrees = 90; break;
case Surface.ROTATION_180: degrees = 180; break;
case Surface.ROTATION_270: degrees = 270; break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees) % 360;
}
camera.setDisplayOrientation(result);
}
private void setFullScreen() {
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN;
getWindow().setFlags(flag,flag);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
this.holder = holder;
try {
camera.setPreviewDisplay(holder);
camera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
this.holder = holder;
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
@Override
protected void onResume() {
super.onResume();
setCameraDisplayOrientation();
param = camera.getParameters();
param.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
param.setPictureFormat(ImageFormat.JPEG);
param.setJpegQuality(100);
param.setPreviewSize(param.getSupportedPreviewSizes().get(0).width,param.getSupportedPreviewSizes().get(0).height);
Log.e(TAG,"param.getSupportedPreviewSizes()"+param.getSupportedPreviewSizes().get(0).width+" X "+param.getSupportedPreviewSizes().get(0).height);
param.setPictureSize(param.getSupportedPictureSizes().get(0).width,param.getSupportedPictureSizes().get(0).height);
camera.setParameters(param);
}
@Override
protected void onPause() {
super.onPause();
if(camera!=null){
camera.release();
camera=null;
}
}
private Camera.PictureCallback pictureCallBack = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
savePicture(data);
}
};
private void savePicture(byte[] data) {
String root = Environment.getExternalStorageDirectory().getPath();
Log.e(TAG,"Environment.getExternalStorageState();"+root);
File filedir = new File(root,"CAMERAV1");
Log.e(TAG,"new File(root,\"CAMERAV1\");"+filedir.getPath());
if(!filedir.exists()){
if(!filedir.mkdirs()){
Toast.makeText(this,"不能在sdcard上创建相关文件",Toast.LENGTH_SHORT).show();
}
}
String temp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File objPic = new File(filedir,"IMG_"+temp+".jpg");
BufferedOutputStream bos = null;
try {
bos = new BufferedOutputStream(new FileOutputStream(objPic));
bos.write(data);
bos.flush();
Toast.makeText(this,"拍照成功",Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(bos!=null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void doClick(View view) {
switch (view.getId()){
case R.id.btn_capture:
camera.takePicture(null,null,pictureCallBack);
break;
case R.id.btn_effect:
param = camera.getParameters();
//测试的
param.setColorEffect(param.getSupportedColorEffects().get(1));
camera.setParameters(param);
break;
case R.id.btn_zoom:
param = camera.getParameters();
param.setZoom(param.getZoom()+5);
camera.setParameters(param);
break;
}
}