تطوير واجهة المستخدم باستخدام Jetpack Compose للواقع الممتد

باستخدام Jetpack Compose لتطبيقات الواقع المعزّز، يمكنك إنشاء واجهة المستخدم المكانية و التنسيق بشكل تعريفي باستخدام مفاهيم Compose المألوفة، مثل الصفوف والأعمدة. يتيح لك ذلك توسيع واجهة مستخدم Android الحالية إلى مساحة ثلاثية الأبعاد أو إنشاء تطبيقات ثلاثية الأبعاد immersive جديدة تمامًا.

إذا كنت بصدد إضافة ميزة العرض المكاني إلى تطبيق حالي يستند إلى Android Views، تتوفّر لك عدة خيارات تطوير. يمكنك استخدام واجهات برمجة التطبيقات للتشغيل التفاعلي أو استخدام Compose وViews معًا أو العمل مباشرةً مع مكتبة SceneCore. يمكنك الاطّلاع على دليل العمل مع المشاهدات للحصول على مزيد من التفاصيل.

لمحة عن المساحات الفرعية والمكونات المخصّصة للعرض في مساحات ثلاثية الأبعاد

عند كتابة تطبيقك لنظام Android XR، من المهم فهم مفاهيم المساحة الفرعية والمكوّنات المكانية.

لمحة عن المساحة الفرعية

عند تطوير تطبيقات لنظام Android XR، ستحتاج إلى إضافة مساحة فرعية إلى تطبيقك أو تنسيقه. المساحة الفرعية هي قسم من المساحة الثلاثية الأبعاد داخل تطبيقك يمكنك فيه وضع محتوى ثلاثي الأبعاد وإنشاء تصاميم ثلاثية الأبعاد وإضافة عمق إلى المحتوى ثنائي الأبعاد. لا يتم عرض المساحات الفرعية إلا عند تفعيل ميزة "الصوت المكاني". في "المساحة الرئيسية" أو على الأجهزة غير المزوّدة بتقنية الواقع المعزّز، يتم تجاهل أي رمز ضمن هذه المساحة الفرعية.

تتوفّر طريقتان لإنشاء مساحة فرعية:

  • setSubspaceContent: تنشئ هذه الدالة مساحة فرعية على مستوى التطبيق. يمكن استدعاء هذا الإجراء في MainActivity بالطريقة نفسها التي تستخدم بها setContent. إنّ المساحة الفرعية على مستوى التطبيق غير محدودة من حيث الارتفاع والعرض والعمق، ما يوفر في الأساس مساحة عرض لا نهائية للمحتوى المكاني.
  • Subspace: يمكن وضع هذا المكوّن القابل للتجميع في أي مكان ضمن التسلسل الهرمي لواجهة المستخدم في تطبيقك، ما يتيح لك الاحتفاظ بتنسيقات لواجهة المستخدم ثنائية الأبعاد وواجهة المستخدم المكانية بدون فقدان السياق بين الملفات. يسهّل ذلك مشاركة عناصر مثل بنية التطبيق الحالية بين الواقع المعزّز وأشكال الأجهزة الأخرى بدون الحاجة إلى رفع الحالة من خلال شجرة واجهة المستخدم بالكامل أو إعادة تصميم تطبيقك.

لمزيد من المعلومات، اطّلِع على مقالة إضافة مساحة فرعية إلى تطبيقك.

لمحة عن المكونات المكانية

العناصر القابلة للتجميع في مساحة فرعية: لا يمكن عرض هذه العناصر إلا في مساحة فرعية. يجب وضعها بين Subspace أو setSubspaceContent قبل وضعها في تنسيق ثنائي الأبعاد. يتيح لك SubspaceModifier إضافة سمات مثل العمق والإزاحة والموضع إلى العناصر القابلة للتجميع في مساحة فرعية.

  • ملاحظة حول عوامل تعديل المساحة الفرعية: انتبه جيدًا لترتيب SubspaceModifier واجهات برمجة التطبيقات.
    • يجب أن يظهر المُعَلِّم المُعوض أولاً في سلسلة المُعدِّلات.
    • يجب أن تكون العناصر القابلة للنقل وتغيير الحجم هي الأخيرة.
    • يجب تطبيق التدوير قبل التكبير/التصغير.

لا تتطلّب المكوّنات الأخرى المستندة إلى الموقع الجغرافي أن يتمّ استدعاؤها داخل مساحة فرعية. وهي تتكون من عناصر ثنائية الأبعاد تقليدية ملفوفة داخل حاوية مكانية. يمكن استخدام هذه العناصر ضمن تنسيقات ثنائية أو ثلاثية الأبعاد إذا تم تحديدها لكلا التنسيقَين. عندما لا يكون وضع "العرض المكاني" مفعّلاً، سيتم تجاهل ميزاته المكانية وسيتم الرجوع إلى نظيراتها ثنائية الأبعاد.

إنشاء لوحة مكانية

SpatialPanel هو مساحة فرعية قابلة للتجميع تتيح لك عرض محتوى التطبيق، على سبيل المثال، يمكنك عرض تشغيل الفيديو أو الصور الثابتة أو أي محتوى آخر في لوحة مكانية.

مثال على لوحة واجهة مستخدم مكانية

يمكنك استخدام SubspaceModifier لتغيير حجم الشاشة المكانية وسلوكها وموضعها، كما هو موضّح في المثال التالي.

Subspace {
   SpatialPanel(
        SubspaceModifier
           .height(824.dp)
           .width(1400.dp)
           .movable()
           .resizable()
           ) {
          SpatialPanelContent()
      }
}

// 2D content placed within the spatial panel
@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
        )
    }
}

النقاط الرئيسية حول الرمز

  • ملاحظة حول عوامل تعديل المساحة الفرعية: انتبه جيدًا لترتيب واجهات برمجة التطبيقات SubspaceModifier.
    • يجب أن يظهر البادئة أولاً في سلسلة المُعدِّلات.
    • يجب أن تظهر المُعدِّلات القابلة للنقل وتغيير الحجم في آخر السلسلة.
    • يجب تطبيق التدوير قبل التكبير/التصغير.
  • بما أنّ واجهات برمجة تطبيقات SpatialPanel هي عناصر قابلة للتجميع في مساحة فرعية، يجب طلبها داخل Subspace أو setSubspaceContent. سيؤدي استدعاؤها خارج مساحة فرعية إلى طرح استثناء.
  • اسمح للمستخدم بتغيير حجم اللوحة أو نقلها من خلال إضافة .movable أو .resizable SubspaceModifier.
  • اطّلِع على إرشادات تصميم اللوحة المكانية للحصول على تفاصيل عن الحجم والموضع. يمكنك الاطّلاع على المستندات المرجعية للحصول على مزيد من التفاصيل حول تنفيذ الرمز.

إنشاء مدار

المسار هو مكوّن لواجهة مستخدِم مكانية. تم تصميمه ليتم إرفاقه بوحة مكانية مقابلة، ويحتوي على عناصر تنقّل وإجراءات مستندة إلى السياق مرتبطة بهذه اللوحة المكانية. على سبيل المثال، إذا أنشأت لوحة مكانية لعرض محتوى فيديو، يمكنك إضافة عناصر التحكّم في تشغيل الفيديو داخل مسار مداري.

مثال على مركبة مدارية

كما هو موضّح في المثال التالي، يمكنك استدعاء عنصر orbiter داخل SpatialPanel ل التفاف حول عناصر التحكّم في المستخدِم، مثل عناصر التنقّل. يؤدي ذلك إلى استخراجها من التنسيق ثنائي الأبعاد وإرفاقها باللوحة المكانية وفقًا لإعداداتك.

setContent {
    Subspace {
        SpatialPanel(
            SubspaceModifier
                .height(824.dp)
                .width(1400.dp)
                .movable()
                .resizable()
        ) {
            SpatialPanelContent()
            OrbiterExample()
        }
    }
}

//2D content inside Orbiter
@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
                )
            }
        }
    }
}

النقاط الرئيسية حول الرمز

  • ملاحظة حول مُعدِّلات المساحات الفرعية: انتبِه جيدًا لترتيب واجهات برمجة تطبيقات SubspaceModifier.
    • يجب أن يظهر المُعَلِّم المُعَدِّل أولاً في سلسلة المُعَلِّمات.
    • يجب أن تكون العناصر القابلة للنقل وتغيير الحجم هي الأخيرة.
    • يجب تطبيق التدوير قبل التكبير/التصغير.
  • بما أنّ العناصر الدوّارة هي مكونات لواجهة المستخدم المكانية، يمكن إعادة استخدام الرمز البرمجي في تنسيقات ثنائية أو ثلاثية الأبعاد. في التنسيق ثنائي الأبعاد، يعرض تطبيقك المحتوى داخل الإطار فقط ويغضّ الطرف عن الإطار نفسه.
  • اطّلِع على إرشادات التصميم للحصول على مزيد من المعلومات عن كيفية استخدام المدارات وتصميمها.

إضافة عدة لوحات مكانية إلى تنسيق مكاني

يمكنك إنشاء عدة لوحات مكانية ووضعها ضمن تنسيق مكاني باستخدام 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) {
    val xrCoreSession = checkNotNull(LocalSession.current)
    val scope = rememberCoroutineScope()
    if (show3DObject) {
        Subspace {
            Volume(
                modifier = SubspaceModifier
                    .offset(volumeXOffset, volumeYOffset, volumeZOffset) //
Relative position
                    .scale(1.2f) // Scale to 120% of the size

            ) { parent ->
                scope.launch {
                   // Load your 3D Object here
                }
            }
        }
    }
}

النقاط الرئيسية حول الرمز

  • ملاحظة حول مُعدِّلات المساحات الفرعية: انتبِه جيدًا لترتيب واجهات برمجة تطبيقات SubspaceModifier.
    • يجب أن يظهر المُعَوِّض أولاً في سلسلة المُعدِّلات.
    • يجب أن تكون العناصر القابلة للنقل وتغيير الحجم هي الأخيرة.
    • يجب تطبيق التدوير قبل التكبير/التصغير.
  • اطّلِع على مقالة إضافة محتوى ثلاثي الأبعاد للتعرّف بشكل أفضل على كيفية تحميل محتوى ثلاثي الأبعاد في مجلّد.

إضافة مكونات أخرى لواجهة المستخدم المكانية

يمكن وضع مكوّنات واجهة المستخدم المكانية في أيّ مكان في التسلسل الهرمي لواجهة المستخدم في تطبيقك. يمكن إعادة استخدام هذه العناصر في واجهة المستخدم ثنائية الأبعاد، ولن تكون سماتها المكانية مرئية إلا عند تفعيل الإمكانات المكانية. يتيح لك ذلك إضافة أثر التمويه إلى القوائم وملفات الحوار والمكونات الأخرى بدون الحاجة إلى كتابة رمزك مرتين. اطّلِع على الأمثلة التالية على واجهة المستخدم المكانية لفهم كيفية استخدام هذه العناصر بشكل أفضل.

مكوّن واجهة المستخدم

عند تفعيل ميزة "الصوت المكاني"

في بيئة ثنائية الأبعاد

SpatialDialog

سيتم دفع اللوحة للخلف قليلاً في العمق z لعرض مربّع حوار مرتفع

الرجوع إلى العرض ثنائي الأبعاد Dialog

SpatialPopUp

سيتم دفع اللوحة للخلف قليلاً في العمق (z) لعرض نافذة منبثقة مرتفعة.

يعود إلى PopUp ثنائي الأبعاد.

SpatialElevation

يمكن ضبط SpatialElevationLevel لإضافة ارتفاع.

عروض بدون ارتفاع مكاني

SpatialDialog

هذا مثال على مربّع حوار يفتح بعد تأخير قصير. عند استخدام SpatialDialog، يظهر مربّع الحوار في عمق z نفسه الذي تظهر به اللوحة المكانية، ويتم دفع اللوحة للخلف بمقدار 125dp عند تفعيل العرض المكاني. لا يزال بإمكانك استخدام SpatialDialog حتى في حال عدم تفعيل ميزة "العرض المكاني"، ويعود الرمز إلى شكله ثنائي الأبعاد: Dialog.

@Composable
fun DelayedDialog() {
   var showDialog by remember { mutableStateOf(false) }
   LaunchedEffect(Unit) {
       Handler(Looper.getMainLooper()).postDelayed({
           showDialog = true
       }, 3000)
   }
   if (showDialog) {
       SpatialDialog (
           onDismissRequest = { showDialog = false },
           SpatialDialogProperties(
               dismissOnBackPress = true)
       ){
           Box(Modifier
               .height(150.dp)
               .width(150.dp)
           ) {
               Button(onClick = { showDialog = false }) {
                   Text("OK")
               }
           }
       }
   }
}

النقاط الرئيسية حول الرمز

إنشاء لوحات وتنسيقات مخصّصة

لإنشاء لوحات مخصّصة غير متوافقة مع Compose for XR، يمكنك العمل مباشرةً مع PanelEntities ورسم المشهد باستخدام واجهات برمجة التطبيقات SceneCore.

انظر أيضًا