Bản xem trước của camera

Lưu ý: Trang này đề cập đến gói Camera2. Trừ phi ứng dụng của bạn yêu cầu các tính năng cụ thể ở cấp độ thấp từ Camera2, bạn nên sử dụng CameraX. Cả CameraX và Camera2 đều hỗ trợ Android 5.0 (API cấp 21) trở lên.

Bản xem trước của máy ảnh và máy ảnh không phải lúc nào cũng theo cùng một hướng trên Android thiết bị.

Camera nằm ở một vị trí cố định trên một thiết bị, bất kể thiết bị đó là điện thoại, máy tính bảng hoặc máy tính. Khi hướng của thiết bị thay đổi, hướng máy ảnh thay đổi.

Do đó, ứng dụng máy ảnh thường giả định mối quan hệ cố định giữa hướng của thiết bị và tỷ lệ khung hình của bản xem trước của máy ảnh. Khi một điện thoại theo hướng dọc, bản xem trước của máy ảnh được giả định là cao hơn chiều rộng. Khi điện thoại (và máy ảnh) được xoay ngang, bản xem trước của máy ảnh dự kiến sẽ rộng hơn chiều cao.

Tuy nhiên, những giả định này đang bị thách thức bởi các kiểu dáng mới, chẳng hạn như có thể gập lại thiết bị và các chế độ hiển thị chẳng hạn như nhiều cửa sổnhiều màn hình. Thiết bị có thể gập lại thay đổi kích thước màn hình và tỷ lệ khung hình mà không cần thay đổi hướng. Chế độ nhiều cửa sổ ràng buộc các ứng dụng máy ảnh trong một phần của màn hình, điều chỉnh tỷ lệ bản xem trước của máy ảnh bất kể hướng của thiết bị. Chế độ nhiều màn hình cho phép sử dụng màn hình phụ mà có thể không có cùng hướng với màn hình chính.

Hướng máy ảnh

Chiến lược phát hành đĩa đơn Định nghĩa về khả năng tương thích với Android chỉ định rằng cảm biến hình ảnh của máy ảnh "PHẢI được định hướng để kích thước của máy ảnh phù hợp với chiều dài của màn hình. Tức là, khi thiết bị được giữ theo hướng ngang, máy ảnh PHẢI chụp ảnh trong hướng ngang. Điều này áp dụng bất kể chế độ cài đặt tự nhiên của thiết bị hướng; áp dụng cho các thiết bị chính theo chiều ngang cũng như thiết bị chính dọc".

Tính năng sắp xếp máy ảnh theo màn hình giúp tối đa hoá khu vực hiển thị của máy ảnh trong ứng dụng máy ảnh. Ngoài ra, cảm biến hình ảnh thường xuất dữ liệu theo tỷ lệ khung hình ngang, 4:3 là phổ biến nhất.

Cảm biến của điện thoại và máy ảnh cả theo hướng dọc.
Hình 1. Mối quan hệ thông thường giữa cảm biến của điện thoại và máy ảnh hướng.

Hướng tự nhiên của cảm biến máy ảnh là hướng ngang. Trong hình 1, cảm biến của máy ảnh mặt trước (máy ảnh hướng theo cùng hướng với màn hình) xoay 270 độ so với điện thoại để tuân thủ Định nghĩa về khả năng tương thích với Android.

Để hiển thị chế độ xoay cảm biến cho các ứng dụng, camera2 bao gồm một SENSOR_ORIENTATION hằng số. Đối với hầu hết điện thoại và máy tính bảng, thiết bị báo cáo hướng cảm biến 270 độ đối với máy ảnh mặt trước và 90 độ (góc nhìn từ mặt sau của thiết bị) cho máy ảnh mặt sau, được căn chỉnh cạnh dài của với cạnh dài của thiết bị. Camera của máy tính xách tay thường báo cáo hướng cảm biến 0 hoặc 180 độ.

Do các cảm biến hình ảnh của camera xuất dữ liệu (vùng đệm hình ảnh) trong hướng tự nhiên của cảm biến (ngang), vùng đệm hình ảnh phải được xoay số độ mà SENSOR_ORIENTATION chỉ định cho bản xem trước của máy ảnh hiển thị thẳng đứng theo hướng tự nhiên của thiết bị. Đối với máy ảnh mặt trước, xoay ngược chiều kim đồng hồ; đối với máy ảnh mặt sau, theo chiều kim đồng hồ.

Ví dụ: đối với máy ảnh mặt trước trong hình 1, vùng đệm hình ảnh do cảm biến của camera tạo ra trông giống như sau:

Cảm biến của máy ảnh được xoay theo hướng ngang kèm hình ảnh
            lệch sang một bên, trên cùng bên trái.

Hình ảnh phải được xoay 270 độ ngược chiều kim đồng hồ để hình ảnh xem trước hướng khớp với hướng của thiết bị:

Cảm biến của máy ảnh theo hướng dọc với hình ảnh thẳng đứng.

Máy ảnh mặt sau sẽ tạo ra vùng đệm hình ảnh có cùng hướng như vùng đệm ở trên, nhưng SENSOR_ORIENTATION là 90 độ. Do đó, vùng đệm xoay 90 độ theo chiều kim đồng hồ.

Xoay thiết bị

Độ xoay thiết bị là số độ xoay thiết bị so với vị trí tự nhiên hướng. Ví dụ: điện thoại theo hướng ngang có một thiết bị xoay 90 hoặc 270 độ, tuỳ thuộc vào hướng quay.

Vùng đệm hình ảnh của cảm biến của máy ảnh phải được xoay cùng một số độ với xoay thiết bị (ngoài số độ hướng cảm biến) cho bản xem trước của máy ảnh hiển thị theo chiều dọc.

Tính toán hướng

Hướng thích hợp của bản xem trước của máy ảnh sẽ xem xét cảm biến hướng và hướng xoay thiết bị.

Độ xoay tổng thể của vùng đệm hình ảnh cảm biến có thể được tính toán bằng cách sử dụng công thức sau:

rotation = (sensorOrientationDegrees - deviceOrientationDegrees * sign + 360) % 360

trong đó sign1 đối với máy ảnh mặt trước, -1 đối với máy ảnh mặt sau.

Đối với máy ảnh mặt trước, vùng đệm hình ảnh được xoay ngược chiều kim đồng hồ (từ hướng tự nhiên của cảm biến). Đối với camera mặt sau, cảm biến vùng đệm hình ảnh được xoay theo chiều kim đồng hồ.

Biểu thức deviceOrientationDegrees * sign + 360 chuyển đổi việc xoay thiết bị từ ngược chiều kim đồng hồ sang chiều kim đồng hồ đối với máy ảnh mặt sau (ví dụ: chuyển đổi 270 độ ngược chiều kim đồng hồ thành 90 độ theo chiều kim đồng hồ). Mô-đun thao tác sẽ chia tỷ lệ kết quả xuống dưới 360 độ (ví dụ: chia tỷ lệ 540 độ xoay thành 180).

Các API khác nhau báo cáo việc xoay thiết bị theo cách khác nhau:

  • Display#getRotation() cung cấp hướng xoay ngược chiều kim đồng hồ của thiết bị (từ điểm của người dùng lượt xem). Giá trị này cũng tương tự như công thức trên.
  • OrientationEventListener#onOrientationChanged() trả về độ xoay theo chiều kim đồng hồ của thiết bị (từ góc nhìn của người dùng). Phủ định giá trị đó để sử dụng trong công thức trên.

Máy ảnh mặt trước

Bản xem trước của máy ảnh và cảm biến cả theo hướng ngang, cảm biến
            ở phía bên phải.
Hình 2. Bản xem trước của máy ảnh và cảm biến khi điện thoại xoay 90 độ thành theo hướng ngang.

Dưới đây là vùng đệm hình ảnh do cảm biến của máy ảnh tạo ra trong hình 2:

Cảm biến của máy ảnh theo hướng ngang với hình ảnh thẳng đứng.

Vùng đệm phải xoay 270 độ ngược chiều kim đồng hồ để điều chỉnh cho cảm biến hướng (xem Hướng máy ảnh ở trên):

Cảm biến của máy ảnh được xoay theo hướng dọc với hình ảnh lệch sang một bên,
            trên cùng bên phải.

Sau đó, vùng đệm được xoay thêm 90 độ ngược chiều kim đồng hồ để tính đến việc xoay thiết bị, dẫn đến hướng chính xác của bản xem trước của máy ảnh trong hình 2:

Cảm biến của máy ảnh được xoay theo hướng ngang kèm hình ảnh
            thẳng đứng.

Sau đây là camera đã xoay sang phải theo hướng ngang:

Bản xem trước của máy ảnh và cảm biến cả theo hướng ngang, nhưng
            cảm biến bị lộn ngược.
Hình 3. Bản xem trước của máy ảnh và cảm biến khi điện thoại xoay 270 độ (hoặc -90 độ) theo hướng ngang.

Dưới đây là vùng đệm hình ảnh:

Cảm biến của máy ảnh được xoay theo hướng ngang với hình ảnh lộn ngược
            xuống.

Vùng đệm phải được xoay 270 độ ngược chiều kim đồng hồ để điều chỉnh cho cảm biến hướng:

Cảm biến của máy ảnh được xếp hạng theo hướng dọc với hình ảnh lệch sang một bên,
            trên cùng bên trái.

Sau đó, vùng đệm được xoay 270 độ ngược chiều kim đồng hồ để tính đến xoay thiết bị:

Cảm biến của máy ảnh được xoay theo hướng ngang kèm hình ảnh
            thẳng đứng.

Camera mặt sau

Máy ảnh mặt sau thường có hướng cảm biến 90 độ (như xem từ mặt sau của thiết bị). Khi định hướng bản xem trước của máy ảnh, vùng đệm hình ảnh cảm biến được xoay theo chiều kim đồng hồ theo số góc xoay cảm biến (thay vì ngược chiều kim đồng hồ như máy ảnh mặt trước), sau đó chọn hình ảnh vùng đệm được xoay ngược chiều kim đồng hồ theo số vòng xoay của thiết bị.

Bản xem trước của máy ảnh và cảm biến cả theo hướng ngang, nhưng
            cảm biến bị lộn ngược.
Hình 4. Điện thoại có máy ảnh mặt sau theo hướng ngang (đã xoay 270 độ hoặc -90 độ).

Dưới đây là vùng đệm hình ảnh qua cảm biến của máy ảnh trong hình 4:

Cảm biến của máy ảnh được xoay theo hướng ngang với hình ảnh lộn ngược
            xuống.

Vùng đệm phải xoay 90 độ theo chiều kim đồng hồ để điều chỉnh cho cảm biến hướng:

Cảm biến của máy ảnh được xếp hạng theo hướng dọc với hình ảnh lệch sang một bên,
            trên cùng bên trái.

Sau đó, vùng đệm xoay 270 độ ngược chiều kim đồng hồ để phù hợp với thiết bị xoay:

Cảm biến của máy ảnh được xoay theo hướng ngang kèm hình ảnh
            thẳng đứng.

Tỷ lệ khung hình

Tỷ lệ khung hình của màn hình thay đổi khi hướng thiết bị thay đổi và cả khi thiết bị có thể gập lại và mở ra khi cửa sổ được đổi kích thước ở chế độ nhiều cửa sổ môi trường xung quanh và khi ứng dụng mở trên màn hình phụ.

Vùng đệm hình ảnh cảm biến của máy ảnh phải được định hướng và điều chỉnh theo tỷ lệ cho phù hợp với hướng và tỷ lệ cỡ ảnh của phần tử trên giao diện người dùng kính ngắm dưới dạng giao diện người dùng tự động thay đổi hướng – có hoặc không có thiết bị thay đổi hướng.

Trên các kiểu dáng mới hoặc trong môi trường nhiều cửa sổ hay nhiều màn hình, nếu ứng dụng giả định rằng bản xem trước của máy ảnh có cùng hướng với thiết bị (dọc hoặc ngang) bản xem trước của bạn có thể không được định hướng đúng, điều chỉnh theo tỷ lệ không chính xác hoặc cả hai.

Thiết bị có thể gập lại không gập lại đang bật chế độ xem trước của máy ảnh theo hướng dọc
            lệch sang một bên.
Hình 5. Hiệu ứng chuyển đổi thiết bị có thể gập lại từ dọc sang ngang tỷ lệ khung hình nhưng cảm biến của máy ảnh vẫn ở hướng dọc.

Trong hình 5, ứng dụng giả định nhầm rằng thiết bị đã được xoay 90 độ độ ngược chiều kim đồng hồ; và do đó, ứng dụng đã xoay bản xem trước theo cùng mức độ.

Thiết bị có thể gập lại khi mở với bản xem trước của máy ảnh ở dạng thẳng đứng nhưng bị đè nén
            do chia tỷ lệ không chính xác.
Hình 6. Hiệu ứng chuyển đổi thiết bị có thể gập lại từ dọc sang ngang tỷ lệ khung hình nhưng cảm biến của máy ảnh vẫn ở hướng dọc.

Trong hình 6, ứng dụng không điều chỉnh tỷ lệ khung hình của vùng đệm hình ảnh thành cho phép nó điều chỉnh tỷ lệ đúng cách cho vừa với kích thước mới của giao diện người dùng bản xem trước của máy ảnh .

Ứng dụng máy ảnh hướng cố định thường gặp sự cố trên thiết bị có thể gập lại và thiết bị có màn hình lớn khác như máy tính xách tay:

Bản xem trước của máy ảnh trên máy tính xách tay đang ở chế độ thẳng đứng, nhưng giao diện người dùng của ứng dụng bị lệch sang một bên.
Hình 7. Ứng dụng dọc theo hướng cố định trên máy tính xách tay.

Trong hình 7, giao diện người dùng của ứng dụng máy ảnh bị lệch sang một bên do hướng của ứng dụng chỉ hiển thị ở chế độ dọc. Hình ảnh kính ngắm được định hướng chính xác so với cảm biến của máy ảnh.

Lồng ghép chế độ dọc

Các ứng dụng máy ảnh không hỗ trợ chế độ nhiều cửa sổ (resizeableActivity="false") và hạn chế hướng của các em (screenOrientation="portrait") hoặc screenOrientation="landscape") có thể được đặt ở chế độ dọc lồng ghép trên các thiết bị có màn hình lớn để định hướng đúng cách bản xem trước của camera.

Lồng ghép các ứng dụng chỉ hiển thị theo chiều dọc ở chế độ dọc ở chế độ dọc mặc dù tỷ lệ khung hình của màn hình là ngang. Các ứng dụng chỉ hiển thị theo hướng ngang được tạo hiệu ứng hòm thư theo hướng ngang mặc dù thì tỷ lệ khung hình của màn hình là dọc. Hình ảnh máy ảnh được xoay để căn chỉnh cho giao diện người dùng của ứng dụng, được cắt cho phù hợp với tỷ lệ khung hình của bản xem trước camera, và sau đó điều chỉnh tỷ lệ để lấp đầy bản xem trước.

Chế độ dọc lồng ghép được kích hoạt khi tỷ lệ khung hình của hình ảnh máy ảnh cảm biến và tỷ lệ khung hình của hoạt động chính của ứng dụng không khớp.

Bản xem trước của máy ảnh và giao diện người dùng của ứng dụng theo hướng dọc thích hợp trên máy tính xách tay.
            Hình ảnh xem trước trên màn hình rộng được điều chỉnh theo tỷ lệ và cắt cho vừa với chiều dọc
            hướng.
Hình 8. Ứng dụng dọc hướng cố định ở chế độ dọc phần lồng ghép đang bật máy tính xách tay.

Trong hình 8, ứng dụng máy ảnh chỉ hiển thị theo hướng dọc đã được xoay để hiển thị giao diện người dùng thẳng đứng trên màn hình máy tính xách tay. Ứng dụng ở dạng hòm thư do sự khác biệt về tỷ lệ khung hình giữa ứng dụng dọc và màn hình ngang. Camera hình ảnh xem trước đã được xoay để bù cho việc xoay giao diện người dùng của ứng dụng (do lồng ghép và hình ảnh đã được cắt và điều chỉnh theo tỷ lệ cho vừa với hướng dọc, giảm trường nhìn.

Xoay, cắt, điều chỉnh theo tỷ lệ

Chế độ chân dung lồng ghép được gọi cho ứng dụng máy ảnh chỉ hiển thị dọc trên màn hình có tỷ lệ khung hình ngang:

Bản xem trước của máy ảnh trên máy tính xách tay đang ở chế độ thẳng đứng, nhưng giao diện người dùng của ứng dụng bị lệch sang một bên.
Hình 9. Ứng dụng dọc theo hướng cố định trên máy tính xách tay.

Ứng dụng được tạo hiệu ứng hòm thư theo hướng dọc:

Ứng dụng được xoay sang hướng dọc và có hiệu ứng hòm thư. Hình ảnh là
            sang một bên, trên cùng bên phải.

Hình ảnh máy ảnh được xoay 90 độ để điều chỉnh hướng của ứng dụng:

Hình ảnh cảm biến đã xoay 90 độ để đứng thẳng.

Hình ảnh được cắt theo tỷ lệ khung hình của bản xem trước của máy ảnh, sau đó điều chỉnh tỷ lệ lấp đầy bản xem trước (trường nhìn giảm):

Hình ảnh máy ảnh đã được cắt được điều chỉnh theo tỷ lệ để lấp đầy bản xem trước của máy ảnh.

Trên các thiết bị có thể gập lại, hướng của cảm biến của máy ảnh có thể là hướng dọc trong khi tỷ lệ khung hình của màn hình là ngang:

Bản xem trước của máy ảnh và giao diện người dùng của ứng dụng bị lệch sang một bên trên màn hình rộng và mở.
Hình 10. Thiết bị mở ra có ứng dụng máy ảnh chỉ hiển thị theo hướng dọc và tỷ lệ khung hình khác nhau của cảm biến và màn hình máy ảnh.

Vì bản xem trước của máy ảnh được xoay để điều chỉnh hướng cảm biến, hình ảnh được định hướng chính xác trong kính ngắm, nhưng ứng dụng chỉ hiển thị theo hướng dọc lệch sang một bên.

Lồng ghép chế độ dọc chỉ cần tạo hiệu ứng hòm thư cho ứng dụng theo hướng dọc để định hướng đúng cách bản xem trước ứng dụng và máy ảnh:

Ứng dụng dạng hòm thư theo hướng dọc với bản xem trước của máy ảnh
            thẳng đứng trên thiết bị có thể gập lại.

API

Kể từ Android 12 (API cấp 31), các ứng dụng cũng có thể kiểm soát rõ ràng phần lồng ghép dọc thông qua phương pháp SCALER_ROTATE_AND_CROP thuộc tính của CaptureRequest .

Giá trị mặc định là SCALER_ROTATE_AND_CROP_AUTO! cho phép hệ thống gọi chế độ dọc lồng ghép. SCALER_ROTATE_AND_CROP_90 là hành vi của chế độ dọc có phần lồng ghép như mô tả ở trên.

Không phải thiết bị nào cũng hỗ trợ toàn bộ giá trị SCALER_ROTATE_AND_CROP. Cách lấy danh sách giá trị được hỗ trợ, tham chiếu CameraCharacteristics#SCALER_AVAILABLE_ROTATE_AND_CROP_MODES.

CameraX

Thư viện Jetpack CameraX giúp tạo ra kính ngắm máy ảnh phù hợp với hướng cảm biến và xoay thiết bị một nhiệm vụ đơn giản.

Phần tử bố cục PreviewView tạo bản xem trước của máy ảnh, tự động điều chỉnh theo hướng cảm biến, xoay thiết bị và chuyển tỷ lệ. PreviewView duy trì tỷ lệ khung hình của hình ảnh máy ảnh bằng cách áp dụng FILL_CENTER loại tỷ lệ, căn giữa hình ảnh nhưng có thể cắt hình ảnh cho phù hợp với kích thước của PreviewView. Để hòm thư cho hình ảnh camera, hãy đặt loại tỷ lệ thành FIT_CENTER.

Để tìm hiểu thông tin cơ bản về cách tạo bản xem trước của máy ảnh bằng PreviewView, hãy xem Triển khai bản xem trước.

Để triển khai mẫu hoàn chỉnh, hãy xem CameraXBasic kho lưu trữ trên GitHub.

Kính ngắm camera

Tương tự như trường hợp sử dụng Xem trước, CameraViewfinder cung cấp một bộ công cụ giúp đơn giản hoá việc tạo bản xem trước của máy ảnh. Nó không phụ thuộc vào CameraX Core nên bạn có thể tích hợp liền mạch vào cơ sở mã Camera2 hiện có.

Thay vì sử dụng Surface trực tiếp, bạn có thể sử dụng CameraViewfinder để hiển thị nguồn cấp dữ liệu máy ảnh cho Camera2.

CameraViewfinder sử dụng nội bộ TextureView hoặc SurfaceView để hiển thị nguồn cấp dữ liệu máy ảnh và áp dụng các phép biến đổi cần thiết cho nguồn cấp dữ liệu máy ảnh hiển thị chính xác kính ngắm. Quá trình này bao gồm việc điều chỉnh tỷ lệ khung hình, tỷ lệ và xoay.

Để yêu cầu nền tảng từ đối tượng CameraViewfinder, bạn cần phải tạo một ViewfinderSurfaceRequest.

Yêu cầu này chứa các yêu cầu về độ phân giải bề mặt và thiết bị máy ảnh từ CameraCharacteristics.

Đang gọi requestSurfaceAsync() gửi yêu cầu đến trình cung cấp nền tảng, đó là TextureView hoặc SurfaceView và nhận được ListenableFuture Surface.

Đang gọi markSurfaceSafeToRelease() thông báo cho nhà cung cấp nền tảng rằng nền tảng là không cần thiết và có liên quan có thể được phát hành.

Kotlin


fun startCamera(){
    val previewResolution = Size(width, height)
    val viewfinderSurfaceRequest =
        ViewfinderSurfaceRequest(previewResolution, characteristics)
    val surfaceListenableFuture =
        cameraViewfinder.requestSurfaceAsync(viewfinderSurfaceRequest)

    Futures.addCallback(surfaceListenableFuture, object : FutureCallback<Surface> {
        override fun onSuccess(surface: Surface) {
            /* create a CaptureSession using this surface as usual */
        }
        override fun onFailure(t: Throwable) { /* something went wrong */}
    }, ContextCompat.getMainExecutor(context))
}

Java


    void startCamera(){
        Size previewResolution = new Size(width, height);
        ViewfinderSurfaceRequest viewfinderSurfaceRequest =
                new ViewfinderSurfaceRequest(previewResolution, characteristics);
        ListenableFuture<Surface> surfaceListenableFuture =
                cameraViewfinder.requestSurfaceAsync(viewfinderSurfaceRequest);

        Futures.addCallback(surfaceListenableFuture, new FutureCallback<Surface>() {
            @Override
            public void onSuccess(Surface result) {
                /* create a CaptureSession using this surface as usual */
            }
            @Override public void onFailure(Throwable t) { /* something went wrong */}
        },  ContextCompat.getMainExecutor(context));
    }

SurfaceView

SurfaceView là một phương pháp đơn giản để tạo bản xem trước của máy ảnh nếu bản xem trước này không yêu cầu xử lý và không được tạo ảnh động.

SurfaceView tự động xoay vùng đệm hình ảnh cảm biến của máy ảnh cho phù hợp hướng màn hình, có tính đến cả hướng cảm biến và thiết bị xoay. Tuy nhiên, vùng đệm hình ảnh được điều chỉnh theo tỷ lệ cho vừa với SurfaceView mà không cần xem xét đến tỷ lệ khung hình.

Bạn phải đảm bảo rằng tỷ lệ khung hình của vùng đệm hình ảnh khớp với khung hình tỷ lệ SurfaceView mà bạn có thể đạt được bằng cách chia tỷ lệ nội dung của SurfaceView trong phần tử của thành phần onMeasure() phương thức:

(Mã nguồn computeRelativeRotation() nằm trong Xoay vòng tương đối bên dưới.)

Kotlin

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    val width = MeasureSpec.getSize(widthMeasureSpec)
    val height = MeasureSpec.getSize(heightMeasureSpec)

    val relativeRotation = computeRelativeRotation(characteristics, surfaceRotationDegrees)

    if (previewWidth > 0f && previewHeight > 0f) {
        /* Scale factor required to scale the preview to its original size on the x-axis. */
        val scaleX =
            if (relativeRotation % 180 == 0) {
                width.toFloat() / previewWidth
            } else {
                width.toFloat() / previewHeight
            }
        /* Scale factor required to scale the preview to its original size on the y-axis. */
        val scaleY =
            if (relativeRotation % 180 == 0) {
                height.toFloat() / previewHeight
            } else {
                height.toFloat() / previewWidth
            }

        /* Scale factor required to fit the preview to the SurfaceView size. */
        val finalScale = min(scaleX, scaleY)

        setScaleX(1 / scaleX * finalScale)
        setScaleY(1 / scaleY * finalScale)
    }
    setMeasuredDimension(width, height)
}

Java

@Override
void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int width = MeasureSpec.getSize(widthMeasureSpec);
    int height = MeasureSpec.getSize(heightMeasureSpec);

    int relativeRotation = computeRelativeRotation(characteristics, surfaceRotationDegrees);

    if (previewWidth > 0f && previewHeight > 0f) {

        /* Scale factor required to scale the preview to its original size on the x-axis. */
        float scaleX = (relativeRotation % 180 == 0)
                       ? (float) width / previewWidth
                       : (float) width / previewHeight;

        /* Scale factor required to scale the preview to its original size on the y-axis. */
        float scaleY = (relativeRotation % 180 == 0)
                       ? (float) height / previewHeight
                       : (float) height / previewWidth;

        /* Scale factor required to fit the preview to the SurfaceView size. */
        float finalScale = Math.min(scaleX, scaleY);

        setScaleX(1 / scaleX * finalScale);
        setScaleY(1 / scaleY * finalScale);
    }
    setMeasuredDimension(width, height);
}

Để biết thêm thông tin về cách triển khai SurfaceView làm bản xem trước của máy ảnh, hãy xem Hướng máy ảnh.

Chế độ xem kết cấu

TextureView có hiệu suất kém hơn SurfaceView — và nhiều việc khác, nhưng TextureView sẽ mang lại cho bạn tối đa quyền kiểm soát bản xem trước của camera.

TextureView xoay vùng đệm hình ảnh cảm biến dựa trên hướng cảm biến nhưng không xử lý việc xoay thiết bị hoặc điều chỉnh tỷ lệ xem trước.

Việc điều chỉnh tỷ lệ và xoay có thể được mã hoá theo Biến đổi ma trận. Để tìm hiểu cách điều chỉnh tỷ lệ và xoay TextureView một cách chính xác, hãy xem Hỗ trợ các nền tảng có thể đổi kích thước trong ứng dụng máy ảnh

Xoay tương đối

Độ xoay tương đối của cảm biến máy ảnh là số vòng xoay cần thiết để căn chỉnh đầu ra cảm biến của máy ảnh theo hướng của thiết bị.

Chế độ xoay tương đối được các thành phần như SurfaceViewTextureView sử dụng để xác định hệ số tỷ lệ x và y cho hình ảnh xem trước. Tên này cũng được dùng để chỉ định chế độ xoay của vùng đệm hình ảnh cảm biến.

Chiến lược phát hành đĩa đơn CameraCharacteristics và Các lớp Surface cho phép tính toán độ xoay tương đối của cảm biến máy ảnh:

Kotlin

/**
 * Computes rotation required to transform the camera sensor output orientation to the
 * device's current orientation in degrees.
 *
 * @param characteristics The CameraCharacteristics to query for the sensor orientation.
 * @param surfaceRotationDegrees The current device orientation as a Surface constant.
 * @return Relative rotation of the camera sensor output.
 */
public fun computeRelativeRotation(
    characteristics: CameraCharacteristics,
    surfaceRotationDegrees: Int
): Int {
    val sensorOrientationDegrees =
        characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!!

    // Reverse device orientation for back-facing cameras.
    val sign = if (characteristics.get(CameraCharacteristics.LENS_FACING) ==
        CameraCharacteristics.LENS_FACING_FRONT
    ) 1 else -1

    // Calculate desired orientation relative to camera orientation to make
    // the image upright relative to the device orientation.
    return (sensorOrientationDegrees - surfaceRotationDegrees * sign + 360) % 360
}

Java

/**
 * Computes rotation required to transform the camera sensor output orientation to the
 * device's current orientation in degrees.
 *
 * @param characteristics The CameraCharacteristics to query for the sensor orientation.
 * @param surfaceRotationDegrees The current device orientation as a Surface constant.
 * @return Relative rotation of the camera sensor output.
 */
public int computeRelativeRotation(
    CameraCharacteristics characteristics,
    int surfaceRotationDegrees
){
    Integer sensorOrientationDegrees =
        characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);

    // Reverse device orientation for back-facing cameras.
    int sign = characteristics.get(CameraCharacteristics.LENS_FACING) ==
        CameraCharacteristics.LENS_FACING_FRONT ? 1 : -1;

    // Calculate desired orientation relative to camera orientation to make
    // the image upright relative to the device orientation.
    return (sensorOrientationDegrees - surfaceRotationDegrees * sign + 360) % 360;
}

Chỉ số cửa sổ

Không nên sử dụng kích thước màn hình để xác định kích thước của máy ảnh kính ngắm; ứng dụng máy ảnh có thể đang chạy ở một phần của màn hình ở chế độ nhiều cửa sổ trên thiết bị di động hoặc ở chế độ rời trên ChromeOS.

WindowManager#getCurrentWindowMetrics() (được thêm vào API cấp 30) trả về kích thước của cửa sổ ứng dụng thay vì kích thước của màn hình. Các phương thức thư viện Jetpack WindowManager WindowMetricsCalculator#computeCurrentWindowMetrics()WindowInfoTracker#currentWindowMetrics() cung cấp khả năng hỗ trợ tương tự có khả năng tương thích ngược với API cấp 14.

xoay 180 độ

Xoay 180 độ của thiết bị (ví dụ: từ hướng tự nhiên sang hướng tự nhiên lộn ngược) sẽ không kích hoạt onConfigurationChanged() . Do đó, bản xem trước của máy ảnh có thể bị lộn ngược.

Để phát hiện chế độ xoay 180 độ, hãy triển khai một DisplayListener và kiểm tra chế độ xoay của thiết bị bằng lệnh gọi đến Display#getRotation() trong phần onDisplayChanged() .

Tài nguyên độc quyền

Trước Android 10, chỉ hoạt động ở trên cùng mới được hiển thị trong chế độ nhiều cửa sổ môi trường đã ở trạng thái RESUMED. Điều này gây nhầm lẫn cho người dùng vì hệ thống không cung cấp chỉ báo nào về hoạt động nào đã được tiếp tục.

Android 10 (API cấp 29) ra mắt tính năng tiếp tục nhiều lần trong đó tất cả hoạt động hiển thị đang ở trạng thái RESUMED. Các hoạt động hiển thị vẫn có thể truy cập vào PAUSED trạng thái nếu, ví dụ: một hoạt động rõ ràng nằm ở trên hoạt động hoặc Không thể lấy hoạt động làm tâm điểm, chẳng hạn như ở chế độ hình trong hình (xem Hỗ trợ chế độ hình trong hình).

Một ứng dụng sử dụng máy ảnh, micrô hoặc bất kỳ tài nguyên singleton trên API cấp 29 trở lên phải hỗ trợ tính năng tiếp tục nhiều lần. Cho Ví dụ: nếu ba hoạt động đã tiếp tục muốn sử dụng camera, thì chỉ một hoạt động có thể để truy cập vào tài nguyên độc quyền này. Mỗi hoạt động phải triển khai một onDisconnected() gọi lại để nhận biết quyền truy cập trước vào máy ảnh theo mức độ ưu tiên cao hơn của bạn.

Để biết thêm thông tin, hãy xem Tiếp tục nhiều lần.

Tài nguyên khác