自己总结的拍照步骤,网上类似的代码很多,但是没有写第一步干什么第二步干什么,我看的云里雾里
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
1.创建CameraInfo对象,CaneraInfo里面装的就是设备的摄像头信息
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
2.CaneraInfo里面装的就是设备的摄像头信息,这里列一下常用的方法
因为设备可能有多个摄像头,所以先获取设备上的摄像头数量
int cameraCount = Camera.getNumberOfCameras();
获取摄像头朝向,摄像头朝向有两个方向,1代表前摄,0代表后摄
int facing = cameraInfo.facing
3.获取摄像头图像方向,也就是摄像机图像需要顺时针旋转的角度,以便以正确的方向正确显示在显示器上。 具体的值为0,90,180或270
int orientation = cameraInfo.orientation
4.摄像头是否可以禁用快门声音
如果此字段为true,则enableShutterSound(false)的呼叫将成功。 如果设置为false,则该呼叫将失败,并且当takePicture时会播放快门声音。
boolean DisableShutterSound= cameraInfo.canDisableShutterSound
Camera camera = Camera.open(摄像头id)
Camera.Parameters parameters = camera.getParameters();
parameters.setPreviewSize(1920, 1080);
parameters.setPictureSize(1920, 1080);
parameters.setPreviewFormat(ImageFormat.NV21);
parameters中的参数可以不设置也行,但是如果设置了parameters参数的话必须要加上这一行代码,否则设置不会生效:
camera.setParameters(parameters);
这个参数是必须要设置的,否则无法预览,最简单的方法:layout中添加SurfaceView控件,这里直接引用就行
camera.setPreviewDisplay(surfaceView.getHolder());
首先获取设备当前横竖屏状态
int rotation = getWindowManager().getDefaultDisplay().getRotation();
然后计算正确的方向
int result = 0;
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
//后置摄像头
result = (cameraInfo.orientation - rotation + 360) % 360;
} else if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
//前置摄像头,多一步镜像
result = (cameraInfo.orientation + rotation ) % 360;
result = (360 - result) % 360;
}
最后将正确的方向设置到摄像头中
camera.setDisplayOrientation(result);
下面这个监听器也是必须要加的,不加的话预览画面会卡住不动,这个监听器有一个参数byte[] data就是预览的图像数据,如果需要做二维码扫描,AI识别这些功能的话就可以从这里取这个数据用了
Camera.PreviewCallback preCallback = new Camera.PreviewCallback(){
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
camera.addCallbackBuffer(data);
}
};
然后将监听器添加到相机中
camera.setPreviewCallbackWithBuffer(preCallback);
还有其他参数就不写了,我也不懂,反正现在这样已经能用了
上面的参数设置完后就一行代码打开预览ok
camera.startPreview();
拍照注意事项:
1.必须是已经打开了预览的时候才可以拍照,否者拍不了
2.拍照的时候预览会自动停止,但是拍完照后预览不会自动打开,所以需要在拍照之后加一行打开预览的代码
拍照步骤:
1.创建一个异步线程用来处理拍摄的照片数据
2.创建一个拍照的回调,Camera.PictureCallback(),重写回调中的onPictureTaken()方法,在这个方法中打开处理数据的异步线程处理数据
3.将拍照的回调添加到拍照的方法中
camera.takePicture(null, null, 回调程序 )
camera.takePicture(null, null, new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
MyAsyncTask myTask = new MyAsyncTask(MainActivity.this, cameraInfo.facing, data,camera);
myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
});
MainActivity.java
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.hardware.Camera;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
public class MainActivity extends AppCompatActivity implements View.OnClickListener, Camera.PreviewCallback {
private SurfaceView surfaceView;
private Button photograph;
private Button recording;
private Button switchs;
private int rotation;
private int shortSize;
private int longSize;
private Camera camera;
private Camera.CameraInfo cameraInfo;
private Camera.Parameters parameters;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
surfaceView = (SurfaceView) findViewById(R.id.camera_view);
photograph = (Button) findViewById(R.id.photograph);
recording = (Button) findViewById(R.id.recording);
switchs = (Button) findViewById(R.id.switchs);
photograph.setOnClickListener(this);
recording.setOnClickListener(this);
switchs.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.photograph: //拍照
if (camera == null) {
Toast.makeText(this, "请打开相机", Toast.LENGTH_SHORT).show();
return;
}
parameters = camera.getParameters();
parameters.setPictureFormat(ImageFormat.JPEG);
camera.setParameters(parameters);
camera.startPreview();
camera.takePicture(null, null, new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
MyAsyncTask myTask = new MyAsyncTask(MainActivity.this, cameraInfo.facing, data,camera);
myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
});
break;
case R.id.recording:
break;
case R.id.switchs:
break;
}
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
int[] a = new int[2];
surfaceView.getLocationOnScreen(a);
shortSize = surfaceView.getHeight(); //横屏为宽度,竖屏为高度
longSize = surfaceView.getWidth();
rotation = getWindowManager().getDefaultDisplay().getRotation();
cameraInfo = new Camera.CameraInfo();
checkCameraPermission();
}
}
// 1.申请相机权限---------------------------------------------------------------------------
private void checkCameraPermission() {
//检查是否有相机权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
//没权限,请求权限
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE},
1);
} else {
//有权限
Log.d("MainActivity", "有权限");
getCameras(0); //0为后摄,1为前摄
}
}
//权限请求回调
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults != null && grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//用户同意权限
getCameras(0); //0为后摄,1为前摄
} else {
// 权限被用户拒绝了,可以提示用户,关闭界面等等。
Toast.makeText(this, "拒绝权限,请去设置里面手动开启权限", Toast.LENGTH_SHORT).show();
}
break;
}
}
//预览数据监听
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
camera.addCallbackBuffer(data);
}
/*
3.查找指定摄像头ID,根据摄像头ID打开指定的相机----------------------------------------------------------------------------------------------
根据摄像头的朝向查找指定摄像头id
0代表后摄的朝向,但是它不一定是后摄的id
1代表前摄的朝向
*/
public void getCameras(int lens_facing) {
//先释放相机资源
releaseCamera();
//获取相机信息
if (cameraInfo == null) {
cameraInfo = new Camera.CameraInfo();
}
//获取相机个数
int cameraCount = Camera.getNumberOfCameras();
//由于不知道第几个是前置摄像头,遍历获取前置摄像头
for (int camIdx = 0; camIdx < cameraCount; camIdx++) {
Camera.getCameraInfo(camIdx, cameraInfo);
if (cameraInfo.facing == lens_facing) {
//根据id打开相机
Log.d("MainActivity", "相机id为" + camIdx);
camera = Camera.open(camIdx);
setPreview();
break;
}
}
}
/*
4.设置预览参数和相机参数后开启预览-----------------------------------------------------------
*/
public void setPreview() {
try {
//设置摄像头参数
parameters = camera.getParameters();
//设置预览大小
parameters.setPreviewSize(1920, 1080);
//设置图片大小,拍照
parameters.setPictureSize(1920, 1080);
//设置预览格式,所有的相机都支持 NV21格式
parameters.setPreviewFormat(ImageFormat.NV21);
//设置聚焦
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
//修改相机摄像头参数
camera.setParameters(parameters);
//修改相机预览方向
camera.setDisplayOrientation(adjustCameraOrientation());
//绑定预览视图
camera.setPreviewDisplay(surfaceView.getHolder());
//设置预览数据监听
camera.setPreviewCallbackWithBuffer(this);
//开始预览
camera.startPreview();
} catch (IOException e) {
}
}
//获取预览最佳尺寸
private Camera.Size getBestSize() {
List<Camera.Size> sizes = parameters.getSupportedPreviewSizes();
Camera.Size bestSize = null;
float uiRatio = (float) longSize / shortSize;
float minRatio = uiRatio;
for (Camera.Size previewSize : sizes) {
float cameraRatio = (float) previewSize.width / previewSize.height;
//如果找不到比例相同的,找一个最近的,防止预览变形
float offset = Math.abs(cameraRatio - minRatio);
if (offset < minRatio) {
minRatio = offset;
bestSize = previewSize;
}
//比例相同
if (uiRatio == cameraRatio) {
bestSize = previewSize;
break;
}
}
return bestSize;
}
/**
* 调整预览方向
* 由于手机的图片数据都来自摄像头硬件传感器,这个传感器默认的方向横向的,所以要根据前后摄像头调整方向
*/
private int adjustCameraOrientation() {
//判断当前的横竖屏
int degress = 0;
//获取手机的方向
switch (rotation) {
case Surface.ROTATION_0:
degress = 0;
break;
case Surface.ROTATION_90:
degress = 90;
break;
case Surface.ROTATION_180:
degress = 180;
break;
case Surface.ROTATION_270:
degress = 270;
break;
}
int result = 0;
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
//后置摄像头
result = (cameraInfo.orientation - degress + 360) % 360;
} else if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
//前置摄像头,多一步镜像
result = (cameraInfo.orientation + degress) % 360;
result = (360 - result) % 360;
}
return result;
}
//释放相机资源
private void releaseCamera() {
if (camera != null) {
camera.stopPreview();
camera.stopFaceDetection();
camera.setPreviewCallback(null);
camera.release();
camera = null;
}
}
}
MyAsyncTask.java
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.hardware.Camera;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Environment;
import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class MyAsyncTask extends AsyncTask<Void, Void, File> {
Context ct;
int facing;
byte[] data;
Camera camera;
public MyAsyncTask(Context ct, int facing, byte[] data, Camera camera) {
this.ct = ct;
this.facing = facing;
this.data = data;
this.camera = camera;
}
@Override
protected File doInBackground(Void... voids) {
File file = null;
//处理图片数据
Bitmap bitmap= BitmapFactory.decodeByteArray(data, 0, data.length);
if (facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
Matrix matrix = new Matrix();
matrix.postRotate(180);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
//创建图片文件
try{
String filePath=null;
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
if(Build.VERSION.SDK_INT< 29){
filePath=Environment.getExternalStorageDirectory().getCanonicalPath();
}else{
filePath=ct.getExternalFilesDir(null).getAbsolutePath();
}
}
file = new File(filePath, System.currentTimeMillis() + ".jpg");
//将图片数据写入图片文件
FileOutputStream outStream=new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG,100,outStream);
outStream.flush();
outStream.close();
}catch(IOException e){
e.printStackTrace();
}
return file;
}
@Override
protected void onPostExecute(File file) {
super.onPostExecute(file);
if (file != null) {
Toast.makeText(ct, "图片保存成功", Toast.LENGTH_SHORT).show();
camera.startPreview();
} else {
Toast.makeText(ct, "图片保存失败", Toast.LENGTH_SHORT).show();
}
}
}
本片内容比较粗浅,没有深入,主要是我理解的还不够,但是基本的步骤已经很清晰了,能保证基本的拍照没问题,至于拍出来保存的是个什么样子就不敢保证了,有空再研究