Camera2是Android5.0對拍照全新的改版設計
其中主要涉及:
- CameraMaganger:攝像頭的管理,主要檢測系統攝像頭、打開攝像頭,調用CameraManager的manager.getCameraCharacteristics(cameraId)獲取相機的屬性
- CameraCharacteristics:攝像頭的特性,用於描述攝像機支持的所有特性。
- CameraDevice:代表攝像頭,類似於早期的Camera
- CameraCaptureSession:非常重要的Api,不管是預覽,拍照,都需要其來控制,控制預覽的方法setRepeatingRequest(),控制拍照Capture().
- CameraRequest()和 CameraRequest.Builder(),當開始預覽或者拍照的時都需要傳入CameraRequest參數,CameraRequest代表一次捕獲請求,用於捕獲圖片的各種參數設置。
開發步驟:
1、獲取CameraManager實例,調用openCamera(String cameraId,callback , handler)
2、打開攝像頭成功之後,獲取CameraDevice對象。然後通過cameraDevice.createCaptureSession(Arrays.asList(surface,imageReader.getSurface()),
new CameraCaptureSession.StateCallback() ,Handler handler)創建Session
3、不管預覽還是錄製、或者拍照都需要使用cameraDevice.createCaptureRequest(int type)指定類型:CameraDevice.TEMPLATE_PREVIEW(預覽),CameraDevice.TEMPLATE_STILL_CAPTURE(拍照),CameraDevice.TEMPLATE_RECORD(拍攝)等。
完整例子:
package com.example.camera2demo;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.res.Configuration;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.util.Size;
import android.util.SparseIntArray;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.widget.Toast;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class TakePhotoActivity extends AppCompatActivity {
String TAG = "=======TakePhotoActivity=========";
/*相機管理類*/
private CameraManager manager;
/*相機的方向 默認爲後置*/
private String cameraId = "0";
/*相機類*/
private CameraDevice cameraDevice;
private CameraCaptureSession captureSession;
private CaptureRequest.Builder previewRequestBuilder;
private ImageReader imageReader;
private AutoFitTextureView mTextureView ;
public final static SparseIntArray o = new SparseIntArray();
static {
o.append(Surface.ROTATION_0,90);
o.append(Surface.ROTATION_90,0);
o.append(Surface.ROTATION_180,270);
o.append(Surface.ROTATION_270,180);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_take_photo);
initView();
init();
}
private void initView() {
mTextureView = findViewById(R.id.mTextureView);
mTextureView.setSurfaceTextureListener(surfaceTextureListener);
findViewById(R.id.btn_capture).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
capturePicture();
}
});
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void init() {
manager = (CameraManager) getSystemService(CAMERA_SERVICE);
}
@SuppressLint("MissingPermission")
public void openCamera() {
try {
manager.openCamera(cameraId, callback, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
/**
* 創建預覽
*/
private void createCameraPreviewSession() {
try {
previewRequestBuilder=cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
Surface surface =new Surface(mTextureView.getSurfaceTexture());
previewRequestBuilder.addTarget(surface);
cameraDevice.createCaptureSession(Arrays.asList(surface,imageReader.getSurface()), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
try {
if (cameraDevice==null)return;
captureSession = session;
/*設置自動對焦模式*/
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
/*設置自動曝光*/
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
/*設置預覽是連續捕獲圖片*/
session.setRepeatingRequest(previewRequestBuilder.build(),null,null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
}
},null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
/**
* 拍照
*/
public void capturePicture(){
try {
CaptureRequest.Builder builder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
builder.addTarget(imageReader.getSurface());
/*設置自動對焦模式*/
builder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
/*設置自動曝光*/
builder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
/*獲取設備方向*/
int rotation = getWindowManager().getDefaultDisplay().getRotation();
builder.set(CaptureRequest.JPEG_ORIENTATION,o.get(rotation));
Log.v(TAG,"設備方向 = "+rotation) ;
/*停止取景*/
captureSession.stopRepeating();
/*拍照*/
captureSession.capture(builder.build(), new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
super.onCaptureCompleted(session, request, result);
Log.v(TAG,"onCaptureCompleted");
try {
/*設置自動對焦模式*/
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
/*設置自動曝光*/
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
/*連續取景*/
captureSession.setRepeatingRequest(previewRequestBuilder.build(),null,null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
},null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private CameraDevice.StateCallback callback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
/*相機連接激活成功*/
cameraDevice = camera ;
createCameraPreviewSession();
Log.v(TAG,"====onOpened==");
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
/*相機斷開連接*/
cameraDevice.close();
cameraDevice = null ;
Log.v(TAG,"====onDisconnected==");
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
cameraDevice.close();
cameraDevice = null ;
Log.v(TAG,"====onError==");
}
};
private TextureView.SurfaceTextureListener surfaceTextureListener = new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
/*mTextureView 可用時候打開相機*/
setUpCameraOutputs(width,height);
openCamera();
Log.v(TAG,"====onSurfaceTextureAvailable==");
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
Log.v(TAG,"====onSurfaceTextureSizeChanged==");
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
Log.v(TAG,"====onSurfaceTextureDestroyed==");
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
// Log.v(TAG,"====onSurfaceTextureUpdated==");
}
};
/**
* @param width
*
* @param height
*/
private void setUpCameraOutputs(int width,int height){
try {
CameraManager manager = (CameraManager) getSystemService(CAMERA_SERVICE);
CameraCharacteristics cameraCharacteristics = manager.getCameraCharacteristics(cameraId);
/*獲取相機配置屬性*/
StreamConfigurationMap map = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
/*獲取所有支持的尺寸*/
Size[] out = map.getOutputSizes(ImageFormat.JPEG);
/*獲取最大支持尺寸*/
Size maxSize = Collections.max(Arrays.asList(out), new CompareSizeByArea());
imageReader = ImageReader.newInstance(maxSize.getWidth(),maxSize.getHeight(),ImageFormat.JPEG,2);
imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
Log.v(TAG,"======onImageAvailable=====");
Image image =reader.acquireNextImage();
ByteBuffer byteBuffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[byteBuffer.remaining()];
byteBuffer.get(bytes);
FileOutputStream fileOutputStream = null;
BufferedOutputStream bufferedOutputStream = null;
try {
File file = new File(Environment.getExternalStorageDirectory().getPath()+File.separator+System.currentTimeMillis()+".jpg");
file.createNewFile();
fileOutputStream=new FileOutputStream(file);
bufferedOutputStream=new BufferedOutputStream(fileOutputStream);
bufferedOutputStream.flush();
bufferedOutputStream.write(bytes);
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(TakePhotoActivity.this,"save",Toast.LENGTH_LONG).show();
}
});
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if (fileOutputStream!=null){
fileOutputStream.close();
}
if (bufferedOutputStream!=null){
bufferedOutputStream.close();
}
if (image!=null){
image.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
},null);
previewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),width,height,maxSize);
int oriention = getResources().getConfiguration().orientation ;
if (oriention == Configuration.ORIENTATION_LANDSCAPE){
mTextureView.setAspectRatio(previewSize.getWidth(),previewSize.getHeight());
}else {
mTextureView.setAspectRatio(previewSize.getHeight(),previewSize.getWidth());
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
Size previewSize;
/**
* @param width
* @param height
* @param aspectRatio
* @return
*/
public static Size chooseOptimalSize(Size[] choices,int width,int height,Size aspectRatio){
List<Size> bigEnough = new ArrayList<>() ;
int w = aspectRatio.getWidth() ;
int h = aspectRatio.getHeight() ;
for (Size option:choices){
if (option.getHeight() == option.getWidth() * h / w && option.getWidth()>=width && option.getHeight() >= height){
bigEnough.add(option) ;
}
}
if (bigEnough.size()>0){
return Collections.min(bigEnough,new CompareSizeByArea());
}else {
return choices [0] ;
}
}
/**
* 比較 大小
*/
static class CompareSizeByArea implements Comparator<Size> {
@Override
public int compare(Size o1, Size o2) {
/*數值比較 1 0 -1 */
return Long.signum(o1.getHeight()*o1.getWidth() - o2.getHeight()*o2.getWidth() );
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (null !=cameraDevice){
cameraDevice.close();
cameraDevice = null ;
}
}
}
AutoFitTextureView .class
package com.example.camera2demo;
import android.content.Context;
import android.util.AttributeSet;
import android.view.TextureView;
public class AutoFitTextureView extends TextureView {
int mRatioWidth ;
int mRatioHeight ;
/**
* 動態改變 顯示大小
* @param mRatioWidth
* @param mRatioHeight
*/
public void setAspectRatio( int mRatioWidth, int mRatioHeight ){
this.mRatioHeight = mRatioHeight ;
this.mRatioWidth = mRatioWidth ;
requestLayout();
}
public AutoFitTextureView(Context context) {
this(context,null);
}
public AutoFitTextureView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public AutoFitTextureView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int w = MeasureSpec.getSize(widthMeasureSpec);
int h = MeasureSpec.getSize(heightMeasureSpec) ;
if ( 0 == mRatioWidth || 0==mRatioHeight){
setMeasuredDimension(w,h);
}else {
if (w < h * mRatioWidth / mRatioHeight){
setMeasuredDimension(w , mRatioHeight * w /mRatioWidth );
}else {
setMeasuredDimension(h*mRatioWidth / mRatioHeight , h);
}
}
}
}
layout.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".TakePhotoActivity">
<com.example.camera2demo.AutoFitTextureView
android:id="@+id/mTextureView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ImageView
android:id="@+id/btn_capture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
android:layout_gravity="bottom|center_horizontal"/>
</FrameLayout>
權限
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>