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

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

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

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

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

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

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

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

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

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

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

หากต้องการแสดงการหมุนเซ็นเซอร์ต่อแอป Camera2 API จะมีค่าคงที่ 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 เปลี่ยนการวางแนวแบบไดนามิก ไม่ว่าจะมีการเปลี่ยนการวางแนวของอุปกรณ์หรือไม่ก็ตาม

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

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

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

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

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

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

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

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

โหมดภาพแนวตั้ง

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

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

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

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

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

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

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

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

แอปมีแถบดำด้านบน-ล่างของภาพในแนวตั้ง:

แอปหมุนไปเป็นแนวตั้งและมีแถบดำด้านบน-ล่างของภาพ ภาพคือ
            ตะแคงข้าง ไปด้านบนทางขวา

ระบบจะหมุนรูปภาพจากกล้อง 90 องศาเพื่อปรับการวางแนวแอปใหม่ โดยทำดังนี้

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

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

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

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

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

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

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

แอปที่มีแถบดำด้านบนและด้านล่างในแนวตั้งพร้อมการแสดงตัวอย่างจากกล้องในอุปกรณ์แบบพับได้

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 หากต้องการให้มีแถบดำด้านบน-ล่างของภาพในกล้อง ให้กำหนดประเภทมาตราส่วนเป็น FIT_CENTER

หากต้องการเรียนรู้พื้นฐานในการสร้างตัวอย่างจากกล้องด้วย PreviewView โปรดดู ใช้การแสดงตัวอย่าง

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

ช่องมองภาพของกล้อง

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

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

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

หากต้องการขอพื้นผิวจากวัตถุ CameraViewfinder คุณต้องสร้างขึ้นViewfinderSurfaceRequest

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

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

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

KotlinJava
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))
}
    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 เป็นแนวทางที่ตรงไปตรงมาในการสร้างตัวอย่างจากกล้องหากตัวอย่างนั้นไม่จำเป็นต้องประมวลผลและไม่ใช่ภาพเคลื่อนไหว

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

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

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

KotlinJava
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)
}
@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);
}

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

TextureView

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

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

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

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

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

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

CameraCharacteristics และ Surface ชั้นเรียนเปิดใช้การคํานวณ การหมุนสัมพัทธ์ของเซ็นเซอร์กล้อง:

KotlinJava
/**
 * 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
}
/**
 * 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;
}

เมตริกกรอบเวลา

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

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

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

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

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

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

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

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

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

สำหรับข้อมูลเพิ่มเติม โปรดดู เล่นต่อหลายรายการ

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