简介
网上对于 Camera2 的介绍有很多,在 Github 上也有很多关于 Camera2 的封装库,但是对于那些库,封装性太强,有时候我们仅仅是需要个简简单单的拍照功能而已,因此,自定义一个 Camera 使之变得轻量级那是非常重要的了。(本文并非重复造轮子, 而是在于学习 Camera2API 的基本功能, 笔记之。)
学习要点:
使用 Android Camera2 API 的基本功能。
迭代连接到设备的所有相机的特征。
显示相机预览和拍摄照片。
Camera2 API 为连接到 Android 设备的各个相机设备提供了一个界面。 它替代了已弃用的 Camera 类。
使用 getCameraIdList 获取所有可用摄像机的列表。 然后,您可以使用 getCameraCharacteristics,并找到适合您需要的最佳相机(前 / 后面,分辨率等)。
创建一个 CameraDevice.StateCallback 的实例并打开相机。 当相机打开时,准备开始相机预览。
使用 TextureView 显示相机预览。 创建一个 CameraCaptureSession 并设置一个重复的 CaptureRequest。
静像拍摄需要几个步骤。 首先,需要通过更新相机预览的 CaptureRequest 来锁定相机的焦点。
然后,以类似的方式,需要运行一个预捕获序列。之后,它准备拍摄一张照片。 创建一个新的 CaptureRequest 并调用 [capture] 。
完成后,别忘了解锁焦点。
实现效果
环境
SDK>21
Camera2 类图
代码实现
CameraPreview.java
/**
* Created by shenhua on 2017-10-20-0020.
* Email shenhuanet@126.com
*/
public class CameraPreview extends TextureView {
private static final String TAG = "CameraPreview";
private static final SparseIntArray ORIENTATIONS = new SparseIntArray();//从屏幕旋转转换为JPEG方向
private static final int MAX_PREVIEW_WIDTH = 1920;//Camera2 API 保证的最大预览宽高
private static final int MAX_PREVIEW_HEIGHT = 1080;
private static final int STATE_PREVIEW = 0;//显示相机预览
private static final int STATE_WAITING_LOCK = 1;//焦点锁定中
private static final int STATE_WAITING_PRE_CAPTURE = 2;//拍照中
private static final int STATE_WAITING_NON_PRE_CAPTURE = 3;//其它状态
private static final int STATE_PICTURE_TAKEN = 4;//拍照完毕
private int mState = STATE_PREVIEW;
private int mRatioWidth = 0, mRatioHeight = 0;
private int mSensorOrientation;
private boolean mFlashSupported;
private Semaphore mCameraOpenCloseLock = new Semaphore(1);//使用信号量 Semaphore 进行多线程任务调度
private Activity activity;
private File mFile;
private HandlerThread mBackgroundThread;
private Handler mBackgroundHandler;
private Size mPreviewSize;
private String mCameraId;
private CameraDevice mCameraDevice;
private CaptureRequest.Builder mPreviewRequestBuilder;
private CaptureRequest mPreviewRequest;
private CameraCaptureSession mCaptureSession;
private ImageReader mImageReader;
static {
ORIENTATIONS.append(Surface.ROTATION_0, 90);
ORIENTATIONS.append(Surface.ROTATION_90, 0);
ORIENTATIONS.append(Surface.ROTATION_180, 270);
ORIENTATIONS.append(Surface.ROTATION_270, 180);
}
public CameraPreview(Context context) {
this(context, null);
}
public CameraPreview(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CameraPreview(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mFile = new File(getContext().getExternalFilesDir(null), "pic.jpg");
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (0 == mRatioWidth || 0 == mRatioHeight) {
setMeasuredDimension(width, height);
} else {
if (width < height * mRatioWidth / mRatioHeight) {
setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
} else {
setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
}
}
}
public void onResume(Activity activity) {
this.activity = activity;
startBackgroundThread();
//当Activity或Fragment OnResume()时,可以冲洗打开一个相机并开始预览,否则,这个Surface已经准备就绪
if (this.isAvailable()) {
openCamera(this.getWidth(), this.getHeight());
} else {
this.setSurfaceTextureListener(mSurfaceTextureListener);
}
}
public void onPause() {
closeCamera();
stopBackgroundThread();
}
public void setOutPutDir(File file) {
this.mFile = file;
}
public void setAspectRatio(int width, int height) {
if (width < 0 || height < 0) {
throw new IllegalArgumentException("Size can't be negative");
}
mRatioWidth = width;
mRatioHeight = height;
requestLayout();
}
public void setAutoFlash(CaptureRequest.Builder requestBuilder) {
if (mFlashSupported) {
requestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
}
}
public void takePicture() {
lockFocus();
}
private void startBackgroundThread() {
mBackgroundThread = new HandlerThread("