หมายเหตุ: หน้านี้กล่าวถึงแพ็กเกจ Camera2 เราขอแนะนำให้ใช้ CameraX เว้นแต่ว่าแอปของคุณต้องใช้ฟีเจอร์ระดับต่ำที่เฉพาะเจาะจงจาก Camera2 ทั้ง CameraX และ Camera2 รองรับ Android 5.0 (API ระดับ 21) ขึ้นไป
กล้องและตัวอย่างจากกล้องอาจไม่ได้อยู่ในการวางแนวเดียวกันเสมอไปบนอุปกรณ์ Android
กล้องจะมีตำแหน่งคงที่ในอุปกรณ์ ไม่ว่าอุปกรณ์จะ คือโทรศัพท์ แท็บเล็ต หรือคอมพิวเตอร์ เมื่อการวางแนวของอุปกรณ์เปลี่ยนแปลง การวางแนวของกล้องก็จะเปลี่ยนแปลงด้วย
ด้วยเหตุนี้ โดยทั่วไปแอปกล้องจะสันนิษฐานว่าการวางแนวของอุปกรณ์กับสัดส่วนภาพพรีวิวของกล้องมีความสัมพันธ์กันแบบตายตัว เมื่อโทรศัพท์อยู่ในแนวตั้ง ระบบจะถือว่าภาพพรีวิวของกล้องมีความสูงมากกว่าความกว้าง เมื่อหมุนโทรศัพท์ (และกล้อง) เป็นแนวนอน การแสดงตัวอย่างจากกล้องควรจะกว้างกว่าความสูง
แต่สมมติฐานเหล่านี้อาจใช้ไม่ได้กับรูปแบบของอุปกรณ์ใหม่ๆ เช่น อุปกรณ์แบบพับได้ และโหมดการแสดงผล เช่น แบบหลายหน้าต่างและหลายจอแสดงผล อุปกรณ์แบบพับได้จะเปลี่ยนขนาดการแสดงผลและสัดส่วนภาพโดยไม่ต้องเปลี่ยนการวางแนว โหมดหลายหน้าต่างจะจำกัดแอปกล้องให้เป็นส่วนหนึ่งของ ปรับขนาดภาพตัวอย่างจากกล้องโดยไม่คำนึงถึงการวางแนวของอุปกรณ์ โหมดหลายจอแสดงผลทำให้สามารถใช้จอแสดงผลสำรองที่อาจไม่ เป็นแนวเดียวกับจอแสดงผลหลัก
การวางแนวของกล้อง
คำจำกัดความความเข้ากันได้ของ Android ระบุว่าเซ็นเซอร์รูปภาพของกล้อง "ต้องวางแนวเพื่อให้ขนาดยาวของกล้องสอดคล้องกับขนาดยาวของหน้าจอ นั่นคือเมื่อ ถืออุปกรณ์ในแนวนอน กล้องต้องจับภาพ การวางแนวแนวนอน การดำเนินการนี้จะมีผลเสมอไม่ว่าอุปกรณ์จะ การวางแนว กล่าวคือ จะใช้กับอุปกรณ์หลักแนวนอน อุปกรณ์หลักในแนวตั้ง"
การจัดเรียงกล้องให้เข้ากับหน้าจอจะขยายพื้นที่แสดงผลของกล้องให้ได้มากที่สุด ช่องมองภาพในแอปกล้อง นอกจากนี้ เซ็นเซอร์ภาพมักจะส่งออกข้อมูล สัดส่วนภาพแนวนอนที่ 4:3 เป็นสิ่งที่ใช้กันมากที่สุด

การวางแนวตามธรรมชาติของเซ็นเซอร์กล้องจะเป็นแนวนอน ในรูปที่ 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 มีดังนี้

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

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

กล้องหันไปทางขวาเป็นแนวนอน

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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()
จะแจ้งให้ผู้ให้บริการแพลตฟอร์มทราบว่าไม่จำเป็นต้องใช้แพลตฟอร์มดังกล่าวและสามารถปล่อยทรัพยากรที่เกี่ยวข้องได้
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()
อยู่ในการหมุนแบบสัมพัทธ์ด้านล่าง)
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
ชั้นเรียนเปิดใช้การคํานวณ
การหมุนสัมพัทธ์ของเซ็นเซอร์กล้อง:
/** * 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 เพื่อรับทราบสิทธิ์ล่วงหน้าในการเข้าถึงกล้องตามลำดับความสำคัญที่สูงกว่า
กิจกรรม
สำหรับข้อมูลเพิ่มเติม โปรดดู เล่นต่อหลายรายการ
แหล่งข้อมูลเพิ่มเติม
- ดูตัวอย่าง Camera2 ได้ที่แอป Camera2Basic ใน GitHub
- ดูข้อมูลเกี่ยวกับกรณีการใช้งานตัวอย่างจากกล้อง CameraX ได้ที่ CameraX ใช้การแสดงตัวอย่าง
- ดูตัวอย่างการใช้งานการแสดงตัวอย่างจากกล้อง CameraX ได้ที่ที่เก็บข้อมูล CameraXBasic ใน GitHub
- ดูข้อมูลเกี่ยวกับการแสดงตัวอย่างจากกล้องใน ChromeOS ได้ที่ การวางแนวกล้อง
- ดูข้อมูลเกี่ยวกับการพัฒนาแอปสำหรับอุปกรณ์แบบพับได้ที่หัวข้อดูข้อมูลเกี่ยวกับอุปกรณ์แบบพับ