แสดงตัวอย่างจากกล้องถ่ายรูป

หมายเหตุ: หน้านี้อ้างอิงถึงแพ็กเกจ Camera2 เราขอแนะนำให้ใช้ CameraX เว้นแต่ว่าแอปของคุณต้องใช้ฟีเจอร์ระดับต่ำที่เฉพาะเจาะจงจาก Camera2 ทั้ง CameraX และ Camera2 รองรับ Android 5.0 (API ระดับ 21) ขึ้นไป

กล้องและตัวอย่างภาพจากกล้องไม่ได้อยู่ในการวางแนวเดียวกันเสมอไปในอุปกรณ์ Android

กล้องจะอยู่ในตำแหน่งคงที่บนอุปกรณ์ ไม่ว่าอุปกรณ์นั้นจะเป็นโทรศัพท์ แท็บเล็ต หรือคอมพิวเตอร์ก็ตาม เมื่อการวางแนวอุปกรณ์เปลี่ยนไป การวางแนวกล้องก็จะเปลี่ยนไปด้วย

ด้วยเหตุนี้ โดยทั่วไปแอปกล้องจะสันนิษฐานว่าการวางแนวของอุปกรณ์กับสัดส่วนภาพพรีวิวของกล้องมีความสัมพันธ์กันแบบตายตัว เมื่อโทรศัพท์อยู่ในแนวตั้ง ระบบจะถือว่าตัวอย่างภาพจากกล้องมีความสูงมากกว่าความกว้าง เมื่อหมุนโทรศัพท์ (และกล้อง) เป็นแนวนอน ตัวอย่างภาพจากกล้องควรจะกว้างกว่าความสูง

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

การวางแนวของกล้อง

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

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

ทั้งโทรศัพท์และเซ็นเซอร์ตรวจจับผ่านกล้องอยู่ในแนวตั้ง
รูปที่ 1 ความสัมพันธ์โดยทั่วไปของการวางแนวเซ็นเซอร์ตรวจจับผ่านกล้องและโทรศัพท์

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

API camera2 มีค่าคงที่ SENSOR_ORIENTATION เพื่อแสดงการหมุนเซ็นเซอร์ต่อแอป สำหรับโทรศัพท์และแท็บเล็ตส่วนใหญ่ อุปกรณ์จะรายงานการวางแนวเซ็นเซอร์ที่ 270 องศาสำหรับกล้องหน้า และ 90 องศา (มุมมองจากด้านหลังของอุปกรณ์) สำหรับกล้องหลัง ซึ่งจะจัดแนวขอบด้านยาวของเซ็นเซอร์ให้ตรงกับขอบด้านยาวของอุปกรณ์ โดยทั่วไปกล้องของแล็ปท็อปจะรายงานการวางแนวเซ็นเซอร์เป็น 0 หรือ 180 องศา

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

ตัวอย่างเช่น สำหรับกล้องหน้าในรูปที่ 1 บัฟเฟอร์รูปภาพที่เซ็นเซอร์กล้องสร้างขึ้นจะมีลักษณะดังนี้

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

ต้องหมุนรูปภาพ 270 องศาทวนเข็มนาฬิกาเพื่อให้การวางแนวของตัวอย่างตรงกับการวางแนวของอุปกรณ์ ดังนี้

เซ็นเซอร์กล้องในแนวตั้งพร้อมรูปภาพที่ตั้งตรง
รูปที่ 3 บัฟเฟอร์รูปภาพของกล้องหน้าหมุนทวนเข็มนาฬิกา 270 องศาเพื่อให้ตรงกับการวางแนวอุปกรณ์ในแนวตั้ง

กล้องหลังจะสร้างบัฟเฟอร์รูปภาพที่มีการวางแนวเดียวกันกับบัฟเฟอร์ก่อนหน้า แต่ SENSOR_ORIENTATION จะเป็น 90 องศา ด้วยเหตุนี้ บัฟเฟอร์จึงหมุน 90 องศาตามเข็มนาฬิกา

การหมุนอุปกรณ์

การหมุนอุปกรณ์คือจำนวนองศาที่อุปกรณ์หมุนจากแนวการวางตามปกติ ตัวอย่างเช่น โทรศัพท์ในแนวนอนจะมีการหมุนอุปกรณ์ 90 หรือ 270 องศา โดยขึ้นอยู่กับทิศทางการหมุน

ต้องหมุนบัฟเฟอร์รูปภาพของเซ็นเซอร์กล้องเป็นจำนวนองศาเท่ากับการหมุนอุปกรณ์ (นอกเหนือจากองศาของการวางแนวเซ็นเซอร์) เพื่อให้ตัวอย่างภาพจากกล้องปรากฏในแนวตั้ง

การคำนวณการวางแนว

การวางแนวที่เหมาะสมของตัวอย่างภาพจากกล้องจะพิจารณาการวางแนวของเซ็นเซอร์และการหมุนของอุปกรณ์

การหมุนโดยรวมของบัฟเฟอร์รูปภาพเซ็นเซอร์สามารถคำนวณได้โดยใช้สูตรต่อไปนี้

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

โดย sign คือ 1 สำหรับกล้องหน้า และ -1 สำหรับกล้องหลัง

สำหรับกล้องหน้า บัฟเฟอร์รูปภาพจะหมุนทวนเข็มนาฬิกา (จากทิศทางตามธรรมชาติของเซ็นเซอร์) สำหรับกล้องหลัง บัฟเฟอร์รูปภาพของเซ็นเซอร์จะหมุนตามเข็มนาฬิกา

นิพจน์ deviceOrientationDegrees * sign + 360 จะแปลงการหมุนอุปกรณ์จากทวนเข็มนาฬิกาเป็นตามเข็มนาฬิกาสำหรับกล้องหลัง (เช่น แปลง 270 องศาทวนเข็มนาฬิกาเป็น 90 องศาตามเข็มนาฬิกา) การดำเนินการมอดูโลจะปรับขนาดผลลัพธ์ให้น้อยกว่า 360 องศา (เช่น ปรับขนาดการหมุน 540 องศาเป็น 180)

API ต่างๆ จะรายงานการหมุนอุปกรณ์แตกต่างกันไป ดังนี้

  • Display#getRotation() แสดงการหมุนอุปกรณ์ทวนเข็มนาฬิกา (จากมุมมองของผู้ใช้) ค่านี้จะเสียบเข้ากับสูตรก่อนหน้าตามที่เป็นอยู่
  • OrientationEventListener#onOrientationChanged() แสดงผลการหมุนอุปกรณ์ตามเข็มนาฬิกา (จากมุมมองของผู้ใช้) ปฏิเสธค่าเพื่อใช้ในสูตรก่อนหน้า

กล้องหน้า

ตัวอย่างภาพจากกล้องและเซ็นเซอร์อยู่ในแนวนอนทั้งคู่ เซ็นเซอร์อยู่ในแนวตั้ง
รูปที่ 4 ตัวอย่างภาพจากกล้องและเซ็นเซอร์เมื่อหมุนโทรศัพท์ 90 องศาเป็นแนวนอน

ต่อไปนี้คือบัฟเฟอร์รูปภาพที่สร้างโดยเซ็นเซอร์ตรวจจับผ่านกล้องในรูปที่ 4

เซ็นเซอร์ตรวจจับผ่านกล้องในแนวนอนพร้อมรูปภาพแนวตั้ง
รูปที่ 5 บัฟเฟอร์รูปภาพที่สร้างโดยเซ็นเซอร์กล้องหน้าเมื่อหมุนโทรศัพท์ทวนเข็มนาฬิกา 90 องศา

ต้องหมุนบัฟเฟอร์ทวนเข็มนาฬิกา 270 องศาเพื่อปรับการวางแนวเซ็นเซอร์ (ดูการวางแนวกล้องด้านบน)

เซ็นเซอร์ตรวจจับผ่านกล้องหมุนเป็นแนวตั้งโดยมีรูปภาพตะแคงข้าง ด้านขวาบน
รูปที่ 6 บัฟเฟอร์รูปภาพหมุนทวนเข็มนาฬิกา 270 องศาเพื่อปรับตามการวางแนวของเซ็นเซอร์

จากนั้นระบบจะหมุนบัฟเฟอร์ทวนเข็มนาฬิกาอีก 90 องศาเพื่อพิจารณาการหมุนของอุปกรณ์ ซึ่งส่งผลให้การแสดงตัวอย่างกล้องในรูปที่ 4 มีการวางแนวที่ถูกต้อง

เซ็นเซอร์กล้องหมุนเป็นแนวนอนโดยให้รูปภาพตั้งตรง
รูปที่ 7 บัฟเฟอร์รูปภาพหลังจากหมุนทวนเข็มนาฬิกาเพิ่มอีก 90 องศาสำหรับการวางแนวอุปกรณ์

ภาพนี้แสดงกล้องที่หมุนไปทางขวาเป็นแนวนอน

ทั้งตัวอย่างจากกล้องและเซ็นเซอร์อยู่ในแนวนอน แต่เซ็นเซอร์กลับหัว
รูปที่ 8 ตัวอย่างภาพจากกล้องและเซ็นเซอร์เมื่อหมุนโทรศัพท์ 270 องศา (หรือ -90 องศา) เป็นแนวนอน

นี่คือบัฟเฟอร์รูปภาพ

เซ็นเซอร์ตรวจจับผ่านกล้องหมุนเป็นแนวนอนโดยที่รูปภาพกลับหัว
รูปที่ 9 บัฟเฟอร์รูปภาพที่สร้างโดยเซ็นเซอร์กล้องหน้าเมื่อหมุนโทรศัพท์ทวนเข็มนาฬิกา 270 องศา

ต้องหมุนบัฟเฟอร์ 270 องศาทวนเข็มนาฬิกาเพื่อปรับการวางแนวเซ็นเซอร์ ดังนี้

เซ็นเซอร์กล้องหมุนเป็นแนวตั้งโดยมีรูปภาพอยู่ด้านข้าง ด้านซ้ายบน
รูปที่ 10 บัฟเฟอร์รูปภาพหมุนทวนเข็มนาฬิกา 270 องศาเพื่อปรับตามการวางแนวของเซ็นเซอร์

จากนั้นบัฟเฟอร์จะหมุนอีก 270 องศาทวนเข็มนาฬิกาเพื่อพิจารณาการหมุนของอุปกรณ์ ดังนี้

เซ็นเซอร์กล้องหมุนเป็นแนวนอนโดยให้รูปภาพตั้งตรง
รูปที่ 11 บัฟเฟอร์รูปภาพหลังจากหมุนทวนเข็มนาฬิกาอีก 270 องศาสำหรับการวางแนวอุปกรณ์

กล้องหลัง

โดยทั่วไปกล้องหลังจะมีการวางแนวเซ็นเซอร์ 90 องศา (เมื่อมองจากด้านหลังของอุปกรณ์) เมื่อปรับการวางแนวตัวอย่างภาพจากกล้อง บัฟเฟอร์รูปภาพของเซ็นเซอร์จะหมุนตามเข็มนาฬิกาตามจำนวนการหมุนของเซ็นเซอร์ (แทนที่จะหมุนทวนเข็มนาฬิกาเหมือนกล้องหน้า) จากนั้นบัฟเฟอร์รูปภาพจะหมุนทวนเข็มนาฬิกาตามจำนวนการหมุนของอุปกรณ์

ทั้งตัวอย่างจากกล้องและเซ็นเซอร์อยู่ในแนวนอน แต่เซ็นเซอร์กลับหัว
รูปที่ 12 โทรศัพท์ที่มีกล้องหลังในแนวนอน (หมุน 270 หรือ -90 องศา)

นี่คือบัฟเฟอร์รูปภาพจากเซ็นเซอร์ตรวจจับผ่านกล้องในรูปที่ 12

เซ็นเซอร์ตรวจจับผ่านกล้องหมุนเป็นแนวนอนโดยที่รูปภาพกลับหัว
รูปที่ 13 บัฟเฟอร์รูปภาพที่สร้างโดยเซ็นเซอร์กล้องหลังเมื่อโทรศัพท์อยู่ในแนวนอน

ต้องหมุนบัฟเฟอร์ 90 องศาตามเข็มนาฬิกาเพื่อปรับการวางแนวเซ็นเซอร์ ดังนี้

เซ็นเซอร์กล้องหมุนเป็นแนวตั้งโดยมีรูปภาพอยู่ด้านข้าง ด้านซ้ายบน
รูปที่ 14 บัฟเฟอร์รูปภาพหมุนตามเข็มนาฬิกา 90 องศาเพื่อปรับการวางแนวเซ็นเซอร์ด้านหลัง

จากนั้นบัฟเฟอร์จะหมุน 270 องศาทวนเข็มนาฬิกาเพื่อพิจารณาการหมุนของอุปกรณ์

เซ็นเซอร์กล้องหมุนเป็นแนวนอนโดยให้รูปภาพตั้งตรง
รูปที่ 15 บัฟเฟอร์รูปภาพหลังจากหมุนทวนเข็มนาฬิกา 270 องศาสำหรับการวางแนวอุปกรณ์

สัดส่วนภาพ

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

บัฟเฟอร์รูปภาพของเซ็นเซอร์กล้องต้องได้รับการปรับการวางแนวและปรับขนาดให้ตรงกับการวางแนวและสัดส่วนภาพขององค์ประกอบ UI ช่องมองภาพเมื่อ UI เปลี่ยนการวางแนวแบบไดนามิก ไม่ว่าอุปกรณ์จะเปลี่ยนการวางแนวหรือไม่ก็ตาม

ในรูปแบบของอุปกรณ์ใหม่ๆ หรือในสภาพแวดล้อมแบบหลายหน้าต่างหรือหลายจอแสดงผล หากแอปของคุณถือว่าตัวอย่างภาพจากกล้องมีการวางแนวเดียวกับอุปกรณ์ (แนวตั้งหรือแนวนอน) ตัวอย่างภาพอาจมีการวางแนวไม่ถูกต้อง ปรับขนาดไม่ถูกต้อง หรือทั้ง 2 อย่าง

อุปกรณ์แบบพับได้ที่กางออกพร้อมตัวอย่างกล้องแนวตั้งที่หมุนไปด้านข้าง
รูปที่ 16 อุปกรณ์แบบพับได้เปลี่ยนจากสัดส่วนภาพแนวตั้งเป็นแนวนอน แต่เซ็นเซอร์กล้องยังคงอยู่ในการวางแนวตั้ง

ในรูปที่ 16 แอปพลิเคชันเข้าใจผิดว่าอุปกรณ์หมุนทวนเข็มนาฬิกา 90 องศา แอปจึงหมุนตัวอย่างภาพในปริมาณเท่ากัน

อุปกรณ์แบบพับได้ที่กางออกโดยมีการแสดงตัวอย่างจากกล้องในแนวตั้ง แต่ถูกบีบเนื่องจากการปรับขนาดที่ไม่ถูกต้อง
รูปที่ 17 อุปกรณ์แบบพับได้เปลี่ยนจากสัดส่วนภาพแนวตั้งเป็นแนวนอนโดยมีตัวอย่างภาพจากกล้องที่ปรับขนาดไม่ถูกต้อง

ในรูปที่ 17 แอปไม่ได้ปรับสัดส่วนภาพของบัฟเฟอร์รูปภาพเพื่อให้ปรับขนาดให้พอดีกับขนาดใหม่ขององค์ประกอบ UI ตัวอย่างภาพจากกล้องได้อย่างเหมาะสม

แอปกล้องที่มีการวางแนวคงที่มักประสบปัญหาในอุปกรณ์แบบพับได้และอุปกรณ์หน้าจอขนาดใหญ่อื่นๆ เช่น แล็ปท็อป ดังนี้

ตัวอย่างจากกล้องในแล็ปท็อปอยู่ในแนวตั้ง แต่ UI ของแอปอยู่ในแนวนอน
รูปที่ 18 แอปแนวตั้งที่มีการวางแนวคงที่บนคอมพิวเตอร์แล็ปท็อป

ในรูปที่ 18 UI ของแอปกล้องจะอยู่ด้านข้างเนื่องจากการวางแนวของแอปถูกจำกัดให้เป็นแนวตั้งเท่านั้น รูปภาพในช่องมองภาพจะวางแนวอย่างถูกต้องเมื่อเทียบกับเซ็นเซอร์กล้อง

โหมดภาพบุคคลแบบแทรก

แอปกล้องที่ไม่รองรับโหมดหลายหน้าต่าง (resizeableActivity="false") และจำกัดการวางแนว (screenOrientation="portrait" หรือ screenOrientation="landscape") สามารถวางในโหมดภาพบุคคลแบบแทรกในอุปกรณ์หน้าจอขนาดใหญ่เพื่อปรับการวางแนวตัวอย่างภาพจากกล้องอย่างเหมาะสม

แทรก Letterbox ในโหมดภาพบุคคล (ส่วนที่เว้นไว้) แอปแนวตั้งเท่านั้นในแนวตั้ง แม้ว่าสัดส่วนการแสดงผลจะเป็นแนวนอนก็ตาม แอปที่แสดงเฉพาะแนวนอนจะแสดงแบบ Letterbox ในแนวนอนแม้ว่าสัดส่วนการแสดงผลจะเป็นแนวตั้งก็ตาม ระบบจะหมุนรูปภาพจากกล้องให้สอดคล้องกับ UI ของแอป ครอบตัดให้ตรงกับสัดส่วนภาพของตัวอย่างกล้อง แล้วปรับขนาดให้เต็มตัวอย่าง

ระบบจะทริกเกอร์โหมดภาพบุคคลแบบแทรกเมื่อสัดส่วนภาพของเซ็นเซอร์รูปภาพของกล้องและสัดส่วนภาพของกิจกรรมหลักของแอปพลิเคชันไม่ตรงกัน

ตัวอย่างจากกล้องและ UI ของแอปในแนวตั้งที่เหมาะสมบนแล็ปท็อป
            รูปภาพตัวอย่างแบบกว้างจะได้รับการปรับขนาดและครอบตัดให้พอดีกับการวางแนวตั้ง
รูปที่ 19 แอปแนวตั้งที่มีการวางแนวคงที่ในโหมดภาพบุคคลแบบแทรกในแล็ปท็อป

ในรูปที่ 19 แอปกล้องที่ใช้ได้เฉพาะแนวตั้งได้รับการหมุนเพื่อแสดง UI ในแนวตั้งบนจอแสดงผลของแล็ปท็อป แอปจะแสดงแบบ Letterbox เนื่องจากสัดส่วนภาพของแอปแนวตั้งและจอแสดงผลแนวนอนแตกต่างกัน ภาพตัวอย่างจากกล้องได้รับการหมุนเพื่อชดเชยการหมุน UI ของแอป (เนื่องจากโหมดภาพบุคคลแบบแทรก) และรูปภาพได้รับการครอบตัดและปรับขนาดให้พอดีกับการวางแนวตั้ง ซึ่งจะลดขอบเขตการมองเห็น

หมุน ครอบตัด ปรับขนาด

ระบบจะเรียกใช้โหมดภาพบุคคลแบบแทรกสำหรับแอปกล้องที่ใช้ได้เฉพาะโหมดภาพบุคคลบนจอแสดงผลที่มีสัดส่วนภาพแนวนอน ดังนี้

ตัวอย่างจากกล้องในแล็ปท็อปอยู่ในแนวตั้ง แต่ UI ของแอปอยู่ในแนวนอน
รูปที่ 20 แอปแนวตั้งที่มีการวางแนวคงที่ในแล็ปท็อป

แอปจะแสดงแบบ Letterbox ในแนวตั้ง

แอปหมุนเป็นแนวตั้งและแสดงผลแบบ Letterbox รูปภาพอยู่ด้านข้าง ด้านบนอยู่ทางขวา
รูปที่ 21 แอปแนวตั้งเท่านั้นที่หมุนและแสดงแบบ Letterbox บนจอแสดงผลแนวนอน ก่อนการหมุนรูปภาพเซ็นเซอร์

ภาพจากกล้องจะหมุน 90 องศาเพื่อปรับตามการปรับแนวของแอป

ภาพเซ็นเซอร์หมุน 90 องศาเพื่อให้ตั้งตรง
รูปที่ 22 รูปภาพจากกล้องหมุน 90 องศาเพื่อให้สอดคล้องกับ UI ของแอปที่ปรับการวางแนวใหม่

ระบบจะครอบตัดรูปภาพตามสัดส่วนภาพของตัวอย่างภาพจากกล้อง จากนั้นปรับขนาดให้เต็มตัวอย่าง (ขอบเขตการมองเห็นลดลง) ดังนี้

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

ในอุปกรณ์แบบพับได้ การวางแนวของเซ็นเซอร์กล้องอาจเป็นแนวตั้งขณะที่สัดส่วนภาพของจอแสดงผลเป็นแนวนอน

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

เนื่องจากตัวอย่างภาพจากกล้องหมุนเพื่อปรับตามการวางแนวเซ็นเซอร์ รูปภาพจึงวางแนวอย่างถูกต้องในช่องมองภาพ แต่แอปที่แสดงเฉพาะแนวตั้งจะแสดงเป็นแนวนอน

โหมดภาพบุคคลแบบแทรกต้องใช้ Letterbox กับแอปในแนวตั้งเท่านั้นเพื่อปรับแนวแอปและตัวอย่างภาพจากกล้องให้เหมาะสม

แอปแบบ Letterbox ในการวางแนวตั้งพร้อมตัวอย่างภาพจากกล้องในแนวตั้งบนอุปกรณ์แบบพับได้
รูปที่ 25 แอปแนวตั้งเท่านั้นที่แสดงแบบ Letterbox ในโหมดแนวตั้งแบบแทรกบนอุปกรณ์แบบพับได้ที่กางออก

API

ตั้งแต่ Android 12 (ระดับ API 31) เป็นต้นไป แอปยังควบคุมโหมดภาพบุคคลแบบแทรกได้อย่างชัดเจนโดยใช้พร็อพเพอร์ตี้ SCALER_ROTATE_AND_CROP ของคลาส CaptureRequest

ค่าเริ่มต้นคือ SCALER_ROTATE_AND_CROP_AUTO ซึ่งช่วยให้ระบบเรียกใช้โหมดภาพบุคคลแบบแทรกได้ SCALER_ROTATE_AND_CROP_90 คือลักษณะการทำงานของโหมดภาพบุคคลแบบแทรกตามที่อธิบายไว้ก่อนหน้านี้

อุปกรณ์บางรุ่นไม่รองรับค่า SCALER_ROTATE_AND_CROP ทั้งหมด หากต้องการดูรายการค่าที่รองรับ โปรดดู CameraCharacteristics#SCALER_AVAILABLE_ROTATE_AND_CROP_MODES

CameraX

ไลบรารี Jetpack CameraX ช่วยให้การสร้างช่องมองภาพของกล้องที่รองรับการวางแนวเซ็นเซอร์และการหมุนอุปกรณ์เป็นเรื่องง่าย

องค์ประกอบเลย์เอาต์ PreviewView จะสร้างตัวอย่างภาพจากกล้อง โดยปรับการวางแนวเซ็นเซอร์ การหมุนอุปกรณ์ และการปรับขนาดโดยอัตโนมัติ PreviewView จะรักษาสัดส่วนภาพของรูปภาพจากกล้องโดยใช้ประเภทการปรับขนาด FILL_CENTER ซึ่งจะจัดรูปภาพให้อยู่ตรงกลาง แต่อาจครอบตัดรูปภาพให้ตรงกับขนาดของ PreviewView หากต้องการแสดงภาพจากกล้องแบบ Letterbox ให้ตั้งค่าประเภทการปรับขนาดเป็น FIT_CENTER

หากต้องการดูข้อมูลเบื้องต้นเกี่ยวกับการสร้างตัวอย่างกล้องด้วย PreviewView โปรดดูหัวข้อใช้ตัวอย่าง

ดูตัวอย่างการติดตั้งใช้งานที่สมบูรณ์ได้ที่ที่เก็บ CameraXBasic ใน GitHub

CameraViewfinder

ไลบรารี CameraViewfinder มีชุดเครื่องมือที่ช่วยให้สร้างตัวอย่างภาพจากกล้องได้ง่ายขึ้น ซึ่งคล้ายกับกรณีการใช้งาน Preview โดยไม่ขึ้นอยู่กับ CameraX Core คุณจึงผสานรวมเข้ากับฐานของโค้ด Camera2 ที่มีอยู่ได้อย่างราบรื่น

คุณสามารถใช้วิดเจ็ต CameraViewfinder เพื่อแสดงฟีดกล้องสำหรับ Camera2 แทนการใช้ Surface โดยตรงได้

CameraViewfinder ใช้ TextureView หรือ SurfaceView ภายในเพื่อแสดงฟีดกล้อง และใช้การเปลี่ยนรูปแบบที่จำเป็นกับฟีดดังกล่าวเพื่อแสดงช่องมองภาพอย่างถูกต้อง ซึ่งรวมถึงการแก้ไขสัดส่วนภาพ ขนาด และการหมุน

หากต้องการขอ Surface จากออบเจ็กต์ CameraViewfinder คุณต้องสร้าง ViewfinderSurfaceRequest

คำขอนี้มีข้อกำหนดสำหรับความละเอียดของพื้นผิวและข้อมูลอุปกรณ์กล้องจาก CameraCharacteristics

การเรียก requestSurfaceAsync() จะส่งคำขอไปยังผู้ให้บริการแพลตฟอร์ม ซึ่งเป็น TextureView หรือ SurfaceView และได้รับ ListenableFuture ของ Surface

การเรียกใช้ markSurfaceSafeToRelease() จะแจ้งให้ผู้ให้บริการ Surface ทราบว่าไม่จำเป็นต้องใช้ Surface และสามารถปล่อยแหล่งข้อมูลที่เกี่ยวข้องได้

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

SurfaceView

SurfaceView เป็นวิธีที่ตรงไปตรงมาในการสร้างตัวอย่างภาพจากกล้องหากตัวอย่างภาพไม่จำเป็นต้องมีการประมวลผลและไม่มีภาพเคลื่อนไหว

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

คุณต้องตรวจสอบว่าสัดส่วนภาพของบัฟเฟอร์รูปภาพตรงกับสัดส่วนภาพของ SurfaceView ซึ่งทำได้โดยการปรับขนาดเนื้อหาของ SurfaceView ในเมธอด onMeasure() ของคอมโพเนนต์ ดังนี้

(ซอร์สโค้ด computeRelativeRotation() อยู่ในการหมุนสัมพัทธ์ด้านล่าง)

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

ดูรายละเอียดเพิ่มเติมเกี่ยวกับการใช้ SurfaceView เป็นตัวอย่างภาพจากกล้องได้ที่การวางแนวกล้อง

TextureView

TextureView มีประสิทธิภาพน้อยกว่า SurfaceView และต้องทำงานมากกว่า แต่ TextureView ช่วยให้คุณควบคุมตัวอย่างภาพจากกล้องได้สูงสุด

TextureView จะหมุนบัฟเฟอร์รูปภาพของเซ็นเซอร์ตามการวางแนวของเซ็นเซอร์ แต่จะไม่จัดการการหมุนอุปกรณ์หรือการปรับขนาดตัวอย่าง

การปรับขนาดและการหมุนสามารถเข้ารหัสได้ในการเปลี่ยนรูปแบบ Matrix ดูวิธีปรับขนาดและหมุน TextureView อย่างถูกต้องได้ที่รองรับแพลตฟอร์มที่ปรับขนาดได้ในแอปกล้อง

การหมุนแบบสัมพัทธ์

การหมุนสัมพัทธ์ของเซ็นเซอร์กล้องคือจำนวนการหมุนที่จำเป็นในการจัดแนวเอาต์พุตของเซ็นเซอร์กล้องให้สอดคล้องกับการวางแนวของอุปกรณ์

คอมโพเนนต์ต่างๆ เช่น SurfaceView และ TextureView จะใช้การหมุนสัมพัทธ์เพื่อกำหนดค่าตัวคูณมาตราส่วน x และ y สำหรับรูปภาพตัวอย่าง นอกจากนี้ยังใช้เพื่อระบุการหมุนของบัฟเฟอร์รูปภาพเซ็นเซอร์ด้วย

คลาส CameraCharacteristics และ Surface ช่วยให้คำนวณการหมุนสัมพัทธ์ของเซ็นเซอร์ตรวจจับผ่านกล้องได้

/**
 * 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) ?: 0

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

เมตริกหน้าต่าง

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

WindowManager#getCurrentWindowMetrics() (เพิ่มในระดับ API 30) จะแสดงขนาดของหน้าต่างแอปพลิเคชันแทนขนาดของหน้าจอ เมธอดไลบรารี Jetpack WindowManager WindowMetricsCalculator#computeCurrentWindowMetrics() และ WindowInfoTracker#currentWindowMetrics() ให้การรองรับที่คล้ายกันซึ่งมีความเข้ากันได้แบบย้อนหลังกับ API ระดับ 14

การหมุน 180 องศา

การหมุนอุปกรณ์ 180 องศา (เช่น จากการวางแนวปกติไปเป็นการวางแนวปกติแบบกลับหัว) จะไม่ทริกเกอร์ Callback onConfigurationChanged() ด้วยเหตุนี้ ตัวอย่างภาพจากกล้องจึงอาจกลับหัว

หากต้องการตรวจจับการหมุน 180 องศา ให้ใช้ DisplayListener และตรวจสอบการหมุนของอุปกรณ์ด้วยการเรียกใช้ Display#getRotation() ใน Callback onDisplayChanged()

แหล่งข้อมูลสุดพิเศษ

ก่อน Android 10 จะมีเพียงกิจกรรมที่มองเห็นได้ด้านบนสุดในสภาพแวดล้อมแบบหลายหน้าต่างเท่านั้นที่อยู่ในสถานะ RESUMED ซึ่งทำให้ผู้ใช้สับสนเนื่องจากระบบไม่ได้ระบุว่ากิจกรรมใดที่กลับมาทำงานต่อ

Android 10 (ระดับ API 29) เปิดตัวโหมดหลายหน้าต่างซึ่งกิจกรรมที่มองเห็นได้ทั้งหมดจะอยู่ในสถานะ RESUMED กิจกรรมที่มองเห็นได้ยังคงเข้าสู่สถานะ PAUSED ได้ เช่น หากกิจกรรมแบบโปร่งใสอยู่ด้านบนของกิจกรรมหรือกิจกรรมไม่สามารถโฟกัสได้ เช่น ในโหมดการแสดงภาพซ้อนภาพ (ดูการรองรับการแสดงภาพซ้อนภาพ)

แอปพลิเคชันที่ใช้กล้อง ไมโครโฟน หรือทรัพยากรแบบเอกสิทธิ์หรือซิงเกิลตันในระดับ API 29 ขึ้นไปต้องรองรับโหมดหลายหน้าต่าง ตัวอย่างเช่น หากกิจกรรมที่กลับมาทำงานอีกครั้ง 3 กิจกรรมต้องการใช้กล้อง จะมีเพียงกิจกรรมเดียวเท่านั้นที่เข้าถึงทรัพยากรพิเศษนี้ได้ กิจกรรมแต่ละรายการต้องใช้ Callback onDisconnected() เพื่อรับทราบการเข้าถึงกล้องล่วงหน้าโดยกิจกรรมที่มีลำดับความสำคัญสูงกว่า

ดูข้อมูลเพิ่มเติมได้ที่โหมดหลายหน้าต่าง

แหล่งข้อมูลเพิ่มเติม