Chủ đề này trình bày cách thiết lập trường hợp sử dụng CameraX bên trong ứng dụng để nhận được hình ảnh với thông tin xoay chính xác, cho dù đó là từ trường hợp sử dụng ImageAnalysis hay ImageCapture. Vì vậy: Show
Thuật ngữChủ đề này sử dụng các thuật ngữ sau, do vậy, việc hiểu rõ nghĩa từng thuật ngữ là rất quan trọng: Hướng của màn hình Cụm từ này muốn chỉ cạnh nào của thiết bị đang ở vị trí hướng lên và có thể là một trong bốn giá trị: dọc, ngang, dọc lộn ngược hoặc ngang lộn ngược. Độ xoay màn hình Đây là giá trị được trả về bởi Display.getRotation() và cho biết số độ mà thiết bị được xoay ngược chiều kim đồng hồ từ hướng tự nhiên của thiết bị. Độ xoay mục tiêu Cụm từ này cho biết số độ cần xoay theo chiều kim đồng hồ để thiết bị trở về hướng tự nhiên.Cách xác định độ xoay mục tiêuCác ví dụ dưới đây cho thấy cách để xác định độ xoay mục tiêu cho thiết bị dựa trên hướng tự nhiên của thiết bị. Ví dụ 1: Hướng tự nhiên theo chiều dọc
Ví dụ 2: Hướng tự nhiên theo chiều ngang
Độ xoay hình ảnhĐầu nào đang hướng lên? Hướng cảm biến được xác định trong Android là một giá trị không đổi. Giá trị này biểu thị số độ (0, 90, 180, 270) mà cảm biến được xoay tính từ đầu thiết bị khi thiết bị đang ở vị trí tự nhiên. Đối với tất cả các trường hợp trong sơ đồ, độ xoay hình ảnh mô tả cách dữ liệu nên được xoay theo chiều kim đồng hồ để hiển thị theo chiều thẳng đứng. Các ví dụ sau đây cho thấy độ xoay hình ảnh nên là gì tuỳ thuộc vào hướng cảm biến của camera. Các ví dụ này cũng giả định rằng độ xoay mục tiêu được đặt là độ xoay màn hình. Ví dụ 1: Cảm biến được xoay 90 độ
Ví dụ 2: Cảm biến được xoay 270 độ
Ví dụ 3: Cảm biến được xoay 0 độ
Tính toán độ xoay hình ảnhImageAnalysisAnalyzer của ImageAnalysis nhận hình ảnh từ camera dưới dạng ImageProxy. Mỗi hình ảnh đều chứa thông tin độ xoay, mà có thể truy cập được qua: val rotation = imageProxy.imageInfo.rotationDegreesGiá trị này biểu thị số độ mà hình ảnh cần được xoay theo chiều kim đồng hồ để khớp với độ xoay mục tiêu của ImageAnalysis. Trong bối cảnh của ứng dụng Android, độ xoay mục tiêu của ImageAnalysis thường sẽ khớp với hướng của màn hình. ImageCaptureLệnh gọi lại được đính kèm vào một thực thể ImageCapture để gửi tín hiệu khi có kết quả chụp. Kết quả có thể là ảnh đã chụp hoặc lỗi. Khi chụp ảnh, lệnh gọi lại được cung cấp có thể thuộc một trong những loại sau:
Việc xoay ảnh chụp, bất kể định dạng của ảnh là gì (ImageProxy, File, OutputStream, MediaStore Uri) có nghĩa là số độ mà ảnh đã chụp cần xoay theo chiều kim đồng hồ để khớp với độ xoay mục tiêu của ImageCapture. Xin nhắc lại, trong ngữ cảnh của một ứng dụng Android độ xoay mục tiêu thường sẽ khớp với hướng của màn hình. Việc truy xuất độ xoay của ảnh chụp có thể được thực hiện theo một trong các cách sau: ImageProxy val rotation = imageProxy.imageInfo.rotationDegreesFile val exif = Exif.createFromFile(file) val rotation = exif.rotationOutputStream val byteArray = outputStream.toByteArray() val exif = Exif.createFromInputStream(ByteArrayInputStream(byteArray)) val rotation = exif.rotationMediaStore uri val inputStream = contentResolver.openInputStream(outputFileResults.savedUri) val exif = Exif.createFromInputStream(inputStream) val rotation = exif.rotationXác định hướng xoay của hình ảnhCác trường hợp sử dụng ImageAnalysis và ImageCapture nhận ImageProxy từ camera sau khi chụp ảnh thành công. ImageProxy gói ảnh và thông tin về ảnh đó, bao gồm cả độ xoay. Thông tin về độ xoay này thể hiện số độ mà bạn phải xoay hình ảnh để khớp với độ xoay mục tiêu của trường hợp sử dụng. Nguyên tắc về độ xoay mục tiêu trong ImageCapture/ImageAnalysisVì nhiều thiết bị không xoay để đảo chiều dọc hoặc đảo chiều ngang theo mặc định, nên một số ứng dụng Android không hỗ trợ các chiều này. Việc ứng dụng có hỗ trợ việc này hay không sẽ thay đổi cách mà độ xoay mục tiêu của trường hợp sử dụng được cập nhật. Dưới đây là hai bảng xác định cách đồng bộ hoá độ xoay mục tiêu của trường hợp sử dụng với độ xoay màn hình. Bảng đầu tiên hướng dẫn cách thực hiện việc này trong khi hỗ trợ cả bốn hướng; bảng thứ hai chỉ xử lý hướng thiết bị xoay theo mặc định. Để chọn thực hiện nguyên tắc nào trong ứng dụng của bạn:
Hỗ trợ cả bốn hướngBảng này đề cập đến một số nguyên tắc nhất định cần tuân theo trong trường hợp thiết bị không xoay để đảo chiều dọc. Áp dụng điều tương tự với các thiết bị không xoay để đảo chiều ngang.
Chỉ hỗ trợ các hướng được thiết bị hỗ trợChỉ hỗ trợ hướng mà thiết bị hỗ trợ theo mặc định (có thể bao gồm hoặc không bao gồm dọc lộn ngược/ngang lộn ngược).
Hướng đã được mở khoáMột Activity có hướng được mở khoá khi hướng của màn hình (như dọc hoặc ngang) khớp với hướng thực của thiết bị, ngoại trừ trường hợp dọc/ngang lộn ngược, mà một số thiết bị không hỗ trợ theo mặc định. Để buộc thiết bị xoay sang cả bốn hướng, hãy đặt thuộc tính screenOrientation của Activity thành fullSensor. Ở chế độ nhiều cửa sổ, theo mặc định một thiết bị không hỗ trợ chế độ dọc/ngang lộn ngược sẽ không xoay thành dọc/ngang lộn ngược, ngay cả khi thuộc tính screenOrientation của thiết bị được đặt thành fullSensor. <!-- The Activity has an unlocked orientation, but might not rotate to reverse portrait/landscape in single-window mode if the device doesn't support it by default. --> <activity android:name=".UnlockedOrientationActivity" /> <!-- The Activity has an unlocked orientation, and will rotate to all four orientations in single-window mode. --> <activity android:name=".UnlockedOrientationActivity" android:screenOrientation="fullSensor" />Hướng bị khoáMàn hình có hướng bị khoá khi vẫn ở cùng một hướng hiển thị (chẳng hạn như dọc hoặc ngang) bất kể hướng thực của thiết bị là gì. Bạn có thể thực hiện việc này bằng cách chỉ định thuộc tính screenOrientation của Activity bên trong phần khai báo của nó trong tệp AndroidManifest.xml. Khi màn hình có hướng bị khoá, hệ thống sẽ không huỷ và tạo lại Activity khi xoay thiết bị. <!-- The Activity keeps a portrait orientation even as the device rotates. --> <activity android:name=".LockedOrientationActivity" android:screenOrientation="portrait" />Ghi đè thay đổi về cấu hình hướngKhi Activity ghi đè thay đổi về cấu hình hướng, hệ thống sẽ không huỷ và tái tạo cấu hình đó khi hướng thực của thiết bị thay đổi. Tuy nhiên, hệ thống sẽ cập nhật giao diện người dùng để phù hợp với hướng thực của thiết bị. <!-- The Activity's UI might not rotate in reverse portrait/landscape if the device doesn't support it by default. --> <activity android:name=".OrientationConfigChangesOverriddenActivity" android:configChanges="orientation|screenSize" /> <!-- The Activity's UI will rotate to all 4 orientations in single-window mode. --> <activity android:name=".OrientationConfigChangesOverriddenActivity" android:configChanges="orientation|screenSize" android:screenOrientation="fullSensor" />Thiết lập các trường hợp sử dụng cameraTrong các trường hợp được mô tả ở trên, các trường hợp sử dụng camera có thể được thiết lập khi Activity được tạo lần đầu tiên. Trong trường hợp Activity có hướng được mở khoá, việc thiết lập này sẽ được thực hiện mỗi khi xoay thiết bị, vì hệ thống sẽ huỷ và tái tạo Activity khi hướng thay đổi. Kết quả là lần nào trường hợp sử dụng cũng sẽ cài đặt độ xoay mục tiêu cho phù hợp với hướng của màn hình theo mặc định. Trong trường hợp Activity có hướng bị hoá hoặc hoạt động ghi đè thay đổi cấu hình hướng, thì việc thiết lập này sẽ được thực hiện một lần khi Activity được tạo lần đầu tiên. class CameraActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val cameraProcessFuture = ProcessCameraProvider.getInstance(this) cameraProcessFuture.addListener(Runnable { val cameraProvider = cameraProcessFuture.get() // By default, the use cases set their target rotation to match the // display’s rotation. val preview = buildPreview() val imageAnalysis = buildImageAnalysis() val imageCapture = buildImageCapture() cameraProvider.bindToLifecycle( this, cameraSelector, preview, imageAnalysis, imageCapture) }, mainExecutor) } }Thiết lập OrientationEventListenerViệc sử dụng OrientationEventListener cho phép bạn liên tục cập nhật độ xoay mục tiêu của các trường hợp sử dụng camera khi hướng thiết bị thay đổi. class CameraActivity : AppCompatActivity() { private val orientationEventListener by lazy { object : OrientationEventListener(this) { override fun onOrientationChanged(orientation: Int) { if (orientation == UNKNOWN_ORIENTATION) { return } val rotation = when (orientation) { in 45 until 135 -> Surface.ROTATION_270 in 135 until 225 -> Surface.ROTATION_180 in 225 until 315 -> Surface.ROTATION_90 else -> Surface.ROTATION_0 } imageAnalysis.targetRotation = rotation imageCapture.targetRotation = rotation } } } override fun onStart() { super.onStart() orientationEventListener.enable() } override fun onStop() { super.onStop() orientationEventListener.disable() } }Thiết lập DisplayListenerViệc sử dụng DisplayListener cho phép bạn cập nhật độ xoay mục tiêu của trường hợp sử dụng camera trong một số trường hợp, chẳng hạn như khi hệ thống không huỷ và tái tạo Activity sau khi thiết bị xoay 180 độ. class CameraActivity : AppCompatActivity() { private val displayListener = object : DisplayManager.DisplayListener { override fun onDisplayChanged(displayId: Int) { if (rootView.display.displayId == displayId) { val rotation = rootView.display.rotation imageAnalysis.targetRotation = rotation imageCapture.targetRotation = rotation } } override fun onDisplayAdded(displayId: Int) { } override fun onDisplayRemoved(displayId: Int) { } } override fun onStart() { super.onStart() val displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager displayManager.registerDisplayListener(displayListener, null) } override fun onStop() { super.onStop() val displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager displayManager.unregisterDisplayListener(displayListener) } } |