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

หมายเหตุ: หน้านี้อ้างอิงถึงแพ็กเกจ 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 บัฟเฟอร์รูปภาพที่เซ็นเซอร์กล้องสร้างขึ้นจะมีลักษณะดังนี้

เซ็นเซอร์กล้องหมุนเป็นแนวนอนโดยมีรูปภาพตะแคงข้างอยู่ด้านซ้ายบน

ต้องหมุนรูปภาพ 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() แสดงผลการหมุนตามเข็มนาฬิกาของอุปกรณ์ (จากมุมมองของผู้ใช้) ปฏิเสธค่าเพื่อใช้ในสูตรด้านบน

กล้องหน้า

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

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

เซ็นเซอร์ตรวจจับผ่านกล้องในแนวนอนพร้อมรูปภาพแนวตั้ง

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

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

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

เซ็นเซอร์กล้องหมุนเป็นแนวนอนโดยให้รูปภาพตั้งตรง

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

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

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

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

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

เซ็นเซอร์กล้องได้รับการจัดอันดับเป็นแนวตั้งโดยมีรูปภาพอยู่ด้านข้าง ซ้ายบน

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

เซ็นเซอร์กล้องหมุนเป็นแนวนอนโดยให้รูปภาพตั้งตรง

กล้องหลัง

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

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

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

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

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

เซ็นเซอร์กล้องได้รับการจัดอันดับเป็นแนวตั้งโดยมีรูปภาพอยู่ด้านข้าง ซ้ายบน

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

เซ็นเซอร์กล้องหมุนเป็นแนวนอนโดยให้รูปภาพตั้งตรง

สัดส่วนภาพ

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

ภาพเซ็นเซอร์หมุน 90 องศาเพื่อให้ตั้งตรง

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

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

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

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

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

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

แอปแบบ 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 ภายในเพื่อแสดงฟีดกล้อง และใช้การเปลี่ยนรูปแบบที่จำเป็นเพื่อแสดงช่องมองภาพอย่างถูกต้อง ซึ่งรวมถึงการแก้ไขสัดส่วนภาพ ขนาด และการหมุน

หากต้องการขอพื้นผิวจากออบเจ็กต์ 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() เพื่อรับทราบการเข้าถึงกล้องล่วงหน้าโดยกิจกรรมที่มีลำดับความสำคัญสูงกว่า

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

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