您好,登錄后才能下訂單哦!
這篇文章主要介紹Android Camera2怎么實(shí)現(xiàn)預(yù)覽功能,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
1. 概述
最近在做一些關(guān)于人臉識(shí)別的項(xiàng)目,需要用到 Android 相機(jī)的預(yù)覽功能。網(wǎng)上查閱相關(guān)資料后,發(fā)現(xiàn) Android 5.0 及以后的版本中,原有的 Camera API 已經(jīng)被 Camera2 API 所取代。
全新的 Camera2 在 Camera 的基礎(chǔ)上進(jìn)行了改造,大幅提升了 Android 系統(tǒng)的拍照功能。它通過(guò)以下幾個(gè)類與方法來(lái)實(shí)現(xiàn)相機(jī)預(yù)覽時(shí)的工作過(guò)程:
?CameraManager :攝像頭管理器,主要用于檢測(cè)系統(tǒng)攝像頭、打開(kāi)系統(tǒng)攝像頭等;
?CameraDevice : 用于描述系統(tǒng)攝像頭,可用于關(guān)閉相機(jī)、創(chuàng)建相機(jī)會(huì)話、發(fā)送拍照請(qǐng)求等;
?CameraCharacteristics :用于描述攝像頭所支持的各種特性;
?CameraCaptureSession :當(dāng)程序需要預(yù)覽、拍照時(shí),都需要先通過(guò) CameraCaptureSession 來(lái)實(shí)現(xiàn)。該會(huì)話通過(guò)調(diào)用方法 setRepeatingRequest() 實(shí)現(xiàn)預(yù)覽;
?CameraRequest :代表一次捕獲請(qǐng)求,用于描述捕獲圖片的各種參數(shù)設(shè)置;
?CameraRequest.Builder :負(fù)責(zé)生成 CameraRequest 對(duì)象。
2. 相機(jī)預(yù)覽
下面通過(guò)源碼來(lái)講解如何使用 Camera2 來(lái)實(shí)現(xiàn)相機(jī)的預(yù)覽功能。
2.1 相機(jī)權(quán)限設(shè)置
<uses-permission android:name="android.permission.CAMERA" />
2.2 App 布局
?activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#000" tools:context=".MainActivity"> </FrameLayout> ?fragment_camera.xml <?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=".CameraFragment"> <com.lightweh.camera2preview.AutoFitTextureView android:id="@+id/textureView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_centerHorizontal="true" /> </RelativeLayout>
2.3 相機(jī)自定義View
public class AutoFitTextureView extends TextureView { private int mRatioWidth = 0; private int mRatioHeight = 0; public AutoFitTextureView(Context context) { this(context, null); } public AutoFitTextureView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public AutoFitTextureView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public void setAspectRatio(int width, int height) { if (width < 0 || height < 0) { throw new IllegalArgumentException("Size cannot be negative."); } mRatioWidth = width; mRatioHeight = height; requestLayout(); } @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); } } } }
2.4 動(dòng)態(tài)申請(qǐng)相機(jī)權(quán)限
public class MainActivity extends AppCompatActivity { private static final int REQUEST_PERMISSION = 1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (hasPermission()) { if (null == savedInstanceState) { setFragment(); } } else { requestPermission(); } } @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { if (requestCode == REQUEST_PERMISSION) { if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { setFragment(); } else { requestPermission(); } } else { super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } // 權(quán)限判斷,當(dāng)系統(tǒng)版本大于23時(shí),才有必要判斷是否獲取權(quán)限 private boolean hasPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { return checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED; } else { return true; } } // 請(qǐng)求相機(jī)權(quán)限 private void requestPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) { Toast.makeText(MainActivity.this, "Camera permission are required for this demo", Toast.LENGTH_LONG).show(); } requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_PERMISSION); } } // 啟動(dòng)相機(jī)Fragment private void setFragment() { getSupportFragmentManager() .beginTransaction() .replace(R.id.container, CameraFragment.newInstance()) .commitNowAllowingStateLoss(); } }
2.5 開(kāi)啟相機(jī)預(yù)覽
首先,在onResume()中,我們需要開(kāi)啟一個(gè) HandlerThread,然后利用該線程的 Looper 對(duì)象構(gòu)建一個(gè) Handler 用于相機(jī)回調(diào)。
@Override public void onResume() { super.onResume(); startBackgroundThread(); // When the screen is turned off and turned back on, the SurfaceTexture is // already available, and "onSurfaceTextureAvailable" will not be called. In // that case, we can open a camera and start preview from here (otherwise, we // wait until the surface is ready in the SurfaceTextureListener). if (mTextureView.isAvailable()) { openCamera(mTextureView.getWidth(), mTextureView.getHeight()); } else { mTextureView.setSurfaceTextureListener(mSurfaceTextureListener); } } private void startBackgroundThread() { mBackgroundThread = new HandlerThread("CameraBackground"); mBackgroundThread.start(); mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); }
同時(shí),在 onPause() 中有對(duì)應(yīng)的 HandlerThread 關(guān)閉方法。
當(dāng)屏幕關(guān)閉后重新開(kāi)啟,SurfaceTexture 已經(jīng)就緒,此時(shí)不會(huì)觸發(fā) onSurfaceTextureAvailable 回調(diào)。因此,我們判斷 mTextureView 如果可用,則直接打開(kāi)相機(jī),否則等待 SurfaceTexture 回調(diào)就緒后再開(kāi)啟相機(jī)。
private void openCamera(int width, int height) { if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { return; } setUpCameraOutputs(width, height); configureTransform(width, height); Activity activity = getActivity(); CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE); try { if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { throw new RuntimeException("Time out waiting to lock camera opening."); } manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } catch (InterruptedException e) { throw new RuntimeException("Interrupted while trying to lock camera opening.", e); } }
開(kāi)啟相機(jī)時(shí),我們首先判斷是否具備相機(jī)權(quán)限,然后調(diào)用 setUpCameraOutputs 函數(shù)對(duì)相機(jī)參數(shù)進(jìn)行設(shè)置(包括指定攝像頭、相機(jī)預(yù)覽方向以及預(yù)覽尺寸的設(shè)定等),接下來(lái)調(diào)用 configureTransform 函數(shù)對(duì)預(yù)覽圖片的大小和方向進(jìn)行調(diào)整,最后獲取 CameraManager 對(duì)象開(kāi)啟相機(jī)。因?yàn)橄鄼C(jī)有可能會(huì)被其他進(jìn)程同時(shí)訪問(wèn),所以在開(kāi)啟相機(jī)時(shí)需要加鎖。
private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(@NonNull CameraDevice cameraDevice) { mCameraOpenCloseLock.release(); mCameraDevice = cameraDevice; createCameraPreviewSession(); } @Override public void onDisconnected(@NonNull CameraDevice cameraDevice) { mCameraOpenCloseLock.release(); cameraDevice.close(); mCameraDevice = null; } @Override public void onError(@NonNull CameraDevice cameraDevice, int error) { mCameraOpenCloseLock.release(); cameraDevice.close(); mCameraDevice = null; Activity activity = getActivity(); if (null != activity) { activity.finish(); } } };
相機(jī)開(kāi)啟時(shí)還會(huì)指定相機(jī)的狀態(tài)變化回調(diào)函數(shù) mStateCallback,如果相機(jī)成功開(kāi)啟,則開(kāi)始創(chuàng)建相機(jī)預(yù)覽會(huì)話。
private void createCameraPreviewSession() { try { // 獲取 texture 實(shí)例 SurfaceTexture texture = mTextureView.getSurfaceTexture(); assert texture != null; // 設(shè)置 TextureView 緩沖區(qū)大小 texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); // 獲取 Surface 顯示預(yù)覽數(shù)據(jù) Surface surface = new Surface(texture); // 構(gòu)建適合相機(jī)預(yù)覽的請(qǐng)求 mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); // 設(shè)置 surface 作為預(yù)覽數(shù)據(jù)的顯示界面 mPreviewRequestBuilder.addTarget(surface); // 創(chuàng)建相機(jī)捕獲會(huì)話用于預(yù)覽 mCameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) { // 如果相機(jī)關(guān)閉則返回 if (null == mCameraDevice) { return; } // 如果會(huì)話準(zhǔn)備好則開(kāi)啟預(yù)覽 mCaptureSession = cameraCaptureSession; try { // 自動(dòng)對(duì)焦 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); mPreviewRequest = mPreviewRequestBuilder.build(); // 設(shè)置反復(fù)捕獲數(shù)據(jù)的請(qǐng)求,預(yù)覽界面一直顯示畫面 mCaptureSession.setRepeatingRequest(mPreviewRequest, null, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed( @NonNull CameraCaptureSession cameraCaptureSession) { showToast("Failed"); } }, null ); } catch (CameraAccessException e) { e.printStackTrace(); } }
Android是一種基于Linux內(nèi)核的自由及開(kāi)放源代碼的操作系統(tǒng),主要使用于移動(dòng)設(shè)備,如智能手機(jī)和平板電腦,由美國(guó)Google公司和開(kāi)放手機(jī)聯(lián)盟領(lǐng)導(dǎo)及開(kāi)發(fā)。
以上是“Android Camera2怎么實(shí)現(xiàn)預(yù)覽功能”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。