<uses-permission android:name="android.permission.CAMERA" />
很簡單的佈局,只有一個 TextureView
。php
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".fragments.PreviewFragment">
<TextureView android:id="@+id/ttv_camera" android:layout_width="wrap_content" android:layout_height="wrap_content" />
</RelativeLayout>
複製代碼
在具體實現編碼以前,先簡單介紹下相關的幾個重要的類。java
3.1 初始化界面和 CameraManagerandroid
在 fragment 的 onViewCreated
聲明週期方法中進行。git
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
previewView = view.findViewById(R.id.ttv_camera);
//初始化 CameraManager
cameraManager = (CameraManager) getActivity().getSystemService(Context.CAMERA_SERVICE);
}
複製代碼
3.2 設置預覽TextureView狀態監聽github
在 fragment 的 onResume
聲明週期方法中對 TextureView 的狀態進行監聽,當其可用是會調用監聽器的 onSurfaceTextureAvailable
方法,便可在該方法中啓動相機。session
@Override
public void onResume() {
super.onResume();
//設置 TextureView 的狀態監聽
previewView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
//TextureView 可用時調用改回調方法
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
//TextureView 可用,啓動相機
setupCamera();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
});
}
複製代碼
3.3 啓動相機ide
啓動相機能夠分爲兩個步驟: 配置相機相關參數,打開相機。佈局
private void setupCamera() {
//配置相機參數(cameraId,previewSize)
configCamera();
//打開相機
openCamera();
}
複製代碼
配置相機相關參數(待打開的相機id,相機輸出尺寸)。ui
private void configCamera() {
try {
//遍歷相機列表,使用前置相機
for (String cid : cameraManager.getCameraIdList()) {
//獲取相機配置
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cid);
//使用後置相機
int facing = characteristics.get(CameraCharacteristics.LENS_FACING);//獲取相機朝向
if (facing == CameraCharacteristics.LENS_FACING_FRONT) {
continue;
}
//獲取相機輸出格式/尺寸參數
StreamConfigurationMap configs = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
//打印相關參數(相機id,相機輸出尺寸、設備屏幕尺寸、previewView尺寸)
printSizes(cid, configs);
//設定最佳預覽尺寸
previewSize = setOptimalPreviewSize(configs.getOutputSizes(SurfaceTexture.class),
previewView.getMeasuredWidth(),
previewView.getMeasuredHeight());
//打印最佳預覽尺寸
Log.d(TAG, "最佳預覽尺寸(w-h):" + previewSize.getWidth() + "-" + previewSize.getHeight());
cameraId = cid;
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private Size setOptimalPreviewSize(Size[] sizes, int previewViewWidth, int previewViewHeight) {
List<Size> bigEnoughSizes = new ArrayList<>();
List<Size> notBigEnoughSizes = new ArrayList<>();
for (Size size : sizes) {
if (size.getWidth() >= previewViewWidth && size.getHeight() >= previewViewHeight) {
bigEnoughSizes.add(size);
} else {
notBigEnoughSizes.add(size);
}
}
if (bigEnoughSizes.size() > 0) {
return Collections.min(bigEnoughSizes, new CompareSizesByArea());
} else if (notBigEnoughSizes.size() > 0) {
return Collections.max(notBigEnoughSizes, new CompareSizesByArea());
} else {
Log.d(TAG, "未找到合適的預覽尺寸");
return sizes[0];
}
}
複製代碼
打開相機編碼
CameraManager.openCamera()
方法打開相機。onOpened()
回調方法。onOpened()
回調方法中建立預覽會話。(很重要)
CameraDevice.createCaptureSession()
方法建立捕獲會話,第一個參數是捕獲數據的輸出Surface列表,第二個參數是CameraCaptureSession的狀態回調接口,當它建立好後會回調onConfigured方法,第三個參數用來肯定Callback在哪一個線程執行,爲null的話就在當前線程執行。private void openCamera() {
try {
//打開相機
cameraManager.openCamera(cameraId,
new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice camera) {
cameraDevice = camera;
//建立相機預覽 session
createPreviewSession();
}
@Override
public void onDisconnected(CameraDevice camera) {
//釋放相機資源
releseCamera();
}
@Override
public void onError(CameraDevice camera, int error) {
//釋放相機資源
releseCamera();
}
},
null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void createPreviewSession() {
//根據TextureView 和 選定的 previewSize 建立用於顯示預覽數據的Surface
SurfaceTexture surfaceTexture = previewView.getSurfaceTexture();
surfaceTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());//設置SurfaceTexture緩衝區大小
final Surface previewSurface = new Surface(surfaceTexture);
try {
//建立預覽session
cameraDevice.createCaptureSession(Arrays.asList(previewSurface),
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
try {
//構建預覽捕獲請求
CaptureRequest.Builder builder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
builder.addTarget(previewSurface);//設置 previewSurface 做爲預覽數據的顯示界面
CaptureRequest captureRequest = builder.build();
//設置重複請求,以獲取連續預覽數據
session.setRepeatingRequest(captureRequest, new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult) {
super.onCaptureProgressed(session, request, partialResult);
}
@Override
public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
super.onCaptureCompleted(session, request, result);
}
},
null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
}
},
null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void releseCamera() {
if (cameraDevice != null) {
cameraDevice.close();
cameraDevice = null;
}
}
複製代碼
從下面效果圖能夠看到,相機預覽的畫面有變形,須要進一步處理。
相機豎向的效果圖
相機橫向的效果圖
本文實現了最基礎的相機預覽功能,重點放在梳理相機預覽的關鍵步驟和相關類的使用。
針對畫面拉伸變形的糾正以及橫豎向佈局切換的問題,後續文章進行實現。