באמצעות Jetpack Compose for XR, אפשר ליצור באופן דקלרטיבי את הפריסה ואת ממשק המשתמש המרחבי באמצעות מושגים מוכרים של Compose, כמו שורות ועמודות. כך תוכלו להרחיב את ממשק המשתמש הקיים של Android למרחב תלת-ממדי או ליצור אפליקציות תלת-ממדיות חדשות לגמרי.
אם אתם מפתחים אפליקציה קיימת מבוססת-תצוגות של Android, יש לכם כמה אפשרויות פיתוח. אפשר להשתמש בממשקי API לתאימות הדדית, להשתמש ב-Compose וב-Views יחד או לעבוד ישירות עם ספריית SceneCore. מידע נוסף זמין במדריך לעבודה עם תצוגות.
מידע על מרחבים משניים ורכיבים במרחב
כשכותבים אפליקציה ל-Android XR, חשוב להבין את המושגים מרחב משנה ורכיבים מרחבי.
מידע על מרחב משנה
כשמפתחים ל-Android XR, צריך להוסיף מרחב משנה לאפליקציה או לפריסה. מרחב משנה הוא מחיצה של מרחב תלת-ממדי באפליקציה, שבה אפשר למקם תוכן תלת-ממדי, ליצור פריסות תלת-ממדיות ולהוסיף עומק לתוכן דו-ממדי. המרחב המשני ימומש רק כשהמיקום במרחב יהיה מופעל. במרחב הבית או במכשירים שאינם XR, המערכת מתעלמת מכל קוד במרחב המשנה הזה.
יש שתי דרכים ליצור מרחב משנה:
setSubspaceContent()
: הפונקציה הזו יוצרת מרחב משנה ברמת האפליקציה. אפשר להפעיל את הפונקציה הזו בפעילות הראשית באותו אופן שבו משתמשים ב-setContent()
. המרחב המשנה ברמת האפליקציה לא מוגבל לגובה, לרוחב ולעומק, ובעצם מספק קנבס אינסופי לתוכן מרחבי.Subspace
: אפשר למקם את הרכיב הזה בכל מקום בהיררכיית ממשק המשתמש של האפליקציה, וכך לשמור על פריסות לממשק משתמש דו-ממדי ומרחבי בלי לאבד את ההקשר בין הקבצים. כך קל יותר לשתף דברים כמו ארכיטקטורת אפליקציה קיימת בין XR לבין גורמי צורה אחרים, בלי שתצטרכו להעביר את המצב דרך כל עץ ממשק המשתמש או לתכנן מחדש את האפליקציה.
מידע נוסף זמין במאמר הוספת מרחב משנה לאפליקציה.
מידע על רכיבים במרחב
רכיבים שניתנים ליצירה במרחב משנה: אפשר להציג את הרכיבים האלה רק במרחב משנה.
צריך להוסיף את התגים האלה בתוך Subspace
או setSubspaceContent
לפני שממקמים אותם בפריסה דו-ממדית. SubspaceModifier
מאפשר לכם להוסיף מאפיינים כמו עומק, הזזה ומיקום לרכיבים הניתנים לשילוב במרחב המשנה.
רכיבים אחרים במרחב לא מחייבים קריאה בתוך מרחבים משניים. הם מורכבים מרכיבים דו-ממדיים רגילים שמקובצים בתוך מאגר מרחבי. אפשר להשתמש ברכיבים האלה בפריסות דו-ממדיות או תלת-ממדיות, אם הם מוגדרים בשתיהן. אם התכונה 'מיקום במרחב' לא מופעלת, המערכת תתעלם מהתכונות הממוקמות במרחב שלהם והן יחזרו לגרסאות הדו-ממדיות שלהן.
יצירת לוח מרחבי
SpatialPanel
הוא מרחבים משניים שאפשר ליצור מהם רכיבים, ומאפשר להציג תוכן של אפליקציות. לדוגמה, אפשר להציג הפעלת וידאו, תמונות סטילס או כל תוכן אחר בחלונית מרחבית.
אפשר להשתמש ב-SubspaceModifier
כדי לשנות את הגודל, ההתנהגות והמיקום של הלוח המרחבי, כפי שמתואר בדוגמה הבאה.
Subspace { SpatialPanel( SubspaceModifier .height(824.dp) .width(1400.dp) .movable() .resizable() ) { SpatialPanelContent() } }
@Composable fun SpatialPanelContent() { Box( Modifier .background(color = Color.Black) .height(500.dp) .width(500.dp), contentAlignment = Alignment.Center ) { Text( text = "Spatial Panel", color = Color.White, fontSize = 25.sp ) } }
נקודות עיקריות לגבי הקוד
- מאחר שממשקי ה-API של
SpatialPanel
הם רכיבים שניתנים ליצירה במרחב משנה, צריך להפעיל אותם בתוךSubspace
אוsetSubspaceContent
. קריאה אליהן מחוץ למרחב משנה תגרום להשלכת חריגה. - מאפשרים למשתמש לשנות את הגודל או להזיז את החלונית על ידי הוספת המשתנים המשנה
movable
אוresizable
. - הנחיות לעיצוב של חלוניות מרחבי – מידע על הגודל והמיקום. מידע ספציפי יותר על הטמעת קוד זמין במאמרי העזרה שלנו.
איך פועלת פונקציית שינוי של משנה-מרחב נייד
כשמשתמש מעביר חלונית הרחק ממנו, ברירת המחדל היא שמשתנה של מרחבים משניים ניידים ישנה את הגודל של החלונית באופן דומה לאופן שבו המערכת משנה את הגודל של חלוניות במרחב הבית. כל תוכן הצאצאים יורש את ההתנהגות הזו. כדי להשבית את האפשרות הזו, מגדירים את הפרמטר scaleWithDistance
לערך false
.
יצירת כלי ניווט במסלול
רכיב מסלול הוא רכיב של ממשק משתמש מרחבי. הוא מיועד להתחבר ללוח מרחבי, לפריסה או לישות אחרת תואמת. בדרך כלל, רכיב מסלול מכיל פריטים של ניווט ופעולות לפי הקשר שקשורים לישות שאליה הוא מקובע. לדוגמה, אם יצרתם לוח מרחבי כדי להציג תוכן וידאו, תוכלו להוסיף לחצני בקרה להפעלת וידאו בתוך כלי ניווט.
כפי שמוצג בדוגמה הבאה, קוראים ל-orbiter בתוך הפריסה הדו-ממדית ב-SpatialPanel
כדי לעטוף את אמצעי הבקרה של המשתמש, כמו ניווט. הפעולה הזו מחלצת אותם מתצוגת ה-2D ומצרפת אותם לחלונית המרחבית בהתאם להגדרה שלכם.
Subspace { SpatialPanel( SubspaceModifier .height(824.dp) .width(1400.dp) .movable() .resizable() ) { SpatialPanelContent() OrbiterExample() } }
@Composable fun OrbiterExample() { Orbiter( position = OrbiterEdge.Bottom, offset = 96.dp, alignment = Alignment.CenterHorizontally ) { Surface(Modifier.clip(CircleShape)) { Row( Modifier .background(color = Color.Black) .height(100.dp) .width(600.dp), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically ) { Text( text = "Orbiter", color = Color.White, fontSize = 50.sp ) } } } }
נקודות עיקריות לגבי הקוד
- מאחר שרכיבי Orbiter הם רכיבים מרחביים של ממשק המשתמש, אפשר לעשות שימוש חוזר בקוד בפריסות 2D או 3D. בפריסה דו-ממדית, האפליקציה תיצור רק את התוכן בתוך הלוויין ותתעלם מהלוויין עצמו.
- מידע נוסף על השימוש ב-Orbiters ועל תכנון שלהם זמין בהנחיות העיצוב שלנו.
הוספת כמה פאנלים מרחביים לפריסה מרחבית
אפשר ליצור כמה לוחות מרחביים ולהציב אותם בפריסה מרחבית באמצעות SpatialRow
, SpatialColumn
, SpatialBox
ו-SpatialLayoutSpacer
.
בדוגמת הקוד הבאה מוסבר איך עושים את זה.
Subspace { SpatialRow { SpatialColumn { SpatialPanel(SubspaceModifier.height(250.dp).width(400.dp)) { SpatialPanelContent("Top Left") } SpatialPanel(SubspaceModifier.height(200.dp).width(400.dp)) { SpatialPanelContent("Middle Left") } SpatialPanel(SubspaceModifier.height(250.dp).width(400.dp)) { SpatialPanelContent("Bottom Left") } } SpatialColumn { SpatialPanel(SubspaceModifier.height(250.dp).width(400.dp)) { SpatialPanelContent("Top Right") } SpatialPanel(SubspaceModifier.height(200.dp).width(400.dp)) { SpatialPanelContent("Middle Right") } SpatialPanel(SubspaceModifier.height(250.dp).width(400.dp)) { SpatialPanelContent("Bottom Right") } } } }
@Composable fun SpatialPanelContent(text: String) { Column( Modifier .background(color = Color.Black) .fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Text( text = "Panel", color = Color.White, fontSize = 15.sp ) Text( text = text, color = Color.White, fontSize = 25.sp, fontWeight = FontWeight.Bold ) } }
נקודות עיקריות לגבי הקוד
SpatialRow
, SpatialColumn
,SpatialBox
ו-SpatialLayoutSpacer
הם רכיבים שאפשר ליצור מהם מרחבים משניים, וצריך למקם אותם במרחב משני.- משתמשים ב-
SubspaceModifier
כדי להתאים אישית את הפריסה. - בפריסות עם כמה פאנלים בשורה, מומלץ להגדיר רדיוס עקומה של 825dp באמצעות
SubspaceModifier
כדי שהפאנלים יקיפו את המשתמש. לפרטים נוספים, אפשר לעיין בהנחיות לעיצוב.
שימוש בנפח כדי למקם אובייקט תלת-ממדי בפריסה
כדי למקם אובייקט תלת-ממדי בפריסה, צריך להשתמש במרחב משנה שאפשר ליצור ממנו אובייקטים שנקרא נפח אחסון. דוגמה לכך מופיעה בהמשך.
Subspace { SpatialPanel( SubspaceModifier.height(1500.dp).width(1500.dp) .resizable().movable() ) { ObjectInAVolume(true) Box( Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { Text( text = "Welcome", fontSize = 50.sp, ) } } }
@Composable fun ObjectInAVolume(show3DObject: Boolean) {
מידע נוסף
- במאמר הוספת מודלים תלת-ממדיים לאפליקציה מוסבר איך לטעון תוכן תלת-ממדי בנפח אחסון.
הוספת משטח לתוכן של תמונות או סרטונים
SpatialExternalSurface
הוא מרחב משנה שאפשר ליצור ולנהל בו את Surface
, שבו האפליקציה יכולה לצייר תוכן, כמו תמונה או סרטון. SpatialExternalSurface
תומך בתוכן סטריאופוליפי או מונוסקופי.
בדוגמה הזו מוסבר איך לטעון סרטון סטריאופוני צד-לצד באמצעות Media3 Exoplayer ו-SpatialExternalSurface
:
@Composable fun SpatialExternalSurfaceContent() { val context = LocalContext.current Subspace { SpatialExternalSurface( modifier = SubspaceModifier .width(1200.dp) // Default width is 400.dp if no width modifier is specified .height(676.dp), // Default height is 400.dp if no height modifier is specified // Use StereoMode.Mono, StereoMode.SideBySide, or StereoMode.TopBottom, depending // upon which type of content you are rendering: monoscopic content, side-by-side stereo // content, or top-bottom stereo content stereoMode = StereoMode.SideBySide, ) { val exoPlayer = remember { ExoPlayer.Builder(context).build() } val videoUri = Uri.Builder() .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) // Represents a side-by-side stereo video, where each frame contains a pair of // video frames arranged side-by-side. The frame on the left represents the left // eye view, and the frame on the right represents the right eye view. .path("sbs_video.mp4") .build() val mediaItem = MediaItem.fromUri(videoUri) // onSurfaceCreated is invoked only one time, when the Surface is created onSurfaceCreated { surface -> exoPlayer.setVideoSurface(surface) exoPlayer.setMediaItem(mediaItem) exoPlayer.prepare() exoPlayer.play() } // onSurfaceDestroyed is invoked when the SpatialExternalSurface composable and its // associated Surface are destroyed onSurfaceDestroyed { exoPlayer.release() } } } }
נקודות עיקריות לגבי הקוד
- מגדירים את
StereoMode
לערךMono
, SideBySide
אוTopBottom
, בהתאם לסוג התוכן שרוצים ליצור לו גרפיקה:Mono
: הפריים של התמונה או הסרטון מורכב מתמונה אחת זהה שמוצגת לשני העיניים.SideBySide
: התמונה או הפריים של הסרטון מכילים זוג תמונות או פריימים של סרטון שמסודרים זה לצד זה, כאשר התמונה או הפריים בצד ימין מייצגים את התצוגה מהעין השמאלית, והתמונה או הפריים בצד ימין מייצגים את התצוגה מהעין הימנית.TopBottom
: התמונה או הפריים של הסרטון מכילים זוג תמונות או פריימים של סרטון שנערמים אנכית, כאשר התמונה או הפריים בחלק העליון מייצגים את התצוגה מהעין השמאלית, והתמונה או הפריים בחלק התחתון מייצגים את התצוגה מהעין הימנית.
SpatialExternalSurface
תומך רק בפלטפורמות מלבניות.Surface
לא מתעד אירועי קלט.- אי אפשר לסנכרן שינויים ב-
StereoMode
עם רינדור של האפליקציה או עם פענוח של וידאו. - אי אפשר להציג את הרכיב הזה מול חלוניות אחרות, לכן לא כדאי להשתמש במודיפיקרים שניתן להזיז אם יש חלוניות אחרות בפריסה.
הוספת רכיבים אחרים של ממשק משתמש מרחבי
אפשר למקם רכיבי ממשק משתמש מרחבי בכל מקום בהיררכיית ממשק המשתמש של האפליקציה. אפשר לעשות שימוש חוזר ברכיבים האלה בממשק המשתמש הדו-מימדי, והמאפיינים המרחביים שלהם יהיו גלויים רק כשהיכולות המרחביות מופעלות. כך תוכלו להוסיף הרשאות גישה לתפריטים, לתיבת דו-שיח ולרכיבים אחרים בלי לכתוב את הקוד פעמיים. כדאי לעיין בדוגמאות הבאות לממשק משתמש מרחבי כדי להבין טוב יותר איך להשתמש ברכיבים האלה.
רכיב UI |
כשהמיקום מופעל |
בסביבה דו-ממדית |
---|---|---|
|
החלונית תזוז מעט לאחור ב-z-depth כדי להציג תיבת דו-שיח מוגבהת |
מעבר לתצוגה דו-ממדית |
|
החלונית תזוז מעט לאחור ב-z-depth כדי להציג חלון קופץ מוגבה |
המערכת חוזרת לתצוגה דו-ממדית |
|
אפשר להגדיר את |
תוכניות ללא תצוגה מרחבית. |
SpatialDialog
זו דוגמה לתיבת דו-שיח שנפתחת לאחר השהיה קצרה. כשמשתמשים ב-SpatialDialog
, תיבת הדו-שיח מופיעה באותו עומק z כמו החלונית המרחבית, והחלונית מוסטת לאחור ב-125dp כשהמיקום המרחבי מופעל. אפשר להשתמש ב-SpatialDialog
גם כשהמיקום הגיאוגרפי לא מופעל, ובמקרה כזה SpatialDialog
חוזר לגרסה הדו-ממדית שלו, Dialog
.
@Composable fun DelayedDialog() { var showDialog by remember { mutableStateOf(false) } LaunchedEffect(Unit) { delay(3000) showDialog = true } if (showDialog) { SpatialDialog( onDismissRequest = { showDialog = false }, SpatialDialogProperties( dismissOnBackPress = true ) ) { Box( Modifier .height(150.dp) .width(150.dp) ) { Button(onClick = { showDialog = false }) { Text("OK") } } } } }
נקודות עיקריות לגבי הקוד
- זו דוגמה ל-
SpatialDialog
. השימוש ב-SpatialPopup
וב-SpatialElevation
דומה מאוד. פרטים נוספים זמינים בחומר העזר בנושא API.
יצירת לוחות ופלטפורמות בהתאמה אישית
כדי ליצור לוחות בהתאמה אישית שלא נתמכים ב-Compose for XR, אפשר לעבוד ישירות עם PanelEntities
ועם תרשים הסצינה באמצעות ממשקי ה-API של SceneCore
.
עיגון של רכיבי מעקב אחר תנועה למיקומים במרחב ולישויות אחרות
אפשר לקשר אובייקט מסוג orbiter לכל ישות שהוגדרה ב-Compose. לשם כך, צריך להצהיר על רכיב מסלול במיקום מרחבי של רכיבי ממשק משתמש, כמו SpatialRow
, SpatialColumn
או SpatialBox
. ה-orbiter יאחז בעצם ההורה הקרוב ביותר למיקום שבו הכרזתם עליו.
ההתנהגות של הלוויין נקבעת לפי המיקום שבו מגדירים אותו:
- בפריסה דו-ממדית שמקיפה את
SpatialPanel
(כפי שמוצג בקטע הקוד הקודם), הרכיב האופקי עוגן ל-SpatialPanel
הזה. - ב-
Subspace
, ה-orbiter מקובע לישות ההורה הקרובה ביותר, שהיא הפריסה המרחבית שבה ה-orbiter מוצהר.
הדוגמה הבאה מראה איך לקשר כלי ניווט למסגרת מרובע במרחב:
Subspace { SpatialRow { Orbiter( position = OrbiterEdge.Top, offset = EdgeOffset.inner(8.dp), shape = SpatialRoundedCornerShape(size = CornerSize(50)) ) { Text( "Hello World!", style = MaterialTheme.typography.h2, modifier = Modifier .background(Color.White) .padding(16.dp) ) } SpatialPanel( SubspaceModifier .height(824.dp) .width(1400.dp) ) { Box( modifier = Modifier .background(Color.Red) ) } SpatialPanel( SubspaceModifier .height(824.dp) .width(1400.dp) ) { Box( modifier = Modifier .background(Color.Blue) ) } } }
נקודות עיקריות לגבי הקוד
- כשמגדירים אובייקט מסלול מחוץ לפריסה דו-ממדית, הוא עוגן לישות ההורה הקרובה ביותר. במקרה כזה, ה-orbiter יאוחז בחלק העליון של ה-
SpatialRow
שבו הוא הוצהר. - למבנים מרחביים כמו
SpatialRow
,SpatialColumn
ו-SpatialBox
משויכות ישויות ללא תוכן. לכן, אובייקט מסלול שמוצהר בפריסה מרחבית מקובע לפריסה הזו.
למידע נוסף
- הוספת מודלים תלת-ממדיים לאפליקציה
- פיתוח ממשק משתמש לאפליקציות ל-Android שמבוססות על תצוגות
- הטמעת Material Design ל-XR