Khung Android hỗ trợ nhiều máy ảnh và các tính năng máy ảnh có trên thiết bị, cho phép bạn chụp ảnh và quay video trong các ứng dụng của mình. Tài liệu này thảo luận một phương pháp đơn giản, nhanh chóng để chụp ảnh và quay video, đồng thời trình bày một phương pháp nâng cao để tạo trải nghiệm tuỳ chỉnh cho người dùng trên máy ảnh.
Lưu ý: Trang này mô tả lớp Camera
(không còn được dùng nữa). Bạn nên dùng thư viện Jetpack CameraX hoặc lớp camera2
(đối với các trường hợp sử dụng cụ thể). Cả CameraX và Camera2 đều hoạt động trên Android 5.0 (API cấp 21) trở lên.
Hãy tham khảo các thông tin có liên quan sau:
Những yếu tố nên cân nhắc
Trước khi cho phép ứng dụng dùng camera trên thiết bị Android, bạn nên cân nhắc một số câu hỏi về cách ứng dụng định dùng tính năng phần cứng này.
- Yêu cầu về máy ảnh – Việc sử dụng máy ảnh có quan trọng đến mức ứng dụng của bạn không muốn cài đặt ứng dụng đó trên thiết bị không có máy ảnh không? Nếu có, bạn nên khai báo yêu cầu về máy ảnh trong tệp kê khai.
- Hình ảnh nhanh hoặc Máy ảnh tuỳ chỉnh – Ứng dụng của bạn sẽ sử dụng máy ảnh như thế nào? Bạn chỉ muốn chụp nhanh ảnh hoặc đoạn video hay ứng dụng sẽ cung cấp một cách mới để sử dụng máy ảnh? Để chụp nhanh hoặc cắt một đoạn video, hãy cân nhắc Dùng các ứng dụng máy ảnh hiện có. Để phát triển một tính năng tuỳ chỉnh cho máy ảnh, hãy xem phần Tạo ứng dụng máy ảnh.
- Yêu cầu đối với dịch vụ trên nền trước – Khi nào ứng dụng của bạn tương tác với máy ảnh? Trên Android 9 (API cấp 28) trở lên, các ứng dụng chạy ở chế độ nền sẽ không thể truy cập vào máy ảnh. Do đó, bạn nên sử dụng camera khi ứng dụng đang chạy ở nền trước hoặc trong một dịch vụ trên nền trước.
- Bộ nhớ – Hình ảnh hoặc video mà ứng dụng của bạn tạo ra chỉ để hiển thị với ứng dụng của bạn hay được chia sẻ để các ứng dụng khác như Thư viện hoặc các ứng dụng mạng xã hội và ứng dụng khác có thể sử dụng chúng? Bạn có muốn hình ảnh và video hiển thị ngay cả khi ứng dụng của bạn đã bị gỡ cài đặt không? Hãy xem phần Lưu tệp nội dung nghe nhìn để xem cách triển khai các tuỳ chọn này.
Thông tin cơ bản
Khung Android hỗ trợ chụp ảnh và quay video thông qua API android.hardware.camera2
hoặc máy ảnh Intent
. Sau đây là các lớp có liên quan:
android.hardware.camera2
- Gói này là API chính để điều khiển các camera của thiết bị. Bạn có thể dùng công cụ này để chụp ảnh hoặc quay video khi tạo ứng dụng camera.
Camera
- Lớp này là API cũ, không dùng nữa để điều khiển máy ảnh của thiết bị.
SurfaceView
- Lớp này dùng để hiển thị bản xem trước máy ảnh trực tiếp cho người dùng.
MediaRecorder
- Lớp này dùng để quay video bằng camera.
Intent
- Bạn có thể dùng loại thao tác theo ý định
MediaStore.ACTION_IMAGE_CAPTURE
hoặcMediaStore.ACTION_VIDEO_CAPTURE
để chụp ảnh hoặc quay video mà không cần trực tiếp sử dụng đối tượngCamera
.
Nội dung khai báo trong tệp kê khai
Trước khi bắt đầu phát triển ứng dụng bằng API Camera, bạn nên đảm bảo tệp kê khai có các nội dung khai báo thích hợp để cho phép sử dụng phần cứng máy ảnh và các tính năng liên quan khác.
- Quyền truy cập vào máy ảnh – Ứng dụng của bạn phải yêu cầu quyền sử dụng máy ảnh của thiết bị.
<uses-permission android:name="android.permission.CAMERA" />
Lưu ý: Nếu bạn đang sử dụng máy ảnh bằng cách gọi một ứng dụng máy ảnh hiện có, thì ứng dụng của bạn không cần yêu cầu quyền này.
- Tính năng của máy ảnh – Ứng dụng của bạn cũng phải khai báo việc sử dụng các tính năng của máy ảnh,
ví dụ:
<uses-feature android:name="android.hardware.camera" />
Để biết danh sách các tính năng của máy ảnh, hãy xem tệp kê khai Tài liệu tham khảo về tính năng.
Việc thêm các tính năng máy ảnh vào tệp kê khai sẽ khiến Google Play ngăn cài đặt ứng dụng của bạn vào các thiết bị không có máy ảnh hoặc không hỗ trợ các tính năng máy ảnh mà bạn chỉ định. Để biết thêm thông tin về cách sử dụng cơ chế lọc dựa trên tính năng với Google Play, hãy xem bài viết Google Play và cơ chế lọc dựa trên tính năng.
Nếu ứng dụng của bạn có thể sử dụng một tính năng máy ảnh hoặc máy ảnh để hoạt động đúng cách nhưng không cần có, thì bạn nên chỉ định thông tin này trong tệp kê khai bằng cách thêm thuộc tính
android:required
và đặt thuộc tính đó thànhfalse
:<uses-feature android:name="android.hardware.camera" android:required="false" />
- Quyền truy cập vào bộ nhớ – Ứng dụng của bạn có thể lưu hình ảnh hoặc video vào bộ nhớ ngoài (Thẻ SD) của thiết bị nếu ứng dụng nhắm đến Android 10 (API cấp 29) trở xuống và chỉ định nội dung sau trong tệp kê khai.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- Quyền ghi âm – Để ghi âm bằng tính năng quay video, ứng dụng của bạn phải yêu cầu quyền ghi âm.
<uses-permission android:name="android.permission.RECORD_AUDIO" />
-
Quyền truy cập thông tin vị trí – Nếu ứng dụng của bạn gắn thẻ hình ảnh bằng thông tin vị trí GPS, bạn phải yêu cầu quyền
ACCESS_FINE_LOCATION
. Xin lưu ý rằng nếu ứng dụng của bạn nhắm đến Android 5.0 (API cấp 21) trở lên, thì bạn cũng cần khai báo rằng ứng dụng dùng GPS của thiết bị:<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> ... <!-- Needed only if your app targets Android 5.0 (API level 21) or higher. --> <uses-feature android:name="android.hardware.location.gps" />
Để biết thêm thông tin về cách nhận thông tin vị trí của người dùng, hãy xem phần Chiến lược vị trí.
Đang dùng các ứng dụng máy ảnh hiện có
Một cách nhanh chóng để cho phép chụp ảnh hoặc quay video trong ứng dụng của bạn mà không cần thêm nhiều mã là sử dụng Intent
để gọi một ứng dụng máy ảnh hiện có trên Android.
Thông tin chi tiết được mô tả trong các bài học đào tạo Chỉ cần chụp ảnh và Quay video.
Tạo ứng dụng máy ảnh
Một số nhà phát triển có thể yêu cầu giao diện người dùng máy ảnh được tuỳ chỉnh cho phù hợp với giao diện của ứng dụng hoặc cung cấp các tính năng đặc biệt. Việc tự viết mã chụp ảnh có thể mang lại trải nghiệm hấp dẫn hơn cho người dùng.
Lưu ý: Hướng dẫn sau đây dành cho API Camera
cũ và không còn dùng nữa. Đối với các ứng dụng máy ảnh mới hoặc nâng cao, bạn nên sử dụng API android.hardware.camera2
mới.
Sau đây là các bước chung để tạo giao diện máy ảnh tuỳ chỉnh cho ứng dụng:
- Phát hiện và truy cập máy ảnh – Tạo mã để kiểm tra xem có máy ảnh hay không và yêu cầu quyền truy cập.
- Create a Preview Class (Tạo lớp xem trước) – Tạo lớp xem trước cho máy ảnh, lớp này mở rộng
SurfaceView
và triển khai giao diệnSurfaceHolder
. Lớp này giúp bạn xem trước hình ảnh trực tiếp từ máy ảnh. - Build a Preview Layout (Tạo bố cục xem trước) – Sau khi bạn có lớp xem trước của máy ảnh, hãy tạo một bố cục khung hiển thị kết hợp bản xem trước và các chế độ điều khiển giao diện người dùng mà bạn muốn.
- Thiết lập trình nghe để chụp – Kết nối trình nghe cho các nút điều khiển giao diện để bắt đầu chụp ảnh hoặc quay video nhằm phản hồi thao tác của người dùng, chẳng hạn như nhấn một nút.
- Capture and Save Files (Ghi và lưu tệp) – Thiết lập mã để chụp ảnh hoặc quay video và lưu kết quả đầu ra.
- Thả Máy ảnh – Sau khi sử dụng máy ảnh, ứng dụng của bạn phải giải phóng máy ảnh đúng cách để các ứng dụng khác dùng.
Phần cứng máy ảnh là một tài nguyên dùng chung phải được quản lý cẩn thận để ứng dụng của bạn không xung đột với các ứng dụng khác cũng có thể muốn dùng. Các phần sau thảo luận cách phát hiện phần cứng máy ảnh, cách yêu cầu quyền truy cập vào máy ảnh, cách chụp ảnh hoặc quay video và cách giải phóng máy ảnh khi ứng dụng của bạn dùng xong.
Thận trọng: Hãy nhớ giải phóng đối tượng Camera
bằng cách gọi Camera.release()
khi ứng dụng của bạn sử dụng xong đối tượng đó! Nếu ứng dụng không giải phóng máy ảnh đúng cách, thì mọi nỗ lực sau đó để truy cập vào máy ảnh (bao gồm cả những nỗ lực của chính ứng dụng) sẽ không thành công và có thể khiến các ứng dụng của bạn hoặc các ứng dụng khác bị tắt.
Phát hiện phần cứng máy ảnh
Nếu ứng dụng của bạn không yêu cầu cụ thể máy ảnh bằng cách sử dụng nội dung khai báo tệp kê khai, bạn nên kiểm tra xem có máy ảnh trong thời gian chạy hay không. Để thực hiện việc kiểm tra này, hãy sử dụng phương thức PackageManager.hasSystemFeature()
, như minh hoạ trong mã ví dụ dưới đây:
Kotlin
/** Check if this device has a camera */ private fun checkCameraHardware(context: Context): Boolean { if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)) { // this device has a camera return true } else { // no camera on this device return false } }
Java
/** Check if this device has a camera */ private boolean checkCameraHardware(Context context) { if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){ // this device has a camera return true; } else { // no camera on this device return false; } }
Thiết bị Android có thể có nhiều máy ảnh, chẳng hạn như máy ảnh mặt sau để chụp ảnh và máy ảnh mặt trước để chụp ảnh. Android 2.3 (API cấp 9) trở lên cho phép bạn kiểm tra số lượng máy ảnh có trên thiết bị bằng phương thức Camera.getNumberOfCameras()
.
Đang truy cập vào camera
Nếu đã xác định rằng thiết bị mà ứng dụng của bạn đang chạy có máy ảnh, bạn phải yêu cầu truy cập vào thiết bị đó bằng cách lấy một thực thể của Camera
(trừ phi bạn đang sử dụng một ý định truy cập vào máy ảnh).
Để truy cập vào máy ảnh chính, hãy sử dụng phương thức Camera.open()
và nhớ phát hiện mọi trường hợp ngoại lệ như trong mã dưới đây:
Kotlin
/** A safe way to get an instance of the Camera object. */ fun getCameraInstance(): Camera? { return try { Camera.open() // attempt to get a Camera instance } catch (e: Exception) { // Camera is not available (in use or does not exist) null // returns null if camera is unavailable } }
Java
/** A safe way to get an instance of the Camera object. */ public static Camera getCameraInstance(){ Camera c = null; try { c = Camera.open(); // attempt to get a Camera instance } catch (Exception e){ // Camera is not available (in use or does not exist) } return c; // returns null if camera is unavailable }
Thận trọng: Luôn kiểm tra các trường hợp ngoại lệ khi sử dụng Camera.open()
. Nếu bạn không kiểm tra các trường hợp ngoại lệ nếu camera đang được sử dụng hoặc không tồn tại thì ứng dụng của bạn sẽ bị hệ thống tắt.
Trên thiết bị chạy Android 2.3 (API cấp 9) trở lên, bạn có thể truy cập vào các máy ảnh cụ thể bằng Camera.open(int)
. Mã ví dụ ở trên sẽ truy cập vào máy ảnh mặt sau đầu tiên trên thiết bị có nhiều máy ảnh.
Đang kiểm tra các tính năng của camera
Sau khi có quyền truy cập vào một máy ảnh, bạn có thể biết thêm thông tin về các chức năng của máy ảnh đó bằng cách sử dụng phương thức Camera.getParameters()
và kiểm tra đối tượng Camera.Parameters
được trả về để xem các tính năng được hỗ trợ. Khi sử dụng API cấp 9 trở lên, hãy sử dụng Camera.getCameraInfo()
để xác định xem máy ảnh nằm ở trước hay sau thiết bị và hướng của hình ảnh.
Tạo lớp xem trước
Để người dùng có thể chụp ảnh hoặc quay video hiệu quả, họ phải xem được những nội dung mà máy ảnh của thiết bị nhìn thấy. Lớp xem trước của máy ảnh là một SurfaceView
có thể hiển thị dữ liệu hình ảnh trực tiếp từ máy ảnh, nhờ đó, người dùng có thể lấy khung hình và chụp ảnh hoặc quay video.
Mã ví dụ sau đây minh hoạ cách tạo một lớp xem trước cơ bản của máy ảnh có thể được đưa vào bố cục View
. Lớp này triển khai SurfaceHolder.Callback
nhằm ghi lại các sự kiện gọi lại nhằm tạo và huỷ bỏ khung hiển thị cần thiết để chỉ định đầu vào cho bản xem trước của máy ảnh.
Kotlin
/** A basic Camera preview class */ class CameraPreview( context: Context, private val mCamera: Camera ) : SurfaceView(context), SurfaceHolder.Callback { private val mHolder: SurfaceHolder = holder.apply { // Install a SurfaceHolder.Callback so we get notified when the // underlying surface is created and destroyed. addCallback(this@CameraPreview) // deprecated setting, but required on Android versions prior to 3.0 setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS) } override fun surfaceCreated(holder: SurfaceHolder) { // The Surface has been created, now tell the camera where to draw the preview. mCamera.apply { try { setPreviewDisplay(holder) startPreview() } catch (e: IOException) { Log.d(TAG, "Error setting camera preview: ${e.message}") } } } override fun surfaceDestroyed(holder: SurfaceHolder) { // empty. Take care of releasing the Camera preview in your activity. } override fun surfaceChanged(holder: SurfaceHolder, format: Int, w: Int, h: Int) { // If your preview can change or rotate, take care of those events here. // Make sure to stop the preview before resizing or reformatting it. if (mHolder.surface == null) { // preview surface does not exist return } // stop preview before making changes try { mCamera.stopPreview() } catch (e: Exception) { // ignore: tried to stop a non-existent preview } // set preview size and make any resize, rotate or // reformatting changes here // start preview with new settings mCamera.apply { try { setPreviewDisplay(mHolder) startPreview() } catch (e: Exception) { Log.d(TAG, "Error starting camera preview: ${e.message}") } } } }
Java
/** A basic Camera preview class */ public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { private SurfaceHolder mHolder; private Camera mCamera; public CameraPreview(Context context, Camera camera) { super(context); mCamera = camera; // Install a SurfaceHolder.Callback so we get notified when the // underlying surface is created and destroyed. mHolder = getHolder(); mHolder.addCallback(this); // deprecated setting, but required on Android versions prior to 3.0 mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } public void surfaceCreated(SurfaceHolder holder) { // The Surface has been created, now tell the camera where to draw the preview. try { mCamera.setPreviewDisplay(holder); mCamera.startPreview(); } catch (IOException e) { Log.d(TAG, "Error setting camera preview: " + e.getMessage()); } } public void surfaceDestroyed(SurfaceHolder holder) { // empty. Take care of releasing the Camera preview in your activity. } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { // If your preview can change or rotate, take care of those events here. // Make sure to stop the preview before resizing or reformatting it. if (mHolder.getSurface() == null){ // preview surface does not exist return; } // stop preview before making changes try { mCamera.stopPreview(); } catch (Exception e){ // ignore: tried to stop a non-existent preview } // set preview size and make any resize, rotate or // reformatting changes here // start preview with new settings try { mCamera.setPreviewDisplay(mHolder); mCamera.startPreview(); } catch (Exception e){ Log.d(TAG, "Error starting camera preview: " + e.getMessage()); } } }
Nếu bạn muốn đặt một kích thước cụ thể cho bản xem trước của máy ảnh, hãy thiết lập trong phương thức surfaceChanged()
như đã nêu trong các nhận xét ở trên. Khi đặt kích thước xem trước, bạn phải sử dụng các giá trị từ getSupportedPreviewSizes()
.
Không đặt các giá trị tuỳ ý trong phương thức setPreviewSize()
.
Lưu ý: Với sự ra mắt của tính năng
Nhiều cửa sổ trong Android 7.0 (API cấp 24) trở lên, bạn không còn giả định rằng tỷ lệ khung hình của bản xem trước giống với hoạt động của mình ngay cả sau khi gọi setDisplayOrientation()
.
Tuỳ thuộc vào kích thước cửa sổ và tỷ lệ khung hình, có thể bạn sẽ phải đưa bản xem trước của máy ảnh rộng vào một bố cục hướng dọc hoặc ngược lại khi sử dụng bố cục hòm thư.
Đặt bản xem trước trong bố cục
Bạn phải đặt một lớp xem trước của máy ảnh, chẳng hạn như ví dụ hiển thị trong phần trước, vào bố cục của một hoạt động cùng với các chế độ điều khiển giao diện người dùng khác để chụp ảnh hoặc quay video. Phần này cho bạn biết cách tạo bố cục và hoạt động cơ bản cho bản xem trước.
Mã bố cục sau đây cung cấp một khung hiển thị rất cơ bản có thể dùng để hiển thị bản xem trước của máy ảnh. Trong ví dụ này, phần tử FrameLayout
là vùng chứa cho lớp xem trước máy ảnh. Loại bố cục này dùng để có thể phủ các chế độ điều khiển hoặc thông tin hình ảnh bổ sung lên hình ảnh xem trước của máy ảnh trực tiếp.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent" > <FrameLayout android:id="@+id/camera_preview" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1" /> <Button android:id="@+id/button_capture" android:text="Capture" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" /> </LinearLayout>
Trên hầu hết thiết bị, hướng mặc định của bản xem trước máy ảnh là hướng ngang. Bố cục trong ví dụ này chỉ định bố cục ngang (ngang) và mã dưới đây sẽ sửa hướng của ứng dụng thành ngang. Để đơn giản hoá việc kết xuất bản xem trước của máy ảnh, bạn nên thay đổi hướng hoạt động xem trước của ứng dụng thành hướng ngang bằng cách thêm nội dung sau vào tệp kê khai.
<activity android:name=".CameraActivity" android:label="@string/app_name" android:screenOrientation="landscape"> <!-- configure this activity to use landscape orientation --> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
Lưu ý: Bản xem trước của máy ảnh không cần phải ở chế độ ngang.
Kể từ Android 2.2 (API cấp 8), bạn có thể sử dụng phương thức setDisplayOrientation()
để đặt chế độ xoay của hình ảnh xem trước. Để thay đổi hướng xem trước khi người dùng định hướng lại điện thoại, trong phương thức surfaceChanged()
của lớp xem trước, trước tiên hãy dừng bản xem trước bằng cách Camera.stopPreview()
thay đổi hướng rồi bắt đầu lại bản xem trước bằng Camera.startPreview()
.
Trong hoạt động cho chế độ xem máy ảnh, hãy thêm lớp xem trước vào phần tử FrameLayout
hiển thị trong ví dụ ở trên. Hoạt động của bạn với máy ảnh cũng phải đảm bảo rằng máy ảnh sẽ nhả ra khi bị tạm dừng hoặc tắt. Ví dụ sau cho thấy cách sửa đổi hoạt động của máy ảnh để đính kèm lớp xem trước như trong phần Tạo lớp xem trước.
Kotlin
class CameraActivity : Activity() { private var mCamera: Camera? = null private var mPreview: CameraPreview? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Create an instance of Camera mCamera = getCameraInstance() mPreview = mCamera?.let { // Create our Preview view CameraPreview(this, it) } // Set the Preview view as the content of our activity. mPreview?.also { val preview: FrameLayout = findViewById(R.id.camera_preview) preview.addView(it) } } }
Java
public class CameraActivity extends Activity { private Camera mCamera; private CameraPreview mPreview; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // Create an instance of Camera mCamera = getCameraInstance(); // Create our Preview view and set it as the content of our activity. mPreview = new CameraPreview(this, mCamera); FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview); preview.addView(mPreview); } }
Lưu ý: Phương thức getCameraInstance()
trong ví dụ trên tham chiếu đến phương thức ví dụ minh hoạ trong phần Truy cập vào máy ảnh.
Đang chụp ảnh
Sau khi tạo lớp xem trước và bố cục thành phần hiển thị để hiển thị lớp đó, bạn đã sẵn sàng bắt đầu chụp ảnh bằng ứng dụng của mình. Trong mã xử lý ứng dụng, bạn phải thiết lập trình nghe cho các chế độ điều khiển giao diện người dùng để phản hồi hành động của người dùng bằng cách chụp ảnh.
Để truy xuất ảnh, hãy sử dụng phương thức Camera.takePicture()
. Phương thức này sử dụng 3 tham số nhận dữ liệu từ máy ảnh.
Để nhận dữ liệu ở định dạng JPEG, bạn phải triển khai giao diện Camera.PictureCallback
để nhận dữ liệu hình ảnh và ghi vào tệp. Mã sau đây cho thấy cách triển khai cơ bản của giao diện Camera.PictureCallback
để lưu hình ảnh nhận được từ máy ảnh.
Kotlin
private val mPicture = Camera.PictureCallback { data, _ -> val pictureFile: File = getOutputMediaFile(MEDIA_TYPE_IMAGE) ?: run { Log.d(TAG, ("Error creating media file, check storage permissions")) return@PictureCallback } try { val fos = FileOutputStream(pictureFile) fos.write(data) fos.close() } catch (e: FileNotFoundException) { Log.d(TAG, "File not found: ${e.message}") } catch (e: IOException) { Log.d(TAG, "Error accessing file: ${e.message}") } }
Java
private PictureCallback mPicture = new PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE); if (pictureFile == null){ Log.d(TAG, "Error creating media file, check storage permissions"); return; } try { FileOutputStream fos = new FileOutputStream(pictureFile); fos.write(data); fos.close(); } catch (FileNotFoundException e) { Log.d(TAG, "File not found: " + e.getMessage()); } catch (IOException e) { Log.d(TAG, "Error accessing file: " + e.getMessage()); } } };
Kích hoạt tính năng chụp ảnh bằng cách gọi phương thức Camera.takePicture()
. Mã ví dụ sau đây cho thấy cách gọi phương thức này từ một nút View.OnClickListener
.
Kotlin
val captureButton: Button = findViewById(R.id.button_capture) captureButton.setOnClickListener { // get an image from the camera mCamera?.takePicture(null, null, picture) }
Java
// Add a listener to the Capture button Button captureButton = (Button) findViewById(R.id.button_capture); captureButton.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { // get an image from the camera mCamera.takePicture(null, null, picture); } } );
Lưu ý: Thành phần mPicture
trong ví dụ sau đề cập đến mã ví dụ ở trên.
Thận trọng: Hãy nhớ giải phóng đối tượng Camera
bằng cách gọi Camera.release()
khi ứng dụng của bạn sử dụng xong đối tượng đó! Để biết thông tin về cách nhả camera, hãy xem phần Thả camera.
Quay video
Việc quay video bằng khung Android yêu cầu bạn phải quản lý cẩn thận đối tượng Camera
và phối hợp với lớp MediaRecorder
. Khi quay video bằng Camera
, bạn phải quản lý các lệnh gọi Camera.lock()
và Camera.unlock()
để cho phép MediaRecorder
truy cập vào phần cứng máy ảnh, ngoài các lệnh gọi Camera.open()
và Camera.release()
.
Lưu ý: Kể từ Android 4.0 (API cấp 14), các lệnh gọi Camera.lock()
và Camera.unlock()
sẽ tự động được quản lý cho bạn.
Không giống như chụp ảnh bằng máy ảnh của thiết bị, việc quay video yêu cầu một thứ tự lệnh gọi rất cụ thể. Bạn phải tuân theo một thứ tự thực thi cụ thể để chuẩn bị và quay video thành công bằng ứng dụng của mình, như trình bày chi tiết dưới đây.
- Mở máy ảnh – Sử dụng
Camera.open()
để lấy thực thể của đối tượng máy ảnh. - Xem trước kết nối – Chuẩn bị bản xem trước hình ảnh máy ảnh trực tiếp bằng cách kết nối
SurfaceView
với máy ảnh bằngCamera.setPreviewDisplay()
. - Bắt đầu xem trước – Gọi
Camera.startPreview()
để bắt đầu hiển thị hình ảnh máy ảnh trực tiếp. - Bắt đầu quay video – Bạn phải hoàn thành các bước sau đây theo thứ tự để quay video thành công:
- Mở khoá máy ảnh – Mở khoá máy ảnh để
MediaRecorder
sử dụng bằng cách gọiCamera.unlock()
. - Định cấu hình MediaRecorder – Gọi trong các phương thức
MediaRecorder
sau theo thứ tự này. Để biết thêm thông tin, hãy xem tài liệu tham khảo vềMediaRecorder
.setCamera()
– Thiết lập máy ảnh dùng để quay video, sử dụng phiên bảnCamera
hiện tại của ứng dụng.setAudioSource()
– Thiết lập nguồn âm thanh, sử dụngMediaRecorder.AudioSource.CAMCORDER
.setVideoSource()
– Thiết lập nguồn video, sử dụngMediaRecorder.VideoSource.CAMERA
.- Đặt định dạng đầu ra video và mã hoá. Đối với Android 2.2 (API cấp 8) trở lên, hãy sử dụng phương thức
MediaRecorder.setProfile
và tải một phiên bản hồ sơ bằngCamcorderProfile.get()
. Đối với các phiên bản Android trước phiên bản 2.2, bạn phải đặt định dạng đầu ra video và các tham số mã hoá:setOutputFormat()
– Đặt định dạng đầu ra, chỉ định chế độ cài đặt mặc định hoặcMediaRecorder.OutputFormat.MPEG_4
.setAudioEncoder()
– Đặt loại mã hoá âm thanh, chỉ định chế độ cài đặt mặc định hoặcMediaRecorder.AudioEncoder.AMR_NB
.setVideoEncoder()
– Đặt loại mã hoá video, chỉ định chế độ cài đặt mặc định hoặcMediaRecorder.VideoEncoder.MPEG_4_SP
.
setOutputFile()
– Thiết lập tệp đầu ra, sử dụnggetOutputMediaFile(MEDIA_TYPE_VIDEO).toString()
từ phương thức ví dụ trong mục Lưu tệp nội dung nghe nhìn.setPreviewDisplay()
– Chỉ định phần tử bố cục xem trướcSurfaceView
cho ứng dụng. Sử dụng cùng một đối tượng mà bạn đã chỉ định cho tính năng Xem trước kết nối.
Thận trọng: Bạn phải gọi các phương thức cấu hình
MediaRecorder
này theo thứ tự này, nếu không ứng dụng sẽ gặp lỗi và không ghi được. - Chuẩn bị MediaRecorder – Chuẩn bị
MediaRecorder
với các chế độ cài đặt cấu hình được cung cấp bằng cách gọiMediaRecorder.prepare()
. - Khởi động MediaRecorder – Bắt đầu quay video bằng cách gọi
MediaRecorder.start()
.
- Mở khoá máy ảnh – Mở khoá máy ảnh để
- Dừng quay video – Gọi các phương thức sau theo thứ tự để quay video thành công:
- Dừng MediaRecorder – Dừng quay video bằng cách gọi
MediaRecorder.stop()
. - Reset MediaRecorder – Nếu muốn, hãy xoá các chế độ cài đặt cấu hình khỏi trình ghi bằng cách gọi
MediaRecorder.reset()
. - Phát hành MediaRecorder – Phát hành
MediaRecorder
bằng cách gọiMediaRecorder.release()
. - Khoá camera – Khoá camera để các phiên
MediaRecorder
trong tương lai có thể sử dụng camera bằng cách gọiCamera.lock()
. Kể từ Android 4.0 (API cấp 14), bạn không bắt buộc phải thực hiện lệnh gọi này trừ phi lệnh gọiMediaRecorder.prepare()
không thành công.
- Dừng MediaRecorder – Dừng quay video bằng cách gọi
- Dừng bản xem trước – Khi hoạt động của bạn đã sử dụng xong máy ảnh, hãy dừng bản xem trước bằng
Camera.stopPreview()
. - Phát hành máy ảnh – Giải phóng máy ảnh để các ứng dụng khác có thể dùng máy ảnh bằng cách gọi
Camera.release()
.
Lưu ý: Bạn có thể sử dụng MediaRecorder
mà không cần tạo bản xem trước của máy ảnh trước. Hãy bỏ qua vài bước đầu tiên của quy trình này. Tuy nhiên, vì người dùng thường muốn xem trước trước khi bắt đầu ghi, nên quá trình này không được thảo luận ở đây.
Lưu ý: Nếu ứng dụng của bạn thường dùng để quay video, hãy đặt setRecordingHint(boolean)
thành true
trước khi bắt đầu xem trước. Chế độ cài đặt này có thể giúp giảm thời gian cần thiết để bắt đầu quay video.
Định cấu hình MediaRecorder
Khi sử dụng lớp MediaRecorder
để quay video, bạn phải thực hiện các bước định cấu hình theo thứ tự cụ thể, sau đó gọi phương thức MediaRecorder.prepare()
để kiểm tra và triển khai cấu hình. Mã ví dụ sau đây minh hoạ cách định cấu hình và chuẩn bị đúng cách lớp MediaRecorder
để quay video.
Kotlin
private fun prepareVideoRecorder(): Boolean { mediaRecorder = MediaRecorder() mCamera?.let { camera -> // Step 1: Unlock and set camera to MediaRecorder camera?.unlock() mediaRecorder?.run { setCamera(camera) // Step 2: Set sources setAudioSource(MediaRecorder.AudioSource.CAMCORDER) setVideoSource(MediaRecorder.VideoSource.CAMERA) // Step 3: Set a CamcorderProfile (requires API Level 8 or higher) setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)) // Step 4: Set output file setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()) // Step 5: Set the preview output setPreviewDisplay(mPreview?.holder?.surface) setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT) setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT) // Step 6: Prepare configured MediaRecorder return try { prepare() true } catch (e: IllegalStateException) { Log.d(TAG, "IllegalStateException preparing MediaRecorder: ${e.message}") releaseMediaRecorder() false } catch (e: IOException) { Log.d(TAG, "IOException preparing MediaRecorder: ${e.message}") releaseMediaRecorder() false } } } return false }
Java
private boolean prepareVideoRecorder(){ mCamera = getCameraInstance(); mediaRecorder = new MediaRecorder(); // Step 1: Unlock and set camera to MediaRecorder mCamera.unlock(); mediaRecorder.setCamera(mCamera); // Step 2: Set sources mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER); mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); // Step 3: Set a CamcorderProfile (requires API Level 8 or higher) mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)); // Step 4: Set output file mediaRecorder.setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()); // Step 5: Set the preview output mediaRecorder.setPreviewDisplay(mPreview.getHolder().getSurface()); // Step 6: Prepare configured MediaRecorder try { mediaRecorder.prepare(); } catch (IllegalStateException e) { Log.d(TAG, "IllegalStateException preparing MediaRecorder: " + e.getMessage()); releaseMediaRecorder(); return false; } catch (IOException e) { Log.d(TAG, "IOException preparing MediaRecorder: " + e.getMessage()); releaseMediaRecorder(); return false; } return true; }
Trước Android 2.2 (API cấp 8), bạn phải trực tiếp đặt các tham số định dạng đầu ra và định dạng mã hoá, thay vì sử dụng CamcorderProfile
. Phương pháp này được
minh hoạ trong đoạn mã sau:
Kotlin
// Step 3: Set output format and encoding (for versions prior to API Level 8) mediaRecorder?.apply { setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT) setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT) }
Java
// Step 3: Set output format and encoding (for versions prior to API Level 8) mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
Các thông số quay video sau đây cho MediaRecorder
được cung cấp
chế độ cài đặt mặc định. Tuy nhiên, bạn có thể điều chỉnh các chế độ cài đặt này cho ứng dụng của mình:
setVideoEncodingBitRate()
setVideoSize()
setVideoFrameRate()
setAudioEncodingBitRate()
setAudioChannels()
setAudioSamplingRate()
Khởi động và dừng MediaRecorder
Khi bắt đầu và dừng quay video bằng lớp MediaRecorder
, bạn phải tuân theo một thứ tự cụ thể, như được liệt kê dưới đây.
- Mở khoá máy ảnh bằng
Camera.unlock()
- Định cấu hình
MediaRecorder
như trong mã ví dụ ở trên - Bắt đầu ghi bằng
MediaRecorder.start()
- Quay video
- Dừng ghi bằng
MediaRecorder.stop()
- Thả trình ghi nội dung nghe nhìn bằng
MediaRecorder.release()
- Khoá camera bằng
Camera.lock()
Mã ví dụ sau đây minh hoạ cách kết nối một nút để bắt đầu và dừng quay video đúng cách bằng máy ảnh và lớp MediaRecorder
.
Lưu ý: Khi hoàn tất quay video, đừng thả camera ra, nếu không bản xem trước sẽ bị dừng lại.
Kotlin
var isRecording = false val captureButton: Button = findViewById(R.id.button_capture) captureButton.setOnClickListener { if (isRecording) { // stop recording and release camera mediaRecorder?.stop() // stop the recording releaseMediaRecorder() // release the MediaRecorder object mCamera?.lock() // take camera access back from MediaRecorder // inform the user that recording has stopped setCaptureButtonText("Capture") isRecording = false } else { // initialize video camera if (prepareVideoRecorder()) { // Camera is available and unlocked, MediaRecorder is prepared, // now you can start recording mediaRecorder?.start() // inform the user that recording has started setCaptureButtonText("Stop") isRecording = true } else { // prepare didn't work, release the camera releaseMediaRecorder() // inform user } } }
Java
private boolean isRecording = false; // Add a listener to the Capture button Button captureButton = (Button) findViewById(id.button_capture); captureButton.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { if (isRecording) { // stop recording and release camera mediaRecorder.stop(); // stop the recording releaseMediaRecorder(); // release the MediaRecorder object mCamera.lock(); // take camera access back from MediaRecorder // inform the user that recording has stopped setCaptureButtonText("Capture"); isRecording = false; } else { // initialize video camera if (prepareVideoRecorder()) { // Camera is available and unlocked, MediaRecorder is prepared, // now you can start recording mediaRecorder.start(); // inform the user that recording has started setCaptureButtonText("Stop"); isRecording = true; } else { // prepare didn't work, release the camera releaseMediaRecorder(); // inform user } } } } );
Lưu ý: Trong ví dụ trên, phương thức prepareVideoRecorder()
đề cập đến mã ví dụ xuất hiện trong phần Định cấu hình MediaRecorder. Phương thức này đảm nhiệm việc khoá máy ảnh, định cấu hình và chuẩn bị thực thể MediaRecorder
.
Cách nhả camera
Camera là một tài nguyên do các ứng dụng trên thiết bị chia sẻ. Ứng dụng của bạn có thể tận dụng máy ảnh sau khi nhận được thực thể của Camera
và bạn phải đặc biệt cẩn thận khi giải phóng đối tượng máy ảnh khi ứng dụng ngừng sử dụng và ngay khi ứng dụng bị tạm dừng (Activity.onPause()
). Nếu ứng dụng không giải phóng máy ảnh đúng cách, thì mọi nỗ lực tiếp theo để truy cập vào máy ảnh (bao gồm cả các lần do ứng dụng của bạn thực hiện) sẽ không thành công và có thể khiến các ứng dụng của bạn hoặc các ứng dụng khác bị tắt.
Để giải phóng một thực thể của đối tượng Camera
, hãy sử dụng phương thức Camera.release()
như minh hoạ trong mã ví dụ dưới đây.
Kotlin
class CameraActivity : Activity() { private var mCamera: Camera? private var preview: SurfaceView? private var mediaRecorder: MediaRecorder? override fun onPause() { super.onPause() releaseMediaRecorder() // if you are using MediaRecorder, release it first releaseCamera() // release the camera immediately on pause event } private fun releaseMediaRecorder() { mediaRecorder?.reset() // clear recorder configuration mediaRecorder?.release() // release the recorder object mediaRecorder = null mCamera?.lock() // lock camera for later use } private fun releaseCamera() { mCamera?.release() // release the camera for other applications mCamera = null } }
Java
public class CameraActivity extends Activity { private Camera mCamera; private SurfaceView preview; private MediaRecorder mediaRecorder; ... @Override protected void onPause() { super.onPause(); releaseMediaRecorder(); // if you are using MediaRecorder, release it first releaseCamera(); // release the camera immediately on pause event } private void releaseMediaRecorder(){ if (mediaRecorder != null) { mediaRecorder.reset(); // clear recorder configuration mediaRecorder.release(); // release the recorder object mediaRecorder = null; mCamera.lock(); // lock camera for later use } } private void releaseCamera(){ if (mCamera != null){ mCamera.release(); // release the camera for other applications mCamera = null; } } }
Thận trọng: Nếu ứng dụng của bạn không nhả máy ảnh đúng cách, thì mọi lần tiếp theo để truy cập vào máy ảnh (bao gồm cả những lần do ứng dụng của bạn thực hiện) sẽ không thành công và có thể khiến các ứng dụng của bạn hoặc các ứng dụng khác bị tắt.
Đang lưu tệp nội dung nghe nhìn
Các tệp nội dung nghe nhìn do người dùng tạo, chẳng hạn như hình ảnh và video, phải được lưu vào thư mục bộ nhớ ngoài (Thẻ SD) của thiết bị để tiết kiệm dung lượng hệ thống và cho phép người dùng truy cập vào các tệp này mà không cần thiết bị của họ. Có nhiều vị trí thư mục có thể dùng để lưu tệp nội dung nghe nhìn trên thiết bị, nhưng chỉ có hai vị trí tiêu chuẩn mà bạn nên cân nhắc với tư cách là nhà phát triển:
Environment.getExternalStoragePublicDirectory
(Environment.DIRECTORY_PICTURES
) – Phương thức này trả về vị trí chuẩn, vị trí chia sẻ và vị trí đề xuất để lưu hình ảnh và video. Thư mục này được chia sẻ (công khai), vì vậy, các ứng dụng khác có thể dễ dàng khám phá, đọc, thay đổi và xoá tệp đã lưu trong vị trí này. Nếu người dùng gỡ cài đặt ứng dụng của bạn, các tệp đa phương tiện đã lưu vào vị trí này sẽ không bị xoá. Để tránh can thiệp vào ảnh và video hiện có của người dùng, bạn nên tạo một thư mục con cho các tệp nội dung nghe nhìn của ứng dụng trong thư mục này, như minh hoạ trong mã mẫu dưới đây. Phương thức này có trong Android 2.2 (API cấp 8). Đối với các lệnh gọi tương đương trong các phiên bản API cũ, hãy xem phần Lưu tệp được chia sẻ.Context.getExternalFilesDir
(Environment.DIRECTORY_PICTURES
) – Phương thức này trả về một vị trí chuẩn để lưu hình ảnh và video liên kết với ứng dụng của bạn. Nếu bạn đã gỡ cài đặt ứng dụng, thì mọi tệp được lưu ở vị trí này sẽ bị xoá. Tính năng bảo mật không được thực thi cho các tệp ở vị trí này và các ứng dụng khác có thể đọc, thay đổi và xoá những tệp đó.
Mã ví dụ sau đây minh hoạ cách tạo vị trí File
hoặc Uri
cho một tệp nội dung nghe nhìn có thể dùng khi gọi máy ảnh của thiết bị bằng Intent
hoặc trong quá trình Tạo ứng dụng máy ảnh.
Kotlin
val MEDIA_TYPE_IMAGE = 1 val MEDIA_TYPE_VIDEO = 2 /** Create a file Uri for saving an image or video */ private fun getOutputMediaFileUri(type: Int): Uri { return Uri.fromFile(getOutputMediaFile(type)) } /** Create a File for saving an image or video */ private fun getOutputMediaFile(type: Int): File? { // To be safe, you should check that the SDCard is mounted // using Environment.getExternalStorageState() before doing this. val mediaStorageDir = File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyCameraApp" ) // This location works best if you want the created images to be shared // between applications and persist after your app has been uninstalled. // Create the storage directory if it does not exist mediaStorageDir.apply { if (!exists()) { if (!mkdirs()) { Log.d("MyCameraApp", "failed to create directory") return null } } } // Create a media file name val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date()) return when (type) { MEDIA_TYPE_IMAGE -> { File("${mediaStorageDir.path}${File.separator}IMG_$timeStamp.jpg") } MEDIA_TYPE_VIDEO -> { File("${mediaStorageDir.path}${File.separator}VID_$timeStamp.mp4") } else -> null } }
Java
public static final int MEDIA_TYPE_IMAGE = 1; public static final int MEDIA_TYPE_VIDEO = 2; /** Create a file Uri for saving an image or video */ private static Uri getOutputMediaFileUri(int type){ return Uri.fromFile(getOutputMediaFile(type)); } /** Create a File for saving an image or video */ private static File getOutputMediaFile(int type){ // To be safe, you should check that the SDCard is mounted // using Environment.getExternalStorageState() before doing this. File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES), "MyCameraApp"); // This location works best if you want the created images to be shared // between applications and persist after your app has been uninstalled. // Create the storage directory if it does not exist if (! mediaStorageDir.exists()){ if (! mediaStorageDir.mkdirs()){ Log.d("MyCameraApp", "failed to create directory"); return null; } } // Create a media file name String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); File mediaFile; if (type == MEDIA_TYPE_IMAGE){ mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_"+ timeStamp + ".jpg"); } else if(type == MEDIA_TYPE_VIDEO) { mediaFile = new File(mediaStorageDir.getPath() + File.separator + "VID_"+ timeStamp + ".mp4"); } else { return null; } return mediaFile; }
Lưu ý: Environment.getExternalStoragePublicDirectory()
có trong Android 2.2 (API cấp 8) trở lên. Nếu bạn đang nhắm đến các thiết bị chạy phiên bản Android cũ, hãy sử dụng Environment.getExternalStorageDirectory()
. Để biết thêm thông tin, hãy xem phần Lưu tệp được chia sẻ.
Để làm cho URI hỗ trợ hồ sơ công việc, trước tiên hãy
chuyển đổi URI tệp thành URI nội dung. Sau đó, hãy thêm URI nội dung vào EXTRA_OUTPUT
của Intent
.
Để biết thêm thông tin về cách lưu tệp trên thiết bị Android, hãy xem bài viết Bộ nhớ dữ liệu.
Tính năng của camera
Android hỗ trợ nhiều tính năng máy ảnh mà bạn có thể điều khiển bằng ứng dụng máy ảnh, chẳng hạn như định dạng hình ảnh, chế độ flash, chế độ cài đặt lấy nét và nhiều tính năng khác. Phần này liệt kê các tính năng phổ biến của máy ảnh và thảo luận ngắn gọn về cách sử dụng các tính năng đó. Bạn có thể truy cập và thiết lập hầu hết các tính năng của camera bằng đối tượng thông qua Camera.Parameters
. Tuy nhiên, có một số tính năng quan trọng không chỉ yêu cầu các chế độ cài đặt đơn giản trong Camera.Parameters
. Những tính năng này được đề cập trong các phần sau:
Để biết thông tin chung về cách sử dụng các tính năng được kiểm soát thông qua Camera.Parameters
, hãy xem phần Sử dụng các tính năng của máy ảnh. Để biết thêm thông tin chi tiết về cách sử dụng các tính năng được điều khiển thông qua đối tượng tham số máy ảnh, hãy truy cập các đường liên kết trong danh sách tính năng bên dưới để xem tài liệu tham khảo API.
Tính năng | Cấp độ API: | Nội dung mô tả |
---|---|---|
Phát hiện khuôn mặt | 14 | Xác định khuôn mặt người trong ảnh và sử dụng các khuôn mặt đó để lấy nét, đo sáng và cân bằng trắng |
Khu vực đo | 14 | Chỉ định một hoặc nhiều vùng trong hình ảnh để tính toán mức cân bằng trắng |
Lĩnh vực trọng tâm | 14 | Đặt một hoặc nhiều vùng trong hình ảnh để dùng làm tiêu điểm |
White Balance Lock |
14 | Dừng hoặc bắt đầu tự động điều chỉnh mức cân bằng trắng |
Exposure Lock |
14 | Dừng hoặc bắt đầu điều chỉnh độ phơi sáng tự động |
Video Snapshot |
14 | Chụp ảnh trong khi quay video (lấy khung hình) |
Video tua nhanh thời gian | 11 | Quay khung hình với độ trễ đã đặt để quay video ở chế độ tua nhanh thời gian |
Multiple Cameras |
9 | Hỗ trợ nhiều máy ảnh trên một thiết bị, bao gồm máy ảnh mặt trước và máy ảnh mặt sau |
Focus Distance |
9 | Báo cáo khoảng cách giữa máy ảnh và các đối tượng dường như được lấy nét |
Zoom |
8 | Đặt độ phóng to hình ảnh |
Exposure
Compensation |
8 | Tăng hoặc giảm mức phơi sáng với ánh sáng |
GPS Data |
5 | Bao gồm hoặc bỏ qua dữ liệu vị trí địa lý với hình ảnh |
White Balance |
5 | Đặt chế độ cân bằng trắng. Chế độ này sẽ ảnh hưởng đến các giá trị màu trong hình ảnh đã chụp |
Focus Mode |
5 | Thiết lập cách máy ảnh lấy nét vào một đối tượng như tự động, cố định, macro hoặc vô cực |
Scene Mode |
5 | Áp dụng một chế độ đặt trước cho các loại cảnh chụp ảnh cụ thể như cảnh ban đêm, bãi biển, cảnh tuyết hoặc ánh nến |
JPEG Quality |
5 | Đặt mức nén cho hình ảnh JPEG. Mức này giúp tăng hoặc giảm kích thước và chất lượng của tệp đầu ra hình ảnh |
Flash Mode |
5 | Bật, tắt đèn flash hoặc sử dụng chế độ cài đặt tự động |
Color Effects |
5 | Áp dụng hiệu ứng màu cho hình ảnh đã chụp, chẳng hạn như trắng đen, tông màu nâu đỏ hoặc âm bản. |
Anti-Banding |
5 | Giảm hiệu ứng dải chuyển màu do nén ảnh JPEG |
Picture Format |
1 | Chỉ định định dạng tệp cho hình ảnh |
Picture Size |
1 | Chỉ định kích thước pixel của hình ảnh đã lưu |
Lưu ý: Các tính năng này không được hỗ trợ trên một số thiết bị do sự khác biệt về phần cứng và cách triển khai phần mềm. Để biết thông tin về cách kiểm tra phạm vi cung cấp các tính năng trên thiết bị mà ứng dụng của bạn đang chạy, hãy xem phần Kiểm tra phạm vi cung cấp của tính năng.
Đang kiểm tra phạm vi cung cấp của các tính năng
Điều đầu tiên cần hiểu là khi bắt đầu sử dụng các tính năng của máy ảnh trên thiết bị Android là không phải thiết bị nào cũng hỗ trợ các tính năng của máy ảnh. Ngoài ra, các thiết bị hỗ trợ một tính năng cụ thể có thể hỗ trợ chúng ở nhiều cấp độ hoặc với nhiều tuỳ chọn. Do đó, khi bạn phát triển một ứng dụng máy ảnh, một phần trong quá trình ra quyết định là quyết định những tính năng của máy ảnh mà bạn muốn hỗ trợ và ở cấp độ nào. Sau khi đưa ra quyết định đó, bạn nên lên kế hoạch đưa mã vào ứng dụng máy ảnh để kiểm tra xem phần cứng của thiết bị có hỗ trợ các tính năng đó hay không, nếu không có tính năng nào thì sẽ không thành công.
Bạn có thể kiểm tra phạm vi cung cấp các tính năng của máy ảnh bằng cách lấy một bản sao của đối tượng tham số của máy ảnh và kiểm tra các phương thức có liên quan. Mã mẫu sau đây cho bạn biết cách lấy đối tượng Camera.Parameters
và kiểm tra xem máy ảnh có hỗ trợ tính năng tự động lấy nét hay không:
Kotlin
val params: Camera.Parameters? = camera?.parameters val focusModes: List<String>? = params?.supportedFocusModes if (focusModes?.contains(Camera.Parameters.FOCUS_MODE_AUTO) == true) { // Autofocus mode is supported }
Java
// get Camera parameters Camera.Parameters params = camera.getParameters(); List<String> focusModes = params.getSupportedFocusModes(); if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) { // Autofocus mode is supported }
Bạn có thể sử dụng kỹ thuật nêu trên cho hầu hết các tính năng của máy ảnh. Đối tượng Camera.Parameters
cung cấp phương thức getSupported...()
, is...Supported()
hoặc getMax...()
để xác định xem (và mức độ) một tính năng được hỗ trợ.
Nếu ứng dụng của bạn đòi hỏi một số tính năng máy ảnh để hoạt động bình thường, thì bạn có thể yêu cầu các tính năng đó bằng cách bổ sung vào tệp kê khai ứng dụng. Khi bạn khai báo việc sử dụng các tính năng cụ thể của máy ảnh, chẳng hạn như đèn flash và tự động lấy nét, Google Play sẽ hạn chế việc cài đặt ứng dụng của bạn trên các thiết bị không hỗ trợ các tính năng này. Để biết danh sách các tính năng của máy ảnh mà bạn có thể khai báo trong tệp kê khai ứng dụng, hãy xem Tài liệu tham khảo về tính năng của tệp kê khai.
Đang sử dụng các tính năng của máy ảnh
Hầu hết các tính năng của máy ảnh đều được kích hoạt và điều khiển bằng một đối tượng Camera.Parameters
. Bạn lấy đối tượng này bằng cách trước tiên lấy một thực thể của đối tượng Camera
, gọi phương thức getParameters()
, thay đổi đối tượng tham số được trả về, sau đó đặt lại đối tượng này vào đối tượng camera, như được minh hoạ trong mã ví dụ sau:
Kotlin
val params: Camera.Parameters? = camera?.parameters params?.focusMode = Camera.Parameters.FOCUS_MODE_AUTO camera?.parameters = params
Java
// get Camera parameters Camera.Parameters params = camera.getParameters(); // set the focus mode params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); // set Camera parameters camera.setParameters(params);
Kỹ thuật này có hiệu lực với hầu hết các tính năng của máy ảnh và bạn có thể thay đổi hầu hết các tham số bất cứ lúc nào sau khi đã có được thực thể của đối tượng Camera
. Các thay đổi đối với tham số thường sẽ hiển thị ngay cho người dùng trong bản xem trước máy ảnh của ứng dụng.
Về phần mềm, các thay đổi về tham số có thể mất một vài khung hình mới thực sự có hiệu lực khi phần cứng máy ảnh xử lý các hướng dẫn mới, sau đó gửi dữ liệu hình ảnh cập nhật.
Lưu ý quan trọng: Bạn không thể tuỳ ý thay đổi một số tính năng của máy ảnh. Cụ thể, việc thay đổi kích thước hoặc hướng của bản xem trước máy ảnh yêu cầu trước tiên bạn phải dừng bản xem trước, thay đổi kích thước bản xem trước rồi bắt đầu lại bản xem trước. Kể từ Android 4.0 (API cấp 14), bạn có thể thay đổi hướng xem trước mà không cần khởi động lại bản xem trước.
Các tính năng khác của máy ảnh yêu cầu thêm mã để triển khai, bao gồm:
- Phạm vi đo sáng và vùng lấy nét
- Phát hiện khuôn mặt
- Video tua nhanh thời gian
Bạn sẽ thấy thông tin tóm tắt ngắn gọn về cách triển khai các tính năng này trong những phần sau.
Phạm vi đo sáng và vùng lấy nét
Trong một số trường hợp chụp ảnh, tính năng lấy nét tự động và đo sáng có thể không tạo ra kết quả mong muốn. Kể từ Android 4.0 (API cấp 14), ứng dụng máy ảnh có thể cung cấp thêm các chế độ điều khiển để cho phép ứng dụng hoặc người dùng chỉ định các vùng trong ảnh cần dùng để xác định chế độ cài đặt lấy nét hoặc mức độ sáng, đồng thời truyền các giá trị này đến phần cứng máy ảnh để dùng trong việc chụp ảnh hoặc quay video.
Các vùng để đo sáng và lấy nét hoạt động rất giống với các tính năng khác của máy ảnh, vì bạn có thể kiểm soát các vùng này thông qua các phương thức trong đối tượng Camera.Parameters
. Mã sau đây minh hoạ việc đặt hai khu vực đo sáng cho một thực thể của Camera
:
Kotlin
// Create an instance of Camera camera = getCameraInstance() // set Camera parameters val params: Camera.Parameters? = camera?.parameters params?.apply { if (maxNumMeteringAreas > 0) { // check that metering areas are supported meteringAreas = ArrayList<Camera.Area>().apply { val areaRect1 = Rect(-100, -100, 100, 100) // specify an area in center of image add(Camera.Area(areaRect1, 600)) // set weight to 60% val areaRect2 = Rect(800, -1000, 1000, -800) // specify an area in upper right of image add(Camera.Area(areaRect2, 400)) // set weight to 40% } } camera?.parameters = this }
Java
// Create an instance of Camera camera = getCameraInstance(); // set Camera parameters Camera.Parameters params = camera.getParameters(); if (params.getMaxNumMeteringAreas() > 0){ // check that metering areas are supported List<Camera.Area> meteringAreas = new ArrayList<Camera.Area>(); Rect areaRect1 = new Rect(-100, -100, 100, 100); // specify an area in center of image meteringAreas.add(new Camera.Area(areaRect1, 600)); // set weight to 60% Rect areaRect2 = new Rect(800, -1000, 1000, -800); // specify an area in upper right of image meteringAreas.add(new Camera.Area(areaRect2, 400)); // set weight to 40% params.setMeteringAreas(meteringAreas); } camera.setParameters(params);
Đối tượng Camera.Area
chứa 2 tham số dữ liệu: Một đối tượng Rect
để chỉ định một vùng trong trường nhìn của máy ảnh và một giá trị trọng số, cho máy ảnh biết mức độ quan trọng của vùng này trong các phép tính đo sáng hoặc lấy nét.
Trường Rect
trong đối tượng Camera.Area
mô tả một hình chữ nhật được ánh xạ trên lưới đơn vị 2000 x 2000. Các toạ độ -1000, -1000 đại diện cho góc trên, góc bên trái của hình ảnh máy ảnh và toạ độ 1000, 1000 đại diện cho góc dưới cùng bên phải của hình ảnh máy ảnh, như minh hoạ trong hình minh hoạ dưới đây.
Các giới hạn của hệ toạ độ này luôn tương ứng với cạnh ngoài của hình ảnh hiển thị trong bản xem trước của máy ảnh và không thu nhỏ hoặc mở rộng theo mức thu phóng. Tương tự, việc xoay bản xem trước hình ảnh bằng Camera.setDisplayOrientation()
không ánh xạ lại hệ toạ độ.
Phát hiện khuôn mặt
Đối với ảnh có người, khuôn mặt thường là phần quan trọng nhất của bức ảnh và nên được dùng để xác định cả tiêu điểm và mức cân bằng trắng khi chụp ảnh. Khung Android 4.0 (API cấp 14) cung cấp các API để nhận dạng khuôn mặt và tính toán chế độ cài đặt hình ảnh bằng công nghệ nhận dạng khuôn mặt.
Lưu ý: Khi tính năng phát hiện khuôn mặt đang chạy, setWhiteBalance(String)
, setFocusAreas(List<Camera.Area>)
và setMeteringAreas(List<Camera.Area>)
sẽ không có hiệu lực.
Để sử dụng tính năng phát hiện khuôn mặt trong ứng dụng máy ảnh, bạn cần thực hiện một số bước chung sau đây:
- Kiểm tra để đảm bảo thiết bị hỗ trợ tính năng phát hiện khuôn mặt
- Tạo trình nghe phát hiện khuôn mặt
- Thêm trình nghe phát hiện khuôn mặt vào đối tượng của camera
- Bắt đầu phát hiện khuôn mặt sau khi xem trước (và sau mỗi lần khởi động lại bản xem trước)
Tính năng phát hiện khuôn mặt không được hỗ trợ trên một số thiết bị. Bạn có thể kiểm tra để đảm bảo tính năng này được hỗ trợ bằng cách gọi getMaxNumDetectedFaces()
. Ví dụ về quy trình kiểm tra này được minh hoạ trong phương thức mẫu startFaceDetection()
dưới đây.
Để nhận thông báo và phản hồi khi phát hiện khuôn mặt, ứng dụng camera phải thiết lập trình nghe cho các sự kiện phát hiện khuôn mặt. Để thực hiện việc này, bạn phải tạo một lớp trình nghe
triển khai giao diện Camera.FaceDetectionListener
như minh hoạ trong
mã ví dụ bên dưới.
Kotlin
internal class MyFaceDetectionListener : Camera.FaceDetectionListener { override fun onFaceDetection(faces: Array<Camera.Face>, camera: Camera) { if (faces.isNotEmpty()) { Log.d("FaceDetection", ("face detected: ${faces.size}" + " Face 1 Location X: ${faces[0].rect.centerX()}" + "Y: ${faces[0].rect.centerY()}")) } } }
Java
class MyFaceDetectionListener implements Camera.FaceDetectionListener { @Override public void onFaceDetection(Face[] faces, Camera camera) { if (faces.length > 0){ Log.d("FaceDetection", "face detected: "+ faces.length + " Face 1 Location X: " + faces[0].rect.centerX() + "Y: " + faces[0].rect.centerY() ); } } }
Sau khi tạo lớp này, bạn sẽ đặt lớp này vào đối tượng Camera
của ứng dụng, như minh hoạ trong mã ví dụ dưới đây:
Kotlin
camera?.setFaceDetectionListener(MyFaceDetectionListener())
Java
camera.setFaceDetectionListener(new MyFaceDetectionListener());
Ứng dụng của bạn phải khởi động chức năng phát hiện khuôn mặt mỗi khi bạn bắt đầu (hoặc khởi động lại) bản xem trước của máy ảnh. Tạo một phương thức để bắt đầu tính năng phát hiện khuôn mặt sao cho bạn có thể gọi phương thức này khi cần, như trong mã ví dụ dưới đây.
Kotlin
fun startFaceDetection() { // Try starting Face Detection val params = mCamera?.parameters // start face detection only *after* preview has started params?.apply { if (maxNumDetectedFaces > 0) { // camera supports face detection, so can start it: mCamera?.startFaceDetection() } } }
Java
public void startFaceDetection(){ // Try starting Face Detection Camera.Parameters params = mCamera.getParameters(); // start face detection only *after* preview has started if (params.getMaxNumDetectedFaces() > 0){ // camera supports face detection, so can start it: mCamera.startFaceDetection(); } }
Bạn phải bật tính năng phát hiện khuôn mặt mỗi khi khởi động (hoặc khởi động lại) bản xem trước của camera. Nếu bạn sử dụng lớp xem trước trong phần Tạo lớp xem trước, hãy thêm phương thức startFaceDetection()
vào cả hai phương thức surfaceCreated()
và surfaceChanged()
trong lớp xem trước, như minh hoạ trong mã mẫu dưới đây.
Kotlin
override fun surfaceCreated(holder: SurfaceHolder) { try { mCamera.setPreviewDisplay(holder) mCamera.startPreview() startFaceDetection() // start face detection feature } catch (e: IOException) { Log.d(TAG, "Error setting camera preview: ${e.message}") } } override fun surfaceChanged(holder: SurfaceHolder, format: Int, w: Int, h: Int) { if (holder.surface == null) { // preview surface does not exist Log.d(TAG, "holder.getSurface() == null") return } try { mCamera.stopPreview() } catch (e: Exception) { // ignore: tried to stop a non-existent preview Log.d(TAG, "Error stopping camera preview: ${e.message}") } try { mCamera.setPreviewDisplay(holder) mCamera.startPreview() startFaceDetection() // re-start face detection feature } catch (e: Exception) { // ignore: tried to stop a non-existent preview Log.d(TAG, "Error starting camera preview: ${e.message}") } }
Java
public void surfaceCreated(SurfaceHolder holder) { try { mCamera.setPreviewDisplay(holder); mCamera.startPreview(); startFaceDetection(); // start face detection feature } catch (IOException e) { Log.d(TAG, "Error setting camera preview: " + e.getMessage()); } } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { if (holder.getSurface() == null){ // preview surface does not exist Log.d(TAG, "holder.getSurface() == null"); return; } try { mCamera.stopPreview(); } catch (Exception e){ // ignore: tried to stop a non-existent preview Log.d(TAG, "Error stopping camera preview: " + e.getMessage()); } try { mCamera.setPreviewDisplay(holder); mCamera.startPreview(); startFaceDetection(); // re-start face detection feature } catch (Exception e){ // ignore: tried to stop a non-existent preview Log.d(TAG, "Error starting camera preview: " + e.getMessage()); } }
Lưu ý: Hãy nhớ gọi phương thức này sau khi gọi startPreview()
. Đừng cố bắt đầu tính năng phát hiện khuôn mặt trong phương thức onCreate()
của hoạt động chính của ứng dụng camera, vì tại thời điểm này, ứng dụng chưa có bản xem trước.
Video tua nhanh thời gian
Tính năng video tua nhanh thời gian cho phép người dùng tạo các đoạn video kết hợp các bức ảnh được chụp cách nhau vài giây hoặc vài phút. Tính năng này sử dụng MediaRecorder
để ghi lại hình ảnh trong trình tự tua nhanh thời gian.
Để quay video ở chế độ tua nhanh thời gian bằng MediaRecorder
, bạn phải định cấu hình đối tượng máy quay như thể bạn đang quay video thông thường, đặt số khung hình đã quay/giây ở mức thấp và sử dụng một trong các chế độ cài đặt chất lượng tua nhanh thời gian, như trong mã ví dụ dưới đây.
Kotlin
mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_TIME_LAPSE_HIGH)) mediaRecorder.setCaptureRate(0.1) // capture a frame every 10 seconds
Java
// Step 3: Set a CamcorderProfile (requires API Level 8 or higher) mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_TIME_LAPSE_HIGH)); ... // Step 5.5: Set the video capture rate to a low number mediaRecorder.setCaptureRate(0.1); // capture a frame every 10 seconds
Các chế độ cài đặt này phải được thực hiện trong một quy trình cấu hình lớn hơn cho MediaRecorder
. Để biết ví dụ về mã cấu hình đầy đủ, hãy xem Định cấu hình MediaRecorder. Sau khi định cấu hình xong, bạn sẽ bắt đầu quay video như thể bạn đang quay một đoạn video thông thường. Để biết thêm thông tin về cách định cấu hình và chạy MediaRecorder
, hãy xem bài viết Quay video.
Các mẫu Camera2Video và HdrViewfinder minh hoạ rõ hơn cách sử dụng các API được trình bày trên trang này.
Các trường về máy ảnh cần có quyền
Ứng dụng chạy Android 10 (API cấp 29) trở lên phải có quyền CAMERA
để truy cập vào giá trị của các trường sau đây mà phương thức getCameraCharacteristics()
trả về:
LENS_POSE_ROTATION
LENS_POSE_TRANSLATION
LENS_INTRINSIC_CALIBRATION
LENS_RADIAL_DISTORTION
LENS_POSE_REFERENCE
LENS_DISTORTION
LENS_INFO_HYPERFOCAL_DISTANCE
LENS_INFO_MINIMUM_FOCUS_DISTANCE
SENSOR_REFERENCE_ILLUMINANT1
SENSOR_REFERENCE_ILLUMINANT2
SENSOR_CALIBRATION_TRANSFORM1
SENSOR_CALIBRATION_TRANSFORM2
SENSOR_COLOR_TRANSFORM1
SENSOR_COLOR_TRANSFORM2
SENSOR_FORWARD_MATRIX1
SENSOR_FORWARD_MATRIX2
Các đoạn mã mẫu khác
Để tải các ứng dụng mẫu xuống, hãy xem bài viết Mẫu Camera2Basic và Ứng dụng mẫu CameraX chính thức.