服务器之家

服务器之家 > 正文

Android自定义Camera实现拍照功能

时间:2022-02-25 15:28     来源/作者:忙碌的咖啡

本文记录了用自定义Camera实现的简单拍照功能。

Camera类在5.0以后不推荐使用了,取而代之的是android.hardware.camera2包下的类,本文使用Camera。
我们首先自定义一个View去继承SurfaceView:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
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:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?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中去完成拍照功能:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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();
   }
  }
 }
}

最后记得添加拍照跟磁盘操作权限:

?
1
2
<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

标签:

相关文章

热门资讯

2022年最旺的微信头像大全 微信头像2022年最新版图片
2022年最旺的微信头像大全 微信头像2022年最新版图片 2022-01-10
蜘蛛侠3英雄无归3正片免费播放 蜘蛛侠3在线观看免费高清完整
蜘蛛侠3英雄无归3正片免费播放 蜘蛛侠3在线观看免费高清完整 2021-08-24
背刺什么意思 网络词语背刺是什么梗
背刺什么意思 网络词语背刺是什么梗 2020-05-22
yue是什么意思 网络流行语yue了是什么梗
yue是什么意思 网络流行语yue了是什么梗 2020-10-11
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全 2019-12-26
返回顶部