הערה: הדף הזה מתייחס לחבילה camera2. מומלץ להשתמש ב-CameraX, אלא אם לאפליקציה שלך נדרשים תכונות ספציפיות ברמה נמוכה. גם CameraX וגם Camera2 תומכים ב-Android 5.0 (רמת API 21) ואילך.
המצלמות ותצוגות מקדימות של מצלמות לא תמיד באותו כיוון ב-Android מכשירים.
מצלמה נמצאת במיקום קבוע במכשיר, לא משנה אם הוא הוא טלפון, טאבלט או מחשב. כשכיוון המכשיר משתנה, שינויים בכיוון המצלמה.
כתוצאה מכך, לאפליקציות מצלמה בדרך כלל יש קשר קבוע בין כיוון המכשיר ויחס הגובה-רוחב של התצוגה המקדימה של המצלמה. כאשר הטלפון בפריסה לאורך. ההנחה היא שהתצוגה המקדימה של המצלמה גבוהה יותר. מאשר שהוא רחב. כשמסובבים את הטלפון (והמצלמה) לרוחב, התצוגה המקדימה של המצלמה צפויה להיות רחבה יותר מהגובה.
אבל על ההנחות האלה עומדים גורמי צורה חדשים, כמו מכשירים מתקפלים מכשירים, ומצבי תצוגה כמו ריבוי חלונות וגם ריבוי מסכים. מכשירים מתקפלים משנים את גודל המסך ואת יחס הגובה-רוחב ללא שינוי לכיוון מסוים. מצב ריבוי חלונות מגביל את אפליקציות המצלמה לחלק לשנות את קנה המידה של התצוגה המקדימה של המצלמה ללא קשר לכיוון המכשיר. מצב תצוגה מרובת מסכים מאפשר להשתמש במסכים משניים, שייתכן שלא להיות באותו כיוון של המסך הראשי.
כיוון המצלמה
הגדרת תאימות ל-Android מציין שחיישן התמונה של המצלמה "חייב להיות מכוון כך מידות המצלמה מתאימות לממד הארוך של המסך. כלומר, כאשר כשמחזיקים את המכשיר לרוחב, המצלמות חייבות לצלם תמונות בכיוון לרוחב. בלי קשר למצב הרגיל של המכשיר כיוון; כלומר, היא חלה על מכשירים ראשיים בפריסה לרוחב וגם על מכשירים ראשיים בפורמט אנכי".
הסידור של המצלמה למסך מגדיל את אזור התצוגה של המצלמה העינית באפליקציית המצלמה. כמו כן, חיישני תמונה בדרך כלל פלט את הנתונים שלהם ביחס גובה-רוחב של לרוחב, 4:3 הוא הנפוץ ביותר.
![חיישן הטלפון וחיישן המצלמה בתצוגה לאורך.](https://developer.android.com/static/images/training/camera/camera2/camera-preview/camera_preview_portrait_front_facing.png?authuser=8&hl=he)
הכיוון הטבעי של חיישן המצלמה הוא לרוחב. באיור 1, החיישן של המצלמה הקדמית (המצלמה פונה באותו כיוון כמו מסך) מסובב ב-270 מעלות ביחס לטלפון כדי לעמוד הגדרת תאימות ל-Android.
כדי לחשוף את הסיבוב של החיישן לאפליקציות,
ה-API של camera2 כולל
SENSOR_ORIENTATION
באופן קבוע. ברוב הטלפונים והטאבלטים, המכשיר מדווח על כיוון החיישן
של 270 מעלות למצלמות קדמיות ו-90 מעלות (נקודת מבט
גב המכשיר) למצלמות אחוריות, שמיישרות את הקצה הארוך של
עם הקצה הארוך של המכשיר. מצלמות של מחשבים ניידים בדרך כלל מדווחות
כיוון החיישן של 0 או 180 מעלות.
מאחר שחיישני התמונה של המצלמה פלטים את הנתונים שלהם (מאגר אחסון של תמונות)
בכיוון הטבעי של החיישן (לרוחב), צריך לסובב את מאגר הנתונים הזמני
מספר המעלות שצוין על ידי SENSOR_ORIENTATION
לתצוגה המקדימה של המצלמה בשביל
יופיעו בצורה זקופה בכיוון הטבעי של המכשיר. במצלמות קדמיות:
הסיבוב הוא נגד כיוון השעון, למצלמות אחוריות, בכיוון השעון.
לדוגמה, עבור המצלמה הקדמית באיור 1, מאגר הנתונים הזמני של התמונה שמופק על ידי חיישן המצלמה נראה כך:
![חיישן המצלמה סובב לרוחב עם תמונה
שמאלה, למעלה משמאל.](https://developer.android.com/static/images/training/camera/camera2/camera-preview/camera_sensor_portrait_rotated_90.png?authuser=8&hl=he)
חובה לסובב את התמונה ב-270 מעלות נגד כיוון השעון כדי שהתצוגה המקדימה הכיוון תואם לכיוון המכשיר:
![חיישן מצלמה בכיוון לאורך כשהתמונה זקופה.](https://developer.android.com/static/images/training/camera/camera2/camera-preview/camera_sensor_portrait_without_coordinates.png?authuser=8&hl=he)
מצלמה אחורית תפיק מאגר תמונות באותו כיוון
כמו מאגר הנתונים הזמני שלמעלה, אבל הטמפרטורה של 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()
מחזירה את סיבוב המכשיר בכיוון השעון (מנקודת המבט של המשתמש). מבטלים את הערך לשימוש בנוסחה שלמעלה.
מצלמות קדמיות
![התצוגה המקדימה של המצלמה והחיישן הן בפריסה לרוחב, בחיישן
נמצא מימין למעלה.](https://developer.android.com/static/images/training/camera/camera2/camera-preview/camera_preview_landscape_counterclockwise_front_facing.png?authuser=8&hl=he)
הנה מאגר הנתונים הזמני שנוצר על ידי חיישן המצלמה באיור 2:
![חיישן מצלמה בכיוון לרוחב עם תמונה זקופה.](https://developer.android.com/static/images/training/camera/camera2/camera-preview/camera_sensor_landscape.png?authuser=8&hl=he)
צריך לסובב את מאגר הנתונים הזמני ב-270 מעלות נגד כיוון השעון כדי להתאים את החיישן כיוון (ראו כיוון המצלמה למעלה):
![חיישן המצלמה מסובב למצב אנכי כשהתמונה לא ישרה,
בפינה הימנית העליונה.](https://developer.android.com/static/images/training/camera/camera2/camera-preview/camera_sensor_landscape_rotated_270.png?authuser=8&hl=he)
לאחר מכן מתבצע סיבוב של מאגר הנתונים הזמני ב-90 מעלות נוספות נגד כיוון השעון חשבון לסיבוב מכשירים, וכתוצאה מכך לכיוון הנכון של תצוגה מקדימה של המצלמה באיור 2:
![חיישן המצלמה סובב לרוחב עם תמונה
זקוף.](https://developer.android.com/static/images/training/camera/camera2/camera-preview/camera_sensor_landscape_without_coordinates.png?authuser=8&hl=he)
הנה המצלמה שפונה ימינה לרוחב:
![התצוגה המקדימה של המצלמה והחיישן גם בפריסה לרוחב, אבל
החיישן הפוך.](https://developer.android.com/static/images/training/camera/camera2/camera-preview/camera_preview_landscape_clockwise_front_facing.png?authuser=8&hl=he)
הנה מאגר הנתונים הזמני:
![חיישן המצלמה מסובב לרוחב עם התמונה הפוך
למטה.](https://developer.android.com/static/images/training/camera/camera2/camera-preview/camera_sensor_landscape_clockwise.png?authuser=8&hl=he)
כדי להתאים את החיישן, צריך לסובב את מאגר הנתונים הזמני ב-270 מעלות נגד כיוון השעון כיוון:
![חיישן מצלמה בדירוג לאורך עם תמונה הצידה,
בפינה השמאלית העליונה.](https://developer.android.com/static/images/training/camera/camera2/camera-preview/camera_sensor_landscape_clockwise_rotated_270.png?authuser=8&hl=he)
לאחר מכן מתבצע סיבוב של מאגר הנתונים הזמני ב-270 מעלות נוספות נגד כיוון השעון, כדי להביא בחשבון סיבוב המכשירים:
![חיישן המצלמה סובב לרוחב עם תמונה
זקוף.](https://developer.android.com/static/images/training/camera/camera2/camera-preview/camera_sensor_landscape_without_coordinates.png?authuser=8&hl=he)
מצלמות אחוריות
למצלמות אחוריות יש בדרך כלל כיוון חיישן של 90 מעלות מוצגת בגב המכשיר). כשמסובבים את התצוגה המקדימה של המצלמה, מאגר התמונות של החיישן מסובב בכיוון השעון לפי מידת הסיבוב של החיישן (במקום נגד כיוון השעון, למשל מצלמות קדמיות), ואז התמונה מתבצע סיבוב של מאגר הנתונים הזמני נגד כיוון השעון בהתאם לסיבוב של המכשיר.
![התצוגה המקדימה של המצלמה והחיישן גם בפריסה לרוחב, אבל
החיישן הפוך.](https://developer.android.com/static/images/training/camera/camera2/camera-preview/camera_preview_landscape_clockwise_back_facing.png?authuser=8&hl=he)
הנה מאגר הנתונים הזמני מחיישן המצלמה באיור 4:
![חיישן המצלמה מסובב לרוחב עם התמונה הפוך
למטה.](https://developer.android.com/static/images/training/camera/camera2/camera-preview/camera_sensor_landscape_clockwise.png?authuser=8&hl=he)
כדי להתאים את החיישן, צריך לסובב את מאגר הנתונים הזמני ב-90 מעלות בכיוון השעון כיוון:
![חיישן מצלמה בדירוג לאורך עם תמונה הצידה,
בפינה השמאלית העליונה.](https://developer.android.com/static/images/training/camera/camera2/camera-preview/camera_sensor_landscape_clockwise_rotated_270.png?authuser=8&hl=he)
לאחר מכן מתבצע סיבוב של מאגר הנתונים הזמני ב-270 מעלות נגד כיוון השעון כדי להביא בחשבון את המכשיר סבב:
![חיישן המצלמה סובב לרוחב עם תמונה
זקוף.](https://developer.android.com/static/images/training/camera/camera2/camera-preview/camera_sensor_landscape_without_coordinates.png?authuser=8&hl=he)
יחס גובה-רוחב
יחס הגובה-רוחב של התצוגה משתנה כשכיוון המכשיר משתנה, אבל גם כאשר מכשירים מתקפלים מתקפלים שנפתחים ומשנים את גודל החלונות בסביבות עסקיות וכשהאפליקציות נפתחות במסכים משניים.
יש לכוון את מאגר התמונות של חיישן המצלמה ולשנות את הגודל שלו כך שיתאים ל הכיוון ויחס הגובה-רוחב של רכיב ממשק המשתמש של העינית כממשק המשתמש משנה באופן דינמי את הכיוון — גם אם המכשיר משתנה או בלעדיו לכיוון מסוים.
בגורמי צורה חדשים או בסביבות של ריבוי חלונות או מסכים מרובים, ההנחה היא שהתצוגה המקדימה של המצלמה זהה לכיוון של המכשיר (לאורך או לרוחב) יכול להיות שהתצוגה המקדימה שלה תהיה שגויה, בטעות, או גם וגם.
![מכשיר מתקפל לא מקופל עם תצוגה מקדימה במצלמה לאורך
בכיוון הנכון.](https://developer.android.com/static/images/training/camera/camera2/camera-preview/foldable_with_rotation_problem.png?authuser=8&hl=he)
באיור 5, האפליקציה הניחה בטעות שהמכשיר הוחלף ב-90 מעלות נגד כיוון השעון; וכך, האפליקציה ביצעה סבב של התצוגה המקדימה באותו משך.
![מכשיר מתקפל לא מקופל עם תצוגה מקדימה של המצלמה זקוף, אבל דחוס
בגלל שינוי קנה מידה שגוי.](https://developer.android.com/static/images/training/camera/camera2/camera-preview/foldable_with_scaling_problem.png?authuser=8&hl=he)
באיור 6, האפליקציה לא שינתה את יחס הגובה-רוחב של מאגר התמונות לאפשר לו להתאים את גודלו למידות החדשות של ממשק המשתמש של התצוגה המקדימה של המצלמה לרכיב מסוים.
באפליקציות של מצלמה עם כיוון קבוע יש בדרך כלל בעיות במכשירים מתקפלים במכשירים אחרים עם מסך גדול, כמו מחשבים ניידים:
![התצוגה המקדימה של המצלמה במחשב הנייד זקפה, אבל ממשק המשתמש של האפליקציה לא יציב.](https://developer.android.com/static/images/training/camera/camera2/camera-preview/laptop_camera_preview_fixed-orientation_app.png?authuser=8&hl=he)
באיור 7, ממשק המשתמש של אפליקציית המצלמה נוטה הצידה מפני שכיוון האפליקציה מוגבל לתצוגה לאורך בלבד. התמונה בעינית מופנית כמו שצריך ביחס לחיישן המצלמה.
הגדרה לאורך
אפליקציות מצלמה שלא תומכות במצב ריבוי חלונות
(resizeableActivity="false"
)
ולהגביל את הכיוון שלהם
(screenOrientation="portrait"
)
או screenOrientation="landscape"
)
יכול להיות ממוקם בפריסה לאורך במכשירים עם מסך גדול כדי לכוון אותו כמו שצריך
את התצוגה המקדימה של המצלמה.
אפליקציות בפריסה לאורך בלבד בפריסה לאורך בלבד (בפריסה לאורך) למרות שיחס הגובה-רוחב של המסך הוא לרוחב. אפליקציות לרוחב בלבד יופיעו בפורמט letterbox לרוחב, גם אם יחס הגובה-רוחב של המסך הוא לאורך. תמונת המצלמה מסובבת כדי ליישר בממשק המשתמש של האפליקציה, נחתכים כדי להתאים ליחס הגובה-רוחב של התצוגה המקדימה של המצלמה ואז משתנה כדי למלא את התצוגה המקדימה.
האפשרות 'הדגשת דיוקן' מופעלת כאשר יחס הגובה-רוחב של תמונת המצלמה מופעל אין התאמה בין החיישן לבין יחס הגובה-רוחב של הפעילות העיקרית של האפליקציה.
![התצוגה המקדימה של המצלמה וממשק המשתמש של האפליקציה בפריסה לאורך מתאים במחשב נייד.
תמונת התצוגה המקדימה הרחבה הותאמה ונחתכה להתאמה לאורך
לכיוון מסוים.](https://developer.android.com/static/images/training/camera/camera2/camera-preview/laptop_inset_portrait_mode.png?authuser=8&hl=he)
באיור 8, אפליקציית המצלמה בפריסה לאורך בלבד סובבה כדי להציג את ממשק המשתמש במסך של המחשב הנייד. האפליקציה מוצגת בפורמט letterbox בגלל ההבדל ביחס גובה-רוחב בין האפליקציה בפריסה לאורך לבין התצוגה לרוחב. המצלמה תמונת התצוגה המקדימה סובבה כדי לפצות על הסיבוב של ממשק המשתמש של האפליקציה (עקב לאורך) בתמונה שנחתכה והותאמה כיוון לאורך, צמצום שדה הראייה.
סיבוב, חיתוך, שינוי קנה מידה
תצוגת דיוקן מופעל באפליקציית מצלמה בפריסה לאורך בלבד במסך שיש לו יחס גובה-רוחב לרוחב:
![התצוגה המקדימה של המצלמה במחשב הנייד זקפה, אבל ממשק המשתמש של האפליקציה לא יציב.](https://developer.android.com/static/images/training/camera/camera2/camera-preview/laptop_inset_portrait_mode_fixed-orientation_app.png?authuser=8&hl=he)
האפליקציה מוצגת בפורמט letterbox לאורך:
![האפליקציה סובבה לאורך ולפורמט letterbox. התמונה היא
הצידה, למעלה מימין.](https://developer.android.com/static/images/training/camera/camera2/camera-preview/laptop_inset_portrait_mode_rotate_ui.png?authuser=8&hl=he)
תמונת המצלמה מסובבת ב-90 מעלות כדי לכוונן את הכיוון מחדש של app:
![תמונת החיישן סובבה ב-90 מעלות כדי להפוך אותה למצב זקוף.](https://developer.android.com/static/images/training/camera/camera2/camera-preview/laptop_inset_portrait_mode_rotate_sensor_image.png?authuser=8&hl=he)
התמונה נחתכת לפי יחס הגובה-רוחב של התצוגה המקדימה של המצלמה, ואז משתנה למלא את התצוגה המקדימה (שדה הראייה מוקטן):
![תמונת המצלמה חתוכה מותאמת לגודל התצוגה המקדימה של המצלמה.](https://developer.android.com/static/images/training/camera/camera2/camera-preview/laptop_inset_portrait_mode_crop_and_scale.png?authuser=8&hl=he)
במכשירים מתקפלים, הכיוון של חיישן המצלמה יכול להיות לאורך ואילו יחס הגובה-רוחב של המסך הוא לרוחב:
![התצוגה המקדימה של המצלמה וממשק המשתמש של האפליקציה פונים הצידה של תצוגה רחבה ולא מקופלת.](https://developer.android.com/static/images/training/camera/camera2/camera-preview/unfolded_phone_camera_preview_fixed-orientation_app.png?authuser=8&hl=he)
מכיוון שהתצוגה המקדימה של המצלמה מסובבת כדי להתאים את כיוון החיישן, התמונה מוצגת כמו שצריך בעינית, אבל באפליקציה בפריסה לאורך בלבד נמצא בכיוון הנכון.
הגדרה של 'הדגשת דיוקן' נדרשת רק בפורמט letterbox בפריסה לאורך כדי לכוון כראוי את התצוגה המקדימה של האפליקציה והמצלמה:
![אפליקציה בפורמט letterbox לאורך עם תצוגה מקדימה של המצלמה
במכשיר מתקפל.](https://developer.android.com/static/images/training/camera/camera2/camera-preview/unfolded_phone_inset_portrait_mode.png?authuser=8&hl=he)
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
מצלמהX
ספריית Jetpack CameraX מאפשרת ליצור עינית מצלמה שמתאימה לכיוון החיישן "להחליף" בין מכשירים במשימה פשוטה.
רכיב הפריסה PreviewView
יוצר תצוגה מקדימה של המצלמה, שמתכוונן באופן אוטומטי לכיוון החיישן
סיבוב מכשירים והתאמה לעומס (scaling). PreviewView
שומר על יחס הגובה-רוחב של
בתמונה מהמצלמה על ידי החלת
FILL_CENTER
סוג קנה מידה, שמרכז את התמונה, אבל עשוי לחתוך אותה כדי להתאים למידות
של PreviewView
. כדי לסמן את תמונת המצלמה בפורמט letterbox, יש להגדיר את סוג קנה המידה כ-
FIT_CENTER
אפשר לקרוא מידע בסיסי על יצירת תצוגה מקדימה של המצלמה באמצעות PreviewView
:
מטמיעים תצוגה מקדימה.
כדי לקבל הטמעה מלאה לדוגמה, אפשר לעיין
CameraXBasic
ב-GitHub.
עינית המצלמה
בדומה לתרחיש לדוגמה של Preview, העינית עם המצלמה הספרייה מספקת ערכת כלים שנועדו לפשט את תהליך היצירה של תצוגה מקדימה למצלמה. היא לא תלויה ב-CarX Core, כך שאפשר לשלב אותו בצורה חלקה Codebase קיים של Camera2.
במקום להשתמש
Surface
ישירות, אפשר להשתמש
CameraViewfinder
ווידג'ט להצגת פיד המצלמה עבור Camera2.
CameraViewfinder
משתמש באופן פנימי ב-TextureView
או ב-SurfaceView
להציג את פיד המצלמה, ולהחיל עליו את הטרנספורמציות הנדרשות
להציג בצורה נכונה את העינית.
לשם כך צריך לתקן את יחס הגובה-רוחב, קנה המידה והסיבוב.
כדי לבקש את פני השטח מהאובייקט CameraViewfinder
, צריך
יוצרים ViewfinderSurfaceRequest
.
הבקשה הזו כוללת דרישות לגבי רזולוציית המשטח ומכשיר המצלמה
מידע מ-CameraCharacteristics
.
אנחנו מתקשרים אל requestSurfaceAsync()
שולחת את הבקשה לספק הפלטפורמה, שהוא TextureView
או
SurfaceView
ומקבלים ListenableFuture
של Surface
.
אנחנו מתקשרים אל markSurfaceSafeToRelease()
מודיע לספק הפלטפורמה שהפלטפורמה לא נחוצה,
יכול להיות משוחרר.
Kotlin
fun startCamera(){ val previewResolution = Size(width, height) val viewfinderSurfaceRequest = ViewfinderSurfaceRequest(previewResolution, characteristics) val surfaceListenableFuture = cameraViewfinder.requestSurfaceAsync(viewfinderSurfaceRequest) Futures.addCallback(surfaceListenableFuture, object : FutureCallbackS<urface >{ override fun onSuccess(surface: Surface) { /* create a CaptureSession using this surface as usual */ } override fun onFailure(t: Throwable) { /* something went wrong */} }, ContextCompat.getMainExecutor(context)) }
Java
void startCamera(){ Size previewResolution = new Size(width, height); ViewfinderSurfaceRequest viewfinderSurfaceRequest = new ViewfinderSurfaceRequest(previewResolution, characteristics); ListenableFutureS<urface >surfaceListenableFuture = cameraViewfinder.requestSurfaceAsync(viewfinderSurfaceRequest); Futures.addCallback(surfaceListenableFuture, new FutureCallbackS<urface(>) { @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)); }
תצוגת Surface
SurfaceView
הוא
גישה ישירה ליצירת תצוגה מקדימה של המצלמה, אם התצוגה המקדימה לא
דורשים עיבוד וללא אנימציה.
SurfaceView
מסובב באופן אוטומטי את מאגר התמונות של חיישן המצלמה בהתאם
של כיוון המסך, תוך התייחסות גם לכיוון החיישן וגם למכשיר
בסבב. עם זאת, מאגר הנתונים הזמני של התמונות מותאם ל-SurfaceView
ללא התייחסות ליחס הגובה-רוחב.
צריך לוודא שיחס הגובה-רוחב של מאגר הנתונים הזמני של התמונות תואם ליחס הגובה-רוחב
של SurfaceView
. אפשר לעשות זאת על ידי שינוי התוכן
של SurfaceView
onMeasure()
method:
(קוד המקור computeRelativeRotation()
הוא
סבב יחסי למטה.)
Kotlin
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) }
Java
@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
פחות טובים מ-
SurfaceView
ועוד עבודה, אבל TextureView
מספק מקסימום
התצוגה המקדימה של המצלמה.
TextureView
מסובב את מאגר התמונות של החיישן על סמך כיוון החיישן, אבל
לא מטפל בסיבוב המכשיר או בתצוגה מקדימה של קנה מידה.
ניתן לקודד קנה מידה וסיבוב
טרנספורמציה של Matrix. כדי ללמוד איך
ולסובב את TextureView
בצורה נכונה,
תמיכה בפלטפורמות שניתן לשנות את הגודל שלהן באפליקציית המצלמה
רוטציה יחסית
הסיבוב היחסי של חיישן המצלמה הוא כמות הסיבוב הדרושה התאימו את הפלט של חיישן המצלמה לכיוון המכשיר.
הרוטציה היחסית משמשת לפי רכיבים כמו SurfaceView
ו-TextureView
כדי לקבוע את הגורמים של קנה המידה של x ו-y עבור תמונת התצוגה המקדימה. הוא משמש גם
לציין את הסיבוב של מאגר התמונות של החיישן.
CameraCharacteristics
וגם
Surface
מחלקות מאפשרות לחשב
הסיבוב היחסי של חיישן המצלמה:
Kotlin
/** * 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 }
Java
/** * 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()
קריאה חוזרת.
מקורות מידע בלעדיים
לפני Android 10, רק הפעילות בחלק העליון ביותר גלויה בחלון מרובים
הסביבה הייתה RESUMED
. זה היה מבלבל את המשתמשים,
המערכת לא ציינה אילו פעילויות התחדשו.
ב-Android 10 (רמת API 29) נוספה אפשרות קורות חיים מרובות שבהן כל הפעילויות הגלויות
נמצאים במצב RESUMED
. פעילויות גלויות עדיין יכולות להיכנס אל PAUSED
מציין אם, לדוגמה, מופיעה פעילות שקופה שקשורה לפעילות או
לא ניתן להתמקד בפעילות הזו, למשל במצב 'תמונה בתוך תמונה'
תמיכה במצב 'תמונה בתוך תמונה').
אפליקציה שמשתמשת במצלמה, במיקרופון או בכל אפליקציה בלעדית אחרת או
משאב singleton ברמת API 29 ומעלה חייב לתמוך בריבוי קורות חיים. עבור
לדוגמה, אם שלוש פעילויות מתחדשות רוצים להשתמש במצלמה, רק אחת מהן יכולה
כדי לגשת למשאב הבלעדי הזה. כל פעילות צריכה לכלול
onDisconnected()
קריאה חוזרת (callback) כדי לשמור על גישה מונעת למצלמה בעדיפות גבוהה יותר
פעילות.
מידע נוסף זמין במאמר הבא: ריבוי קורות חיים.
מקורות מידע נוספים
- לצפייה בדוגמה של Camera2, ב-אפליקציית Camera2Basic ב-GitHub.
- מידע נוסף על התרחיש לדוגמה של התצוגה המקדימה של CameraX זמין ב- CameraX להטמיע תצוגה מקדימה.
- כדי לראות תצוגה מקדימה לדוגמה של מצלמת CameraX, אפשר לעיין בקטע מצלמהXBasic ב-GitHub.
- למידע נוסף על התצוגה המקדימה של המצלמה ב-ChromeOS: כיוונים במצלמה.
- מידע נוסף על פיתוח למכשירים מתקפלים זמין בכתובת מידע על מכשירים מתקפלים