การหมุนกรณีการใช้งานของ CameraX

หัวข้อนี้แสดงวิธีตั้งค่ากรณีการใช้งาน CameraX ภายในแอปเพื่อรับ ซึ่งมีข้อมูลการหมุนที่ถูกต้อง ไม่ว่าจะมาจาก ImageAnalysis หรือกรณีการใช้งาน ImageCapture ดังนั้น:

  • Analyzer ของกรณีการใช้งาน ImageAnalysis ควรได้รับเฟรมที่มี การหมุนที่ถูกต้อง
  • กรณีการใช้งาน ImageCapture ควรถ่ายภาพด้วยการหมุนที่ถูกต้อง

คำศัพท์

หัวข้อนี้ใช้คำศัพท์ต่อไปนี้ ดังนั้นการทำความเข้าใจว่าแต่ละคำหมายความว่าอย่างไร เป็นสิ่งสำคัญ:

การวางแนวจอแสดงผล
ส่วนนี้หมายถึงด้านใดของอุปกรณ์อยู่ในตำแหน่งที่สูงขึ้น และ ค่าใดค่าหนึ่งใน 4 ค่า ได้แก่ แนวตั้ง แนวนอน ย้อนกลับแนวตั้ง หรือย้อนกลับ แนวนอน
การหมุนจอแสดงผล
นี่คือค่าที่แสดงผลโดย Display.getRotation() และ แสดงองศาที่หมุนอุปกรณ์ทวนเข็มนาฬิกาจาก การวางแนวตามธรรมชาติ
การหมุนเวียนเป้าหมาย
ข้อมูลนี้แสดงค่าองศาที่จะหมุน วางอุปกรณ์ตามเข็มนาฬิกาเพื่อให้ถึงแนวที่เป็นธรรมชาติ

วิธีระบุการหมุนเวียนเป้าหมาย

ตัวอย่างต่อไปนี้แสดงวิธีกำหนดการหมุนเป้าหมายสำหรับอุปกรณ์ ตามการวางแนวธรรมชาติ

ตัวอย่างที่ 1: การวางแนวตามธรรมชาติของแนวตั้ง

ตัวอย่างอุปกรณ์: Pixel 3 XL

การวางแนวธรรมชาติ = แนวตั้ง
การวางแนวปัจจุบัน = แนวตั้ง

การหมุนจอแสดงผล = 0
การหมุนเวียนเป้าหมาย = 0

การวางแนวธรรมชาติ = แนวตั้ง
การวางแนวปัจจุบัน = แนวนอน

การหมุนจอแสดงผล = 90
การหมุนเวียนเป้าหมาย = 90

ตัวอย่างที่ 2: การวางแนวนอนตามธรรมชาติ

ตัวอย่างอุปกรณ์: Pixel C

การวางแนวธรรมชาติ = แนวนอน
การวางแนวปัจจุบัน = แนวนอน

การหมุนจอแสดงผล = 0
การหมุนเวียนเป้าหมาย = 0

การวางแนวธรรมชาติ = แนวนอน
การวางแนวปัจจุบัน = แนวตั้ง

การหมุนจอแสดงผล = 270
การหมุนเวียนเป้าหมาย = 270

การหมุนภาพ

ปลายสายข้างไหนเป็นปลายสาย การวางแนวเซ็นเซอร์กำหนดไว้ใน Android เป็นค่าคงที่ ซึ่งแสดงองศา (0, 90, 180, 270) ที่เซ็นเซอร์หมุนจาก ที่ด้านบนของอุปกรณ์เมื่ออุปกรณ์อยู่ในตำแหน่งตามปกติ สำหรับ ในแผนภาพ การหมุนภาพจะอธิบายว่าข้อมูลควรเป็นอย่างไร หมุนตามเข็มนาฬิกาเพื่อให้ตั้งตรง

ตัวอย่างต่อไปนี้แสดงลักษณะการหมุนของภาพโดยขึ้นอยู่กับ การวางแนวเซ็นเซอร์ของกล้อง และจะถือว่ามีการตั้งค่าการหมุนเวียนเป้าหมายเป็น การหมุนจอแสดงผล

ตัวอย่างที่ 1: เซ็นเซอร์หมุน 90 องศา

ตัวอย่างอุปกรณ์: Pixel 3 XL

การหมุนจอแสดงผล = 0
การวางแนวจอแสดงผล = แนวตั้ง
การหมุนรูปภาพ = 90

การหมุนจอแสดงผล = 90
การวางแนวจอแสดงผล = แนวนอน
การหมุนรูปภาพ = 0

ตัวอย่างที่ 2: เซ็นเซอร์หมุน 270 องศา

ตัวอย่างอุปกรณ์: Nexus 5X

การหมุนจอแสดงผล = 0
การวางแนวจอแสดงผล = แนวตั้ง
การหมุนรูปภาพ = 270

การหมุนจอแสดงผล = 90
การวางแนวจอแสดงผล = แนวนอน
การหมุนรูปภาพ = 180

ตัวอย่างที่ 3: เซ็นเซอร์หมุน 0 องศา

ตัวอย่างอุปกรณ์: Pixel C (แท็บเล็ต)

การหมุนจอแสดงผล = 0
การวางแนวจอแสดงผล = แนวนอน
การหมุนรูปภาพ = 0

การหมุนจอแสดงผล = 270
การวางแนวจอแสดงผล = แนวตั้ง
การหมุนรูปภาพ = 90

การคำนวณการหมุนของรูปภาพ

การวิเคราะห์รูปภาพ

Analyzer ของ ImageAnalysis ได้รับรูปภาพจากกล้องในรูปแบบ ImageProxy วินาที รูปภาพแต่ละรูปจะมีข้อมูลการหมุน ซึ่งสามารถเข้าถึงได้ ผ่าน:

val rotation = imageProxy.imageInfo.rotationDegrees

ค่านี้แสดงองศาที่ต้องใช้การหมุนรูปภาพ ตามเข็มนาฬิกาเพื่อให้ตรงกับการหมุนเป้าหมายของ ImageAnalysis ในบริบทของ แอป Android การหมุนเป้าหมายของ ImageAnalysis มักจะตรงกับ การวางแนวของหน้าจอ

จับภาพ

Callback จะแนบอยู่กับอินสแตนซ์ ImageCapture เพื่อส่งสัญญาณเมื่อมีการจับภาพ ผลลัพธ์พร้อมแล้ว ผลลัพธ์อาจเป็นรูปภาพที่จับภาพไว้หรือข้อผิดพลาดก็ได้

เมื่อถ่ายภาพ Callback ที่ให้ไว้อาจมีสถานะอย่างใดอย่างหนึ่งต่อไปนี้ ประเภท:

  • OnImageCapturedCallback: รับรูปภาพที่มีสิทธิ์เข้าถึงด้วยหน่วยความจำใน ของ ImageProxy
  • OnImageSavedCallback: เรียกใช้เมื่อจับภาพแล้ว จัดเก็บไว้ในตำแหน่งที่ระบุโดย ImageCapture.OutputFileOptions ตัวเลือกสามารถระบุ File OutputStream หรือสถานที่ตั้งใน MediaStore

การหมุนของรูปภาพที่จับภาพไว้โดยไม่คำนึงถึงรูปแบบ (ImageProxy File, OutputStream, MediaStore Uri) แสดงถึงองศาการหมุนตาม ซึ่งภาพที่จับไว้จะต้องหมุนตามเข็มนาฬิกาเพื่อให้ตรงกับ ImageCapture การหมุนเวียนเป้าหมาย ซึ่งในบริบทของแอป Android โดยทั่วไป ให้ตรงกับการวางแนวของหน้าจอ

การดึงข้อมูลการหมุนภาพที่จับภาพทำได้โดยทำอย่างใดอย่างหนึ่งต่อไปนี้ ด้วยวิธีต่อไปนี้

ImageProxy

val rotation = imageProxy.imageInfo.rotationDegrees

File

val exif = Exif.createFromFile(file)
val rotation = exif.rotation

OutputStream

val byteArray = outputStream.toByteArray()
val exif = Exif.createFromInputStream(ByteArrayInputStream(byteArray))
val rotation = exif.rotation

MediaStore uri

val inputStream = contentResolver.openInputStream(outputFileResults.savedUri)
val exif = Exif.createFromInputStream(inputStream)
val rotation = exif.rotation

ยืนยันการหมุนของรูปภาพ

กรณีการใช้งาน ImageAnalysis และ ImageCapture ได้รับ ImageProxy จาก กล้องหลังจากส่งคำขอจับภาพสำเร็จ ImageProxy รวมรูปภาพและ เกี่ยวกับข้อมูลที่เกี่ยวข้อง รวมถึงการหมุนเวียนของโมเดลนั้นๆ ข้อมูลการหมุนเวียนนี้ แสดงองศาของการหมุนภาพเพื่อให้ตรงกับการใช้งาน การหมุนเวียนเป้าหมายของตัวพิมพ์เล็ก/ใหญ่

ขั้นตอนการยืนยันการหมุนของรูปภาพ

หลักเกณฑ์การหมุนเวียนเป้าหมายของ ImageCapture/ImageAnalysis

เนื่องจากอุปกรณ์จำนวนมากไม่หมุนกลับในแนวตั้งหรือกลับด้านแนวนอน โดยค่าเริ่มต้น แอป Android บางแอปจะไม่รองรับการวางแนวเหล่านี้ การระบุว่าแอป รองรับหรือไม่เปลี่ยนวิธีการหมุนเวียนเป้าหมายของกรณีการใช้งาน อัปเดตแล้ว

ด้านล่างคือตาราง 2 ตารางที่กำหนดวิธีทำให้การหมุนเวียนเป้าหมายของกรณีการใช้งานซิงค์กันอยู่เสมอ ด้วยการหมุนจอแสดงผล ภาพแรกแสดงวิธีการ พร้อมทั้งสนับสนุน 4 ทิศทาง ส่วนที่สองจะจัดการกับการวางแนวที่อุปกรณ์หมุนเท่านั้น เป็น โดยค่าเริ่มต้น

วิธีเลือกหลักเกณฑ์ที่ต้องปฏิบัติตามในแอป

  1. ตรวจสอบว่ากล้อง Activity ของแอปมีการวางแนวที่ล็อกไว้หรือไม่ การวางแนวที่ปลดล็อกอยู่ หรือหากมีการลบล้างการเปลี่ยนแปลงการกำหนดค่าการวางแนว

  2. เลือกว่าจะให้กล้องของแอป Activity รองรับอุปกรณ์ทั้ง 4 เครื่องหรือไม่ การวางแนว (แนวตั้ง แนวตั้งแบบกลับด้าน แนวนอน และกลับด้าน) หรือควรรองรับการวางแนวที่อุปกรณ์ ใช้อยู่รองรับเท่านั้น โดยค่าเริ่มต้น

รองรับการวางแนวทั้ง 4 แบบ

ตารางนี้กล่าวถึงหลักเกณฑ์บางประการที่ต้องปฏิบัติตามสำหรับกรณีที่อุปกรณ์ ไม่หมุนเพื่อกลับแนวตั้ง เช่นเดียวกันกับอุปกรณ์ ไม่หมุนกลับแนวนอน

สถานการณ์ หลักเกณฑ์ โหมดหน้าต่างเดียว โหมดแยกหน้าจอหลายหน้าต่าง
การวางแนวที่ไม่ได้ล็อก ตั้งค่า Use Case ทุก เวลาที่มีการสร้าง Activity เช่น การติดต่อกลับ onCreate() ของ Activity
ใช้ของ OrientationEventListener onOrientationChanged() ภายใน Callback ให้อัปเดตการหมุนเวียนเป้าหมายของ Use Case ซึ่งสามารถรองรับกรณีที่ระบบไม่ สร้าง Activity ใหม่แม้หลังจากการเปลี่ยนการวางแนว เหมือนตอนที่อุปกรณ์หมุน 180 องศา แฮนเดิลด้วยเมื่อจอแสดงผลอยู่ในแนวกลับด้าน แนวตั้งและอุปกรณ์ไม่หมุนเพื่อกลับภาพแนวตั้ง "ค่าเริ่มต้น" จัดการกรณีที่ Activity ไม่ เกิดขึ้นใหม่เมื่ออุปกรณ์หมุน (เช่น 90 องศา) สิ่งนี้จะเกิดขึ้นในวันที่ อุปกรณ์รูปแบบของอุปกรณ์ขนาดเล็กเมื่อแอปกินพื้นที่ครึ่งหนึ่งของหน้าจอ อุปกรณ์เมื่อแอปใช้พื้นที่ 2 ใน 3 ของหน้าจอ
ไม่บังคับ: ตั้งค่าscreenOrientationของ Activity พร็อพเพอร์ตี้ให้กับ fullSensor ใน AndroidManifest วิธีนี้จะช่วยให้ UI ตั้งตรงเมื่ออุปกรณ์อยู่กลับด้าน แนวตั้ง และอนุญาตให้ สร้าง Activity อีกครั้ง เมื่อใดก็ตามที่อุปกรณ์หมุน 90 องศา จะไม่มีผลกับอุปกรณ์ที่ไม่มีการหมุนเพื่อกลับภาพบุคคลภายใน "ค่าเริ่มต้น" ระบบไม่รองรับโหมดหลายหน้าต่างขณะที่จอแสดงผลอยู่ใน กลับแนวตั้ง
การวางแนวที่ล็อก ตั้งค่า Use Case เพียงครั้งเดียวเมื่อ Activity สร้างขึ้นครั้งแรก เช่น ใน Activity onCreate() Callback
ใช้ของ OrientationEventListener onOrientationChanged() ภายใน Callback ให้อัปเดตการหมุนเวียนเป้าหมายของกรณีการใช้งาน ยกเว้นการแสดงตัวอย่าง จัดการกรณีที่ Activity ไม่ เกิดขึ้นใหม่เมื่ออุปกรณ์หมุน (เช่น 90 องศา) สิ่งนี้จะเกิดขึ้นในวันที่ อุปกรณ์รูปแบบของอุปกรณ์ขนาดเล็กเมื่อแอปกินพื้นที่ครึ่งหนึ่งของหน้าจอ อุปกรณ์เมื่อแอปใช้พื้นที่ 2 ใน 3 ของหน้าจอ
ลบล้าง configChanges การวางแนวแล้ว ตั้งค่า Use Case เพียงครั้งเดียวเมื่อ Activity สร้างขึ้นครั้งแรก เช่น ใน Activity onCreate() Callback
ใช้ของ OrientationEventListener onOrientationChanged() ภายใน Callback ให้อัปเดตการหมุนเวียนเป้าหมายของ Use Case จัดการกรณีที่ Activity ไม่ เกิดขึ้นใหม่เมื่ออุปกรณ์หมุน (เช่น 90 องศา) สิ่งนี้จะเกิดขึ้นในวันที่ อุปกรณ์รูปแบบของอุปกรณ์ขนาดเล็กเมื่อแอปกินพื้นที่ครึ่งหนึ่งของหน้าจอ อุปกรณ์เมื่อแอปใช้พื้นที่ 2 ใน 3 ของหน้าจอ
ไม่บังคับ: ตั้งค่าพร็อพเพอร์ตี้ screenOrientation ของกิจกรรมเป็น FullSensor ใน ไฟล์ AndroidManifest อนุญาตให้ UI ตั้งตรงเมื่ออุปกรณ์อยู่ในแนวตั้งกลับกัน จะไม่มีผลกับอุปกรณ์ที่ไม่มีการหมุนเพื่อกลับภาพบุคคลภายใน "ค่าเริ่มต้น" ระบบไม่รองรับโหมดหลายหน้าต่างขณะที่จอแสดงผลอยู่ใน กลับแนวตั้ง

รองรับการวางแนวที่อุปกรณ์รองรับเท่านั้น

รองรับเฉพาะการวางแนวที่อุปกรณ์รองรับโดยค่าเริ่มต้น (ซึ่งอาจ ต้องไม่รวมแนวตั้ง/กลับด้าน)

สถานการณ์ หลักเกณฑ์ โหมดแยกหน้าจอหลายหน้าต่าง
การวางแนวที่ไม่ได้ล็อก ตั้งค่า Use Case ทุก เวลาที่มีการสร้าง Activity เช่น การติดต่อกลับ onCreate() ของ Activity
ใช้ของ DisplayListener onDisplayChanged() ภายใน Callback อัปเดตการหมุนเวียนเป้าหมายของกรณีการใช้งาน เช่น อุปกรณ์หมุน 180 องศาแล้ว จัดการกรณีที่ Activity ไม่ เกิดขึ้นใหม่เมื่ออุปกรณ์หมุน (เช่น 90 องศา) สิ่งนี้จะเกิดขึ้นในวันที่ อุปกรณ์รูปแบบของอุปกรณ์ขนาดเล็กเมื่อแอปกินพื้นที่ครึ่งหนึ่งของหน้าจอ อุปกรณ์เมื่อแอปใช้พื้นที่ 2 ใน 3 ของหน้าจอ
การวางแนวที่ล็อก ตั้งค่า Use Case เพียงครั้งเดียวเมื่อ Activity สร้างขึ้นครั้งแรก เช่น ใน Activity onCreate() Callback
ใช้ของ OrientationEventListener onOrientationChanged() ภายใน Callback ให้อัปเดตการหมุนเวียนเป้าหมายของ Use Case จัดการกรณีที่ Activity ไม่ เกิดขึ้นใหม่เมื่ออุปกรณ์หมุน (เช่น 90 องศา) สิ่งนี้จะเกิดขึ้นในวันที่ อุปกรณ์รูปแบบของอุปกรณ์ขนาดเล็กเมื่อแอปกินพื้นที่ครึ่งหนึ่งของหน้าจอ อุปกรณ์เมื่อแอปใช้พื้นที่ 2 ใน 3 ของหน้าจอ
ลบล้าง configChanges การวางแนวแล้ว ตั้งค่า Use Case เพียงครั้งเดียวเมื่อ Activity สร้างขึ้นครั้งแรก เช่น ใน Activity onCreate() Callback
ใช้ของ DisplayListener onDisplayChanged() ภายใน Callback อัปเดตการหมุนเวียนเป้าหมายของกรณีการใช้งาน เช่น อุปกรณ์หมุน 180 องศาแล้ว จัดการกรณีที่ Activity ไม่ เกิดขึ้นใหม่เมื่ออุปกรณ์หมุน (เช่น 90 องศา) สิ่งนี้จะเกิดขึ้นในวันที่ อุปกรณ์รูปแบบของอุปกรณ์ขนาดเล็กเมื่อแอปกินพื้นที่ครึ่งหนึ่งของหน้าจอ อุปกรณ์เมื่อแอปใช้พื้นที่ 2 ใน 3 ของหน้าจอ

ปลดล็อกการวางแนวแล้ว

Activity มีการวางแนวที่ปลดล็อกเมื่อการวางแนวจอแสดงผล (เช่น แนวตั้งหรือแนวนอน) ตรงกับการวางแนวของอุปกรณ์ด้วย ยกเว้นแนวตั้ง/แนวนอนแบบย้อนกลับ ซึ่งอุปกรณ์บางรุ่นไม่รองรับ โดยค่าเริ่มต้น หากต้องการบังคับให้อุปกรณ์หมุนไปทั้ง 4 ทิศทาง ให้ตั้งค่า พร็อพเพอร์ตี้ screenOrientation ของ Activity ไปยัง fullSensor

ในโหมดหลายหน้าต่าง อุปกรณ์ที่ไม่รองรับกลับแนวตั้ง/แนวนอน โดยค่าเริ่มต้น จะไม่หมุนกลับในแนวตั้ง/แนวนอน แม้ว่า ตั้งค่าพร็อพเพอร์ตี้ screenOrientation เป็น 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" />

ล็อกการวางแนวอยู่

จอแสดงผลจะมีการวางแนวที่ล็อกไว้เมื่อวางอยู่ในการวางแนวจอแสดงผลเดิม (เช่น แนวตั้งหรือแนวนอน) ไม่ว่าอุปกรณ์จะวางในแนวใด อุปกรณ์ ซึ่งทำได้โดยการระบุ screenOrientation ของ Activity ภายในการประกาศในไฟล์ AndroidManifest.xml

เมื่อวางการวางแนวจอแสดงผลไว้ ระบบจะไม่ทำลายและ สร้าง Activity ใหม่ขณะที่หมุนอุปกรณ์

<!-- The Activity keeps a portrait orientation even as the device rotates. -->
<activity
   android:name=".LockedOrientationActivity"
   android:screenOrientation="portrait" />

ลบล้างการเปลี่ยนแปลงการกำหนดค่าการวางแนวแล้ว

เมื่อ Activity ลบล้างการกำหนดค่าการวางแนว ระบบอาจ ไม่ทำลายและสร้างขึ้นใหม่เมื่อการวางแนวอุปกรณ์เปลี่ยนไป ระบบจะอัปเดต UI เพื่อให้ตรงกับการวางแนวของอุปกรณ์

<!-- 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" />

การตั้งค่ากรณีการใช้งานกล้อง

ในสถานการณ์ที่อธิบายไว้ข้างต้น กรณีการใช้งานกล้องสามารถตั้งค่าได้เมื่อ Activity สร้างขึ้นครั้งแรก

ในกรณีของ Activity ที่มีการวางแนวที่ปลดล็อกแล้ว การตั้งค่านี้จะดำเนินการเสร็จสิ้น ทุกครั้งที่หมุนอุปกรณ์ เพราะระบบจะทำลายและสร้าง Activity เมื่อเปลี่ยนการวางแนว ซึ่งทำให้ตั้งค่ากรณีการใช้งาน การหมุนเป้าหมายให้ตรงกับการวางแนวของจอแสดงผลโดยค่าเริ่มต้นทุกครั้ง

ในกรณีของ Activity ซึ่งการวางแนวที่ล็อกไว้หรือการวางแนวที่ลบล้าง มีการเปลี่ยนแปลงการกำหนดค่าการวางแนว การตั้งค่านี้จะเกิดขึ้นเพียงครั้งเดียวเมื่อActivity สร้างขึ้นครั้งแรก

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)
   }
}

การตั้งค่า OrientationEventListener

การใช้ OrientationEventListener ช่วยให้คุณอัปเดตเป้าหมายอย่างต่อเนื่อง การหมุนของกรณีการใช้งานกล้องเมื่อการวางแนวของอุปกรณ์เปลี่ยนแปลง

class CameraActivity : AppCompatActivity() {

    private val orientationEventListener by lazy {
        object : OrientationEventListener(this) {
            override fun onOrientationChanged(orientation: Int) {
                if (orientation == ORIENTATION_UNKNOWN) {
                    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()
    }
}

การตั้งค่า DisplayListener

การใช้ DisplayListener ช่วยให้คุณอัปเดตการหมุนเป้าหมายของกล้องได้ ใช้งานในบางกรณี เช่น เมื่อระบบไม่ทำลาย และสร้าง Activity ใหม่หลังจากที่อุปกรณ์หมุน 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)
    }
}