google已經在Android5.1以後取消了對Camera1的更新,轉而提供了功能更增強大的Camera2.雖然新版本依然可使用Camera1可是,不論是各類機型適配仍是拍照參數自定義都是很雞肋的.跟上最新的技術瞭解Camera2是必要的.關於Camera2的兼容通常是支持API22以後包括API22的Android版本,可是也發現一些機型(好比三星)在API22版本上並無支持Camera2.java
由於Camera2提供的功能更增強大,因此使用比Camera1會複雜許多.須要調用的API和回調也更多.這裏簡單介紹一下這些API的對應功能.好初步認識Camera2.android
攝像頭管理類:算法
主要有4個功能:緩存
攝像頭設備類:session
主要功能有3個app
攝像頭狀態接口回調類:ide
主要是負責回調攝像頭的開啓/斷開/異常/銷燬.咱們使用CameraManager打開指定id的攝像頭時須要添加這個回調.oop
獲取數據會話的狀態接口回調類:ui
咱們建立相機預覽圖像/拍照/錄像都須要這個回調類,來告訴咱們獲取數據會話的通道狀態是配置成功或者配置失敗.它還負責給咱們回調一個重要的CameraCaptureSession提供給咱們操做,這個CameraCaptureSession類我下面會介紹this
獲取數據會話的數據接口回調類:
負責回調獲取數據的生命週期(好比開始/進行中/完成/失敗等等),若是你並不須要對生命週期裏作操做,因此有時候沒有啥做用.可是它也是必需建立的一個回調接口類,是在建立預覽圖像/拍照/錄像的時候添加進去,可是拍照或者錄像的數據都不在這個回調接口裏出來(一開始很容易誤解,覺得拍照數據會從這裏返回).除了回調獲取數據的生命週期,還能夠在回調方法裏獲取拍照或者錄製過程的的一些參數信息,好比圖片的Size/分辨率等等.
獲取數據請求配置類:
很重要,也是咱們頻繁操做的一個配置類.由CameraDevice類建立.主要負責
數據配置完成後交給CameraCaptureSession會話類,讓CameraCaptureSession操做提供咱們須要的數據,例如圖像預覽或者拍照/錄製視頻
獲取數據會話類:
很重要,是咱們頻繁操做的一個數據會話類,好比建立預覽/中止預覽/拍照/錄像都要它來操做,它由CameraCaptureSession.StateCallback這個接口回調方法裏回調提供給咱們.
圖片讀取類:
不屬於Camera2Api的類,可是是拍照功能重要的類,照片的數據流由它緩存,而後咱們提取保存到本地成爲圖片文件或者顯示在ImageView裏
在上面的API介紹裏,你是否是對這麼多的配置類/會話類/接口回調類感到眼花繚亂?是的,Camera2的使用是至關眼花繚亂的,可是咱們抓住一條線慢慢從上面跟到下面就應該能明白是怎麼一回事了.下面咱們來簡單介紹一些Camera2的操做流程:
動態相機權限獲取 >> 設置TextureView回調 >> TextureView啓用成功回調方法觸發 >> 選擇攝像頭 >> 打開相機 >> 相機開啓回調方法觸發 >> 建立CaptureRequest.Builder配置類 >> 設置配置類圖像預覽模式 >> 配置類導入須要顯示預覽的TextureView的surface >> 建立數據會話 >> 數據會話的配置成功回調方法觸發 >> 建立預覽圖像 >> 預覽圖像顯示成功 >> 按鍵點擊拍照 >> 建立新的CaptureRequest.Builder配置類,添加目標爲拍照 >> 配置類導入ImageReader的surface >> 數據會話使用這個配置類建立拍照 >> ImageReader的接口類圖片可用方法觸發 >> 保存圖片
實現簡單的拍照功能demo
package demo.yt.com.demo; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import android.Manifest; import android.content.Context; import android.content.pm.PackageManager; 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.CaptureFailure; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.TotalCaptureResult; import android.hardware.camera2.params.StreamConfigurationMap; import android.media.Image; import android.media.ImageReader; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; 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.Button; import android.widget.Toast; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; public class Camera2Activity extends AppCompatActivity { private static final String TAG = Camera2Activity.class.getName(); private String[] permission = {Manifest.permission.CAMERA}; private TextureView mTextureView;//注意使用TextureView須要開啓硬件加速,開啓方法很簡單在AndroidManifest.xml 清單文件裏,你須要使用TextureView的activity添加 android:hardwareAccelerated="true" private Button mBtnPhotograph; private Handler mMainHandler = new Handler(Looper.getMainLooper()); private Handler mChildHanler = null; private CameraManager mCameraManager;//相機管理類,用於檢測系統相機獲取相機id private CameraDevice mCameraDevice;//Camera設備類 private CameraCaptureSession.StateCallback mSessionStateCallback;//獲取的會話類狀態回調 private CameraCaptureSession.CaptureCallback mSessionCaptureCallback;//獲取會話類的獲取數據回調 private CaptureRequest.Builder mCaptureRequest;//獲取數據請求配置類 private CameraDevice.StateCallback mStateCallback; //攝像頭狀態回調 private CameraCaptureSession mCameraCaptureSession; //獲取數據會話類 private ImageReader mImageReader; //照片讀取器 private String mCurrentCameraId; private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); // /爲了使照片豎直顯示 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); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_camera2); mTextureView = findViewById(R.id.textureview); mBtnPhotograph = findViewById(R.id.btn_Photograph); mBtnPhotograph.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { mCameraCaptureSession.stopRepeating();//中止重複 取消任何正在進行的重複捕獲集 在這裏就是中止畫面預覽 /* mCameraCaptureSession.abortCaptures(); 終止獲取 儘量快地放棄當前掛起和正在進行的全部捕獲。 * 這裏有一個坑,其實這個並不能隨便調用(我是看到別的demo這麼使用,可是實際上是錯誤的,因此就在這裏備註這個坑). * 最好只在Activity裏的onDestroy調用它,終止獲取是耗時操做,須要必定時間從新打開會話通道. * 在這個demo裏我並無恢復預覽,若是你調用了這個方法關閉了會話又拍照後恢復圖像預覽,會話就會頻繁的開關, * 會致使拍照圖片在處理耗時緩存時你又關閉了會話.致使照片緩存不完整而且失敗. * 因此切記不要隨便使用這個方法,會話開啓後並不須要關閉刷新.後續其餘拍照/預覽/錄製視頻直接操做這個會話便可 */ takePicture();//拍照 } catch (CameraAccessException e) { e.printStackTrace(); } } }); initPermission(); initChildThread(); initImageReader(); initTextureView(); } private void initPermission() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, permission, 1); } } private void initTextureView(){ mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { Log.e(TAG,"TextureView 啓用成功"); initCameraManager(); initCameraCallback(); initCameraCaptureSessionStateCallback(); initCameraCaptureSessionCaptureCallback(); selectCamera(); openCamera(); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { Log.e(TAG,"TextureView 變化"); } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { Log.e(TAG,"TextureView 銷燬"); return false; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { } }); } /** * 初始化子線程 */ private void initChildThread() { HandlerThread handlerThread = new HandlerThread("camera2"); handlerThread.start(); mChildHanler = new Handler(handlerThread.getLooper()); } /** * 初始化相機管理 */ private void initCameraManager() { mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); } /** * 獲取匹配的大小 * @return */ private Size getMatchingSize(){ Size selectSize = null; float selectProportion = 0; try { float viewProportion = (float)mTextureView.getWidth() / (float)mTextureView.getHeight(); CameraCharacteristics cameraCharacteristics = mCameraManager.getCameraCharacteristics(mCurrentCameraId); StreamConfigurationMap streamConfigurationMap = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); Size[] sizes = streamConfigurationMap.getOutputSizes(ImageFormat.JPEG); for (int i = 0; i < sizes.length; i++){ Size itemSize = sizes[i]; float itemSizeProportion = (float)itemSize.getHeight() / (float)itemSize.getWidth(); float differenceProportion = Math.abs(viewProportion - itemSizeProportion); Log.e(TAG, "相減差值比例="+differenceProportion ); if (i == 0){ selectSize = itemSize; selectProportion = differenceProportion; continue; } if (differenceProportion <= selectProportion){ if (differenceProportion == selectProportion){ if (selectSize.getWidth() + selectSize.getHeight() < itemSize.getWidth() + itemSize.getHeight()){ selectSize = itemSize; selectProportion = differenceProportion; } }else { selectSize = itemSize; selectProportion = differenceProportion; } } } } catch (CameraAccessException e) { e.printStackTrace(); } Log.e(TAG, "getMatchingSize: 選擇的比例是="+selectProportion); Log.e(TAG, "getMatchingSize: 選擇的尺寸是 寬度="+selectSize.getWidth()+"高度="+selectSize.getHeight()); return selectSize; } /** * 選擇攝像頭 */ private void selectCamera() { try { String[] cameraIdList = mCameraManager.getCameraIdList();//獲取攝像頭id列表 if (cameraIdList.length == 0) { return; } for (String cameraId : cameraIdList) { Log.e(TAG, "selectCamera: cameraId=" + cameraId); //獲取相機特徵,包含先後攝像頭信息,分辨率等 CameraCharacteristics cameraCharacteristics = mCameraManager.getCameraCharacteristics(cameraId); Integer facing = cameraCharacteristics.get(CameraCharacteristics.LENS_FACING);//獲取這個攝像頭的面向 //CameraCharacteristics.LENS_FACING_BACK 後攝像頭 //CameraCharacteristics.LENS_FACING_FRONT 前攝像頭 //CameraCharacteristics.LENS_FACING_EXTERNAL 外部攝像頭,好比OTG插入的攝像頭 if (facing == CameraCharacteristics.LENS_FACING_FRONT) { mCurrentCameraId = cameraId; } } } catch (CameraAccessException e) { e.printStackTrace(); } } /** * 初始化攝像頭狀態回調 */ private void initCameraCallback() { mStateCallback = new CameraDevice.StateCallback() { /** * 攝像頭打開時 * @param camera */ @Override public void onOpened(@NonNull CameraDevice camera) { Log.e(TAG, "相機開啓"); mCameraDevice = camera; try { SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture(); Size matchingSize = getMatchingSize(); surfaceTexture.setDefaultBufferSize(matchingSize.getWidth(),matchingSize.getHeight());//設置預覽的圖像尺寸 Surface surface = new Surface(surfaceTexture); // CaptureRequest能夠徹底自定義拍攝參數,可是須要配置的參數太多了,因此Camera2提供了一些快速配置的參數,以下: // TEMPLATE_PREVIEW :預覽 // TEMPLATE_RECORD:拍攝視頻 // TEMPLATE_STILL_CAPTURE:拍照 // TEMPLATE_VIDEO_SNAPSHOT:建立視視頻錄製時截屏的請求 // TEMPLATE_ZERO_SHUTTER_LAG:建立一個適用於零快門延遲的請求。在不影響預覽幀率的狀況下最大化圖像質量。 // TEMPLATE_MANUAL:建立一個基本捕獲請求,這種請求中全部的自動控制都是禁用的(自動曝光,自動白平衡、自動焦點)。 mCaptureRequest = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);//建立預覽請求 mCaptureRequest.addTarget(surface); //添加目標 mCaptureRequest.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);//自動對焦 /** * 建立獲取會話 * 這裏會有一個容易忘記的坑,那就是Arrays.asList(surface, mImageReader.getSurface())這個方法 * 這個方法須要你導入後面須要操做功能的全部surface,好比預覽/拍照若是你2個都要操做那就要導入2個 * 不然後續操做沒有添加的那個功能就報錯surface沒有準備好,這也是我爲何先初始化ImageReader的緣由,由於在這裏就能夠拿到ImageReader的surface了 */ mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), mSessionStateCallback, mChildHanler); } catch (CameraAccessException e) { e.printStackTrace(); } } /** *攝像頭斷開時 * @param camera */ @Override public void onDisconnected(@NonNull CameraDevice camera) { } /** * 出現異常狀況時 * @param camera * @param error */ @Override public void onError(@NonNull CameraDevice camera, int error) { } /** * 攝像頭關閉時 * @param camera */ @Override public void onClosed(@NonNull CameraDevice camera) { super.onClosed(camera); } }; } /** * 攝像頭獲取會話狀態回調 */ private void initCameraCaptureSessionStateCallback(){ mSessionStateCallback = new CameraCaptureSession.StateCallback() { //攝像頭完成配置,能夠處理Capture請求了。 @Override public void onConfigured(@NonNull CameraCaptureSession session) { try { mCameraCaptureSession = session; //注意這裏使用的是 setRepeatingRequest() 請求經過此捕獲會話無休止地重複捕獲圖像。用它來一直請求預覽圖像 mCameraCaptureSession.setRepeatingRequest(mCaptureRequest.build(), mSessionCaptureCallback, mChildHanler); // mCameraCaptureSession.stopRepeating();//中止重複 取消任何正在進行的重複捕獲集 // mCameraCaptureSession.abortCaptures();//終止獲取 儘量快地放棄當前掛起和正在進行的全部捕獲。請只在銷燬activity的時候調用它 } catch (CameraAccessException e) { e.printStackTrace(); } } //攝像頭配置失敗 @Override public void onConfigureFailed(@NonNull CameraCaptureSession session) { } }; } /** * 攝像頭獲取會話數據回調 */ private void initCameraCaptureSessionCaptureCallback(){ mSessionCaptureCallback = new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureStarted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, long timestamp, long frameNumber) { super.onCaptureStarted(session, request, timestamp, frameNumber); } @Override public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) { super.onCaptureProgressed(session, request, partialResult); } @Override public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { super.onCaptureCompleted(session, request, result); // Log.e(TAG, "onCaptureCompleted: 觸發接收數據"); // Size size = request.get(CaptureRequest.JPEG_THUMBNAIL_SIZE); } @Override public void onCaptureFailed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureFailure failure) { super.onCaptureFailed(session, request, failure); } @Override public void onCaptureSequenceCompleted(@NonNull CameraCaptureSession session, int sequenceId, long frameNumber) { super.onCaptureSequenceCompleted(session, sequenceId, frameNumber); } @Override public void onCaptureSequenceAborted(@NonNull CameraCaptureSession session, int sequenceId) { super.onCaptureSequenceAborted(session, sequenceId); } @Override public void onCaptureBufferLost(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull Surface target, long frameNumber) { super.onCaptureBufferLost(session, request, target, frameNumber); } }; } /** * 打開攝像頭 */ private void openCamera() { try { if (checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) { mCameraManager.openCamera(mCurrentCameraId, mStateCallback, mMainHandler); return; } Toast.makeText(this, "沒有受權", Toast.LENGTH_SHORT).show(); } catch (CameraAccessException e) { e.printStackTrace(); } } /** * 初始化圖片讀取器 */ private void initImageReader(){ //建立圖片讀取器,參數爲分辨率寬度和高度/圖片格式/須要緩存幾張圖片,我這裏寫的2意思是獲取2張照片 mImageReader = ImageReader.newInstance(1080, 1920, ImageFormat.JPEG, 2); mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() { @Override public void onImageAvailable(ImageReader reader) { // image.acquireLatestImage();//從ImageReader的隊列中獲取最新的image,刪除舊的 // image.acquireNextImage();//從ImageReader的隊列中獲取下一個圖像,若是返回null沒有新圖像可用 Image image = reader.acquireNextImage(); try { File path = new File(Camera2Activity.this.getExternalCacheDir().getPath()); if (!path.exists()){ Log.e(TAG, "onImageAvailable: 路徑不存在"); path.mkdirs(); }else { Log.e(TAG, "onImageAvailable: 路徑存在" ); } File file = new File(path,"demo.jpg"); FileOutputStream fileOutputStream = new FileOutputStream(file); // 這裏的image.getPlanes()[0]實際上是圖層的意思,由於個人圖片格式是JPEG只有一層因此是geiPlanes()[0],若是你是其餘格式(例如png)的圖片會有多個圖層,就能夠獲取指定圖層的圖像數據 ByteBuffer byteBuffer = image.getPlanes()[0].getBuffer(); byte[] bytes = new byte[byteBuffer.remaining()]; byteBuffer.get(bytes); fileOutputStream.write(bytes); fileOutputStream.flush(); fileOutputStream.close(); image.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } },mChildHanler); } private void takePicture(){ CaptureRequest.Builder captureRequestBuilder = null; try { captureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);//自動對焦 captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);//自動爆光 // // 獲取手機方向,若是你的app有提供橫屏和豎屏,那麼就須要下面的方法來控制照片爲豎立狀態 // int rotation = getWindowManager().getDefaultDisplay().getRotation(); // Log.e(TAG, "takePicture: 手機方向="+rotation); // Log.e(TAG, "takePicture: 照片方向="+ORIENTATIONS.get(rotation)); captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, 270);//個人項目不須要,直接寫死270度 將照片豎立 Surface surface = mImageReader.getSurface(); captureRequestBuilder.addTarget(surface); CaptureRequest request = captureRequestBuilder.build(); mCameraCaptureSession.capture(request,null,mChildHanler); //獲取拍照 } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode){ case 1: if (permissions.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ Toast.makeText(this, "受權成功", Toast.LENGTH_SHORT).show(); }else { Toast.makeText(this, "受權失敗", Toast.LENGTH_SHORT).show(); finish(); } break; default: } } }
上面的只是簡簡單單的demo,下面是實際使用的樣子,其實差很少,貼出來也是多一個參考
public class FaceCameraActivity extends BaseActivity implements View.OnClickListener { private TextureView mTextureView; private Button mBtnCamera; private ImageView mBack; private MaterialDialog mHandlerImageWaitDialog; private CameraManager mCameraManager; private CameraDevice mCameraDevice; private ImageReader mImageReader; private CaptureRequest.Builder mCaptureRequest; private CameraDevice.StateCallback mCameraDeviceStateCallback; private CameraCaptureSession.StateCallback mCameraCaptureSessionStateCallback; private CameraCaptureSession.CaptureCallback mCameraCaptureSessionCaptureCallback; private CameraCaptureSession mCameraCaptureSession; private String mCurrentCameraId; private Size mCurrentSelectSize; private Handler mChildHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initChildThread(); initCameraManager(); initSelectCamera(); initHandlerMatchingSize(); initImageReader(); initTextureViewListener(); initCameraDeviceStateCallbackListener(); initCameraCaptureSessionStateCallbackListener(); initCameraCaptureSessionCaptureCallbackListener(); } @Override public int getLayout() { return R.layout.activity_face_camera; } @Override public void initView() { mBack = findViewById(R.id.back); mTextureView = findViewById(R.id.texture_view); mBtnCamera = findViewById(R.id.btn_camera); mBack.setOnClickListener(this); mBtnCamera.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_camera: if (ButtonDelayUtil.isFastClick()){ handlerImageWaitDialog().show(); stopPreview(); takePicture(); } break; case R.id.back: finish(); break; default: break; } } private void initChildThread() { HandlerThread handlerThread = new HandlerThread("faceCamera"); handlerThread.start(); mChildHandler = new Handler(handlerThread.getLooper()); } /** * 初始化相機管理 */ private void initCameraManager() { mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); } /** * 初始化選擇攝像頭 */ private void initSelectCamera() { try { String[] cameraIdArray = mCameraManager.getCameraIdList(); for (String itemId : cameraIdArray) { CameraCharacteristics itemCharacteristics = mCameraManager.getCameraCharacteristics(itemId); Integer facing = itemCharacteristics.get(CameraCharacteristics.LENS_FACING); if (facing == CameraCharacteristics.LENS_FACING_FRONT) { mCurrentCameraId = itemId; break; } } } catch (CameraAccessException e) { e.printStackTrace(); } if (mCurrentCameraId == null) { finish(); Toast.makeText(this, "此設備不支持前攝像頭", Toast.LENGTH_SHORT).show(); } } /** * 初始化計算適合當前屏幕分辨率的拍照分辨率 * @return */ private void initHandlerMatchingSize() { try { CameraCharacteristics cameraCharacteristics = mCameraManager.getCameraCharacteristics(mCurrentCameraId); StreamConfigurationMap streamConfigurationMap = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); Size[] sizes = streamConfigurationMap.getOutputSizes(ImageFormat.JPEG); DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); int deviceWidth = displayMetrics.widthPixels; int deviceHeigh = displayMetrics.heightPixels; L.e("當前屏幕密度寬度="+deviceWidth+"高度="+deviceHeigh); for (int j = 1; j < 81; j++) { for (int i = 0; i < sizes.length; i++) { Size itemSize = sizes[i]; if (itemSize.getHeight() < (deviceWidth + j * 5) && itemSize.getHeight() > (deviceWidth - j * 5)) { if (mCurrentSelectSize != null) { //若是以前已經找到一個匹配的寬度 if (Math.abs(deviceHeigh-itemSize.getWidth()) < Math.abs(deviceHeigh - mCurrentSelectSize.getWidth())){ //求絕對值算出最接近設備高度的尺寸 mCurrentSelectSize = itemSize; continue; } }else { mCurrentSelectSize = itemSize; } } } if (mCurrentSelectSize != null) { //若是不等於null 說明已經找到了 跳出循環 break; } } } catch (CameraAccessException e) { e.printStackTrace(); } L.e("當前預覽寬度="+mCurrentSelectSize.getWidth()+"高度="+mCurrentSelectSize.getHeight()); } private void initImageReader() { L.e("初始化圖片ImageReader的寬="+mCurrentSelectSize.getWidth()+"高="+mCurrentSelectSize.getHeight()); mImageReader = ImageReader.newInstance(mCurrentSelectSize.getWidth() , mCurrentSelectSize.getHeight() , ImageFormat.JPEG , 2); mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() { @Override public void onImageAvailable(ImageReader reader) { FilePathSession.deleteFaceImageFile(); Image image = reader.acquireLatestImage(); ByteBuffer byteBuffer = image.getPlanes()[0].getBuffer(); byte[] bytes = new byte[byteBuffer.remaining()]; byteBuffer.get(bytes); try { FileOutputStream fileOutputStream = new FileOutputStream(FilePathSession.getFaceImagePath()); fileOutputStream.write(bytes); fileOutputStream.flush(); fileOutputStream.close(); image.close(); startPreview(); handlerImageWaitDialog().dismiss(); runOnUiThread(new Runnable() { @Override public void run() { Intent startFaceConfirm = new Intent(FaceCameraActivity.this, FaceConfirmActivity.class); startActivity(startFaceConfirm); FaceCameraActivity.this.finish(); } }); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }, mChildHandler); } private void initTextureViewListener() { mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { openCamera(); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { return false; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { } }); } private void initCameraDeviceStateCallbackListener() { mCameraDeviceStateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(@NonNull CameraDevice camera) { //相機開啓 mCameraDevice = camera; try { SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture(); surfaceTexture.setDefaultBufferSize(mCurrentSelectSize.getWidth(),mCurrentSelectSize.getHeight()); Surface surface = new Surface(surfaceTexture); mCaptureRequest = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); mCaptureRequest.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); mCaptureRequest.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);//自動爆光 mCaptureRequest.addTarget(surface); mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()) , mCameraCaptureSessionStateCallback , mChildHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onDisconnected(@NonNull CameraDevice camera) { } @Override public void onError(@NonNull CameraDevice camera, int error) { finish(); Toast.makeText(FaceCameraActivity.this, "相機打開失敗", Toast.LENGTH_SHORT).show(); L.e("CameraDevice.StateCallback onError : 相機異常 error code="+error); } }; } private void initCameraCaptureSessionStateCallbackListener() { mCameraCaptureSessionStateCallback = new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession session) { mCameraCaptureSession = session; startPreview(); } @Override public void onConfigureFailed(@NonNull CameraCaptureSession session) { finish(); Toast.makeText(FaceCameraActivity.this, "相機打開失敗", Toast.LENGTH_SHORT).show(); L.e("CameraCaptureSession.StateCallback onConfigureFailed : CameraCaptureSession會話通道建立失敗"); } }; } private void initCameraCaptureSessionCaptureCallbackListener() { mCameraCaptureSessionCaptureCallback = new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureStarted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, long timestamp, long frameNumber) { super.onCaptureStarted(session, request, timestamp, frameNumber); //獲取開始 } @Override public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) { super.onCaptureProgressed(session, request, partialResult); //獲取中 } @Override public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { super.onCaptureCompleted(session, request, result); //獲取結束 } @Override public void onCaptureFailed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureFailure failure) { super.onCaptureFailed(session, request, failure); //獲取失敗 Toast.makeText(FaceCameraActivity.this, "拍照失敗", Toast.LENGTH_SHORT).show(); L.e("失敗報告Reason="+failure.getReason()); } }; } @SuppressLint("MissingPermission") private void openCamera() { try { mCameraManager.openCamera(mCurrentCameraId, mCameraDeviceStateCallback, mChildHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } private MaterialDialog handlerImageWaitDialog(){ if (mHandlerImageWaitDialog == null){ mHandlerImageWaitDialog = new MaterialDialog.Builder(this) .content("正在處理圖像中...") .progress(true,-1) .cancelable(false) .build(); } return mHandlerImageWaitDialog; } /** * 開始預覽 */ private void startPreview(){ try { mCameraCaptureSession.setRepeatingRequest(mCaptureRequest.build(), mCameraCaptureSessionCaptureCallback, mChildHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } /** * 中止預覽 */ private void stopPreview(){ try { mCameraCaptureSession.stopRepeating(); } catch (CameraAccessException e) { e.printStackTrace(); } } /** * 拍照 */ private void takePicture(){ try { CaptureRequest.Builder takePictureRequest = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); takePictureRequest.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);//自動對焦 takePictureRequest.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);//自動爆光 int rotation = getWindowManager().getDefaultDisplay().getRotation(); int angle = getJpegOrientation(mCameraManager.getCameraCharacteristics(mCurrentCameraId), rotation); L.i("人臉拍照 照片角度="+angle); takePictureRequest.set(CaptureRequest.JPEG_ORIENTATION, angle); Surface surface = mImageReader.getSurface(); takePictureRequest.addTarget(surface); CaptureRequest request = takePictureRequest.build(); mCameraCaptureSession.capture(request, null, mChildHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } /** * 官方提供的JPEG圖片方向算法 * @param c * @param deviceOrientation * @return */ private int getJpegOrientation(CameraCharacteristics c, int deviceOrientation) { if (deviceOrientation == OrientationEventListener.ORIENTATION_UNKNOWN){ return 0; } int sensorOrientation = c.get(CameraCharacteristics.SENSOR_ORIENTATION);//獲取傳感器方向 // Round device orientation to a multiple of 90 deviceOrientation = (deviceOrientation + 45) / 90 * 90; // Reverse device orientation for front-facing cameras boolean facingFront = c.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT;//判斷攝像頭面向 if (facingFront) { deviceOrientation = -deviceOrientation; } // Calculate desired JPEG orientation relative to camera orientation to make // the image upright relative to the device orientation int jpegOrientation = (sensorOrientation + deviceOrientation + 360) % 360; return jpegOrientation; } @Override protected void onDestroy() { super.onDestroy(); if (mImageReader != null){ mImageReader.close(); mImageReader = null; } if (mCameraCaptureSession != null){ stopPreview(); try { mCameraCaptureSession.abortCaptures(); } catch (CameraAccessException e) { e.printStackTrace(); } mCameraCaptureSession.close(); mCameraCaptureSession = null; } if (mCameraDevice != null){ mCameraDevice.close(); mCameraDevice = null; } mCameraManager = null; if (mChildHandler != null){ mChildHandler.removeCallbacksAndMessages(null); mChildHandler = null; } } }