XR-এর জন্য Jetpack Compose ব্যবহার করে স্থানিক UI তৈরি করুন

প্রযোজ্য এক্সআর ডিভাইস
এই নির্দেশিকা আপনাকে এই ধরনের এক্সআর ডিভাইসগুলির জন্য অভিজ্ঞতা তৈরি করতে সাহায্য করে।
এক্সআর হেডসেট
তারযুক্ত এক্সআর চশমা

Jetpack Compose for XR-এর সাহায্যে, আপনি সারি এবং কলামের মতো পরিচিত Compose ধারণাগুলি ব্যবহার করে ডিক্লারেটিভভাবে আপনার স্পেশিয়াল UI এবং লেআউট তৈরি করতে পারেন। এটি আপনাকে আপনার বিদ্যমান অ্যান্ড্রয়েড UI-কে 3D স্পেসে প্রসারিত করতে অথবা সম্পূর্ণ নতুন ইমারসিভ 3D অ্যাপ্লিকেশন তৈরি করতে সাহায্য করে।

আপনি যদি বিদ্যমান কোনো অ্যান্ড্রয়েড ভিউ-ভিত্তিক অ্যাপকে স্পেশিয়ালাইজ করতে চান, তবে আপনার কাছে বেশ কয়েকটি ডেভেলপমেন্ট বিকল্প রয়েছে। আপনি ইন্টারঅপারেবিলিটি এপিআই ব্যবহার করতে পারেন, কম্পোজ এবং ভিউ একসাথে ব্যবহার করতে পারেন, অথবা সরাসরি সিনকোর (SceneCore) লাইব্রেরির সাথে কাজ করতে পারেন। আরও বিস্তারিত জানতে ভিউ নিয়ে কাজ করার বিষয়ে আমাদের নির্দেশিকাটি দেখুন।

উপস্থান এবং স্থানিক উপাদান সম্পর্কে

অ্যান্ড্রয়েড এক্সআর (Android XR)-এর জন্য অ্যাপ লেখার সময় সাবস্পেস (subspace ) এবং স্পেশিয়ালাইজড কম্পোনেন্ট (spatialized components) -এর ধারণাগুলো বোঝা গুরুত্বপূর্ণ।

সাবস্পেস সম্পর্কে

Android XR-এর জন্য ডেভেলপ করার সময়, আপনাকে আপনার অ্যাপ বা লেআউটে একটি সাবস্পেস যোগ করতে হবে। সাবস্পেস হলো আপনার অ্যাপের মধ্যে থাকা ত্রিমাত্রিক স্থানের (3D space) একটি অংশ, যেখানে আপনি ত্রিমাত্রিক কন্টেন্ট রাখতে, ত্রিমাত্রিক লেআউট তৈরি করতে এবং অন্যথায় দ্বিমাত্রিক (2D) কন্টেন্টে গভীরতা যোগ করতে পারেন। একটি সাবস্পেস শুধুমাত্র তখনই রেন্ডার করা হয় যখন স্পেশিয়ালাইজেশন (spatialization) সক্রিয় থাকে। হোম স্পেসে (Home Space) বা নন-XR ডিভাইসগুলিতে, সেই সাবস্পেসের ভেতরের যেকোনো কোড উপেক্ষা করা হয়।

সাবস্পেস তৈরি করার কয়েকটি উপায় আছে:

  • Subspace : এই কম্পোজেবলটি একটি নতুন, স্বাধীন স্পেশিয়াল UI হায়ারার্কি তৈরি করে। এটি যে প্যারেন্ট Subspace মধ্যে নেস্টেড থাকে, তার স্পেশিয়াল পজিশন, ওরিয়েন্টেশন বা স্কেল উত্তরাধিকার সূত্রে পায় না। Subspace স্বয়ংক্রিয়ভাবে সিস্টেমের প্রস্তাবিত কন্টেন্ট বক্স দ্বারা আবদ্ধ থাকে।
  • PlanarEmbeddedSubspace : এই কম্পোজেবলটি আপনার অ্যাপের UI হায়ারার্কির মধ্যে রাখা যেতে পারে, যা আপনাকে 2D এবং স্পেশিয়াল UI-এর লেআউট বজায় রাখতে সাহায্য করে। PlanarEmbeddedSubspace তার প্যারেন্টের সীমাবদ্ধতা এবং পজিশনিং মেনে চলে। এর ভিতরে রাখা 3D কন্টেন্ট তখন এই 2D-নির্ধারিত এলাকার সাপেক্ষে অবস্থান নেয়।

আরও তথ্যের জন্য, আপনার অ্যাপে একটি সাবস্পেস যোগ করুন দেখুন।

স্থানিক উপাদান সম্পর্কে

সাবস্পেস কম্পোজেবল : এই কম্পোনেন্টগুলো শুধুমাত্র একটি সাবস্পেসের মধ্যেই রেন্ডার করা যায়। একটি 2D লেআউটে স্থাপন করার আগে এগুলোকে অবশ্যই Subspace মধ্যে আবদ্ধ করতে হবে। একটি SubspaceModifier আপনাকে আপনার সাবস্পেস কম্পোজেবলগুলোতে ডেপথ, অফসেট এবং পজিশনিং- এর মতো অ্যাট্রিবিউট যোগ করার সুযোগ দেয়।

অন্যান্য স্পেশিয়ালাইজড কম্পোনেন্টগুলোকে সাবস্পেসের ভেতর থেকে কল করার প্রয়োজন হয় না। এগুলো একটি স্পেশিয়াল কন্টেইনারের মধ্যে মোড়ানো প্রচলিত ২ডি এলিমেন্ট নিয়ে গঠিত। এই এলিমেন্টগুলো ২ডি বা ৩ডি লেআউটের জন্য ব্যবহার করা যেতে পারে, যদি উভয়টির জন্যই এগুলো সংজ্ঞায়িত করা থাকে। যখন স্পেশিয়ালাইজেশন সক্রিয় থাকে না, তখন এদের স্পেশিয়ালাইজড ফিচারগুলো উপেক্ষা করা হয় এবং এগুলো তাদের ২ডি প্রতিরূপে ফিরে যায়।

একটি স্থানিক প্যানেল তৈরি করুন

একটি SpatialPanel হলো একটি কম্পোজেবল সাবস্পেস যা আপনাকে অ্যাপের কন্টেন্ট প্রদর্শন করতে দেয়—উদাহরণস্বরূপ, আপনি একটি স্পেশাল প্যানেলে ভিডিও প্লেব্যাক, স্থির চিত্র বা অন্য যেকোনো কন্টেন্ট প্রদর্শন করতে পারেন।

স্থানিক UI প্যানেলের একটি উদাহরণ

নিম্নলিখিত উদাহরণে দেখানো অনুযায়ী, আপনি SubspaceModifier ব্যবহার করে স্পেশিয়াল প্যানেলের আকার, আচরণ এবং অবস্থান পরিবর্তন করতে পারেন।

Subspace {
    SpatialPanel(
        SubspaceModifier
            .height(824.dp)
            .width(1400.dp),
        dragPolicy = MovePolicy(),
        resizePolicy = ResizePolicy(),
    ) {
        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
        )
    }
}

কোড সম্পর্কে মূল বিষয়গুলো

  • যেহেতু SpatialPanel API-গুলো সাবস্পেস কম্পোজেবল, তাই আপনাকে অবশ্যই এগুলোকে [ Subspace ][4]-এর ভিতরে কল করতে হবে। সাবস্পেসের বাইরে কল করলে একটি এক্সেপশন থ্রো হবে।
  • SubspaceModifier এর height এবং width স্পেসিফিকেশন ব্যবহার করে SpatialPanel এর আকার নির্ধারণ করা হয়েছে। এই স্পেসিফিকেশনগুলো বাদ দিলে প্যানেলের আকার তার ভেতরের বিষয়বস্তুর পরিমাপ অনুযায়ী নির্ধারিত হয়।
  • একটি MovePolicy যোগ করে ব্যবহারকারীকে প্যানেল সরানোর অনুমতি দিন।
  • একটি ResizePolicy যোগ করে ব্যবহারকারীকে প্যানেলের আকার পরিবর্তন করার অনুমতি দিন।
  • আকার ও অবস্থান নির্ধারণের বিশদ বিবরণের জন্য আমাদের স্পেশিয়াল প্যানেল ডিজাইন নির্দেশিকা দেখুন। কোড বাস্তবায়নের আরও নির্দিষ্ট তথ্যের জন্য আমাদের রেফারেন্স ডকুমেন্টেশন দেখুন।

MovePolicy কীভাবে কাজ করে

যখন কোনো ব্যবহারকারী একটি প্যানেলকে নিজের থেকে দূরে সরান, তখন ডিফল্টরূপে, একটি MovePolicy প্যানেলটিকে ঠিক সেভাবেই স্কেল করে যেভাবে হোম স্পেসে সিস্টেম প্যানেলগুলির আকার পরিবর্তন করে। এর সমস্ত চাইল্ড কন্টেন্ট এই আচরণটি গ্রহণ করে। এটি নিষ্ক্রিয় করতে, shouldScaleWithDistance প্যারামিটারটিকে false এ সেট করুন।

একটি অরবিটার তৈরি করুন

অরবিটার হলো একটি স্পেশিয়াল UI কম্পোনেন্ট। এটিকে সংশ্লিষ্ট স্পেশিয়াল প্যানেল অথবা স্পেশিয়াল লেআউট কম্পোনেন্ট, যেমন SpatialColumn , SpatialRow , বা SpatialBox এর সাথে সংযুক্ত করার জন্য ডিজাইন করা হয়েছে। একটি অরবিটারে সাধারণত সেই এনটিটির সাথে সম্পর্কিত নেভিগেশন এবং প্রাসঙ্গিক অ্যাকশন আইটেম থাকে, যার সাথে এটি অ্যাঙ্কর করা থাকে। উদাহরণস্বরূপ, যদি আপনি ভিডিও কন্টেন্ট দেখানোর জন্য একটি স্পেশিয়াল প্যানেল তৈরি করে থাকেন, তাহলে আপনি একটি অরবিটারের ভিতরে ভিডিও প্লেব্যাক কন্ট্রোল যোগ করতে পারেন।

অরবিটারের উদাহরণ

নিচের উদাহরণে যেমন দেখানো হয়েছে, নেভিগেশনের মতো ইউজার কন্ট্রোলগুলোকে র‍্যাপ করার জন্য একটি SpatialPanel এর 2D লেআউটের ভিতরে একটি অরবিটার কল করুন। এটি করলে, ইউজার কন্ট্রোলগুলো আপনার 2D লেআউট থেকে এক্সট্র্যাক্ট হয়ে আপনার কনফিগারেশন অনুযায়ী স্পেশিয়াল প্যানেলে সংযুক্ত হয়ে যায়।

Subspace {
    SpatialPanel(
        SubspaceModifier
            .height(824.dp)
            .width(1400.dp),
        dragPolicy = MovePolicy(),
        resizePolicy = ResizePolicy(),
    ) {
        SpatialPanelContent()
        OrbiterExample()
    }
}

@Composable
fun OrbiterExample() {
    Orbiter(
        position = ContentEdge.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
                )
            }
        }
    }
}

কোড সম্পর্কে মূল বিষয়গুলো

  • যেহেতু অরবিটারগুলো স্পেশিয়াল UI কম্পোনেন্ট, তাই এর কোড 2D বা 3D লেআউটে পুনরায় ব্যবহার করা যায়। একটি 2D লেআউটে, আপনার অ্যাপ শুধুমাত্র অরবিটারের ভেতরের কন্টেন্ট রেন্ডার করে এবং অরবিটারটিকে উপেক্ষা করে।
  • অরবিটার কীভাবে ব্যবহার ও ডিজাইন করতে হয়, সে সম্পর্কে আরও তথ্যের জন্য আমাদের ডিজাইন নির্দেশিকা দেখুন।

একটি স্থানিক বিন্যাসে একাধিক স্থানিক প্যানেল যোগ করুন

আপনি 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 ব্যবহার করুন।
  • যেসব লেআউটে এক সারিতে একাধিক প্যানেল থাকে, সেগুলোর ক্ষেত্রে আমরা SubspaceModifier ব্যবহার করে 825dp-এর একটি কার্ভ রেডিয়াস সেট করার পরামর্শ দিই, যাতে প্যানেলগুলো আপনার ব্যবহারকারীকে ঘিরে রাখে। বিস্তারিত জানতে আমাদের ডিজাইন নির্দেশিকা দেখুন।

আপনার লেআউটে এনটিটি স্থাপন করতে একটি SceneCoreEntity ব্যবহার করুন।

আপনার লেআউটে একটি 3D অবজেক্ট স্থাপন করতে, আপনাকে SceneCoreEntity নামক একটি সাবস্পেস কম্পোজেবল ব্যবহার করতে হবে। এটি কীভাবে করতে হয় তার একটি উদাহরণ এখানে দেওয়া হলো।

Subspace {
    SceneCoreEntity(
        modifier = SubspaceModifier.offset(x = 50.dp),
        factory = {
            SurfaceEntity.create(
                session = session,
                pose = Pose.Identity,
                stereoMode = SurfaceEntity.StereoMode.MONO
            )
        },
        update = { entity ->
            // compose state changes may be applied to the
            // SceneCore entity here.
            entity.stereoMode = SurfaceEntity.StereoMode.SIDE_BY_SIDE
        },
        sizeAdapter =
            SceneCoreEntitySizeAdapter({
                IntSize2d(it.width, it.height)
            }),
    ) {
        // Content here will be children of the SceneCoreEntity
        // in the scene graph.
    }
}

অতিরিক্ত তথ্য

ছবি বা ভিডিও কন্টেন্টের জন্য একটি সারফেস যোগ করুন

SpatialExternalSurface হলো একটি কম্পোজেবল সাবস্পেস যা এমন একটি Surface তৈরি ও পরিচালনা করে, যেখানে আপনার অ্যাপ ছবি বা ভিডিওর মতো কন্টেন্ট ড্র করতে পারে। SpatialExternalSurface স্টেরিওস্কোপিক বা মনোস্কোপিক উভয় প্রকার কন্টেন্টই সমর্থন করে।

এই উদাহরণটি দেখায় কিভাবে Media3 Exoplayer এবং SpatialExternalSurface ব্যবহার করে পাশাপাশি স্টেরিওস্কোপিক ভিডিও লোড করতে হয়।

@OptIn(ExperimentalComposeApi::class)
@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 পরিবর্তন সিঙ্ক্রোনাইজ করা সম্ভব নয়।
  • এই কম্পোজেবলটি অন্য প্যানেলের সামনে রেন্ডার হতে পারে না, তাই লেআউটে অন্য প্যানেল থাকলে আপনার MovePolicy ব্যবহার করা উচিত নয়।

DRM সুরক্ষিত ভিডিও কন্টেন্টের জন্য একটি পৃষ্ঠতল যোগ করুন

SpatialExternalSurface ডিআরএম-সুরক্ষিত ভিডিও স্ট্রিম প্লেব্যাকও সমর্থন করে। এটি সক্রিয় করতে, আপনাকে অবশ্যই একটি সুরক্ষিত সারফেস তৈরি করতে হবে যা সুরক্ষিত গ্রাফিক্স বাফারে রেন্ডার করে। এটি কন্টেন্টকে স্ক্রিন-রেকর্ড হওয়া বা অসুরক্ষিত সিস্টেম কম্পোনেন্ট দ্বারা অ্যাক্সেস হওয়া থেকে বিরত রাখে।

একটি সুরক্ষিত সারফেস তৈরি করতে, SpatialExternalSurface কম্পোজেবলে surfaceProtection প্যারামিটারটিকে SurfaceProtection.Protected এ সেট করুন। এছাড়াও, লাইসেন্স সার্ভার থেকে লাইসেন্স সংগ্রহের বিষয়টি পরিচালনা করার জন্য আপনাকে অবশ্যই উপযুক্ত DRM তথ্য দিয়ে Media3 Exoplayer কনফিগার করতে হবে।

নিম্নলিখিত উদাহরণটি দেখায় কিভাবে একটি DRM-সুরক্ষিত ভিডিও স্ট্রিম চালানোর জন্য SpatialExternalSurface এবং ExoPlayer কনফিগার করতে হয়:

@OptIn(ExperimentalComposeApi::class)
@Composable
fun DrmSpatialVideoPlayer() {
    val context = LocalContext.current
    Subspace {
        SpatialExternalSurface(
            modifier = SubspaceModifier
                .width(1200.dp)
                .height(676.dp),
            stereoMode = StereoMode.SideBySide,
            surfaceProtection = SurfaceProtection.Protected
        ) {
            val exoPlayer = remember { ExoPlayer.Builder(context).build() }

            // Define the URI for your DRM-protected content and license server.
            val videoUri = "https://your-content-provider.com/video.mpd"
            val drmLicenseUrl = "https://your-license-server.com/license"

            // Build a MediaItem with the necessary DRM configuration.
            val mediaItem = MediaItem.Builder()
                .setUri(videoUri)
                .setDrmConfiguration(
                    MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID)
                        .setLicenseUri(drmLicenseUrl)
                        .build()
                )
                .build()

            onSurfaceCreated { surface ->
                // The created surface is secure and can be used by the player.
                exoPlayer.setVideoSurface(surface)
                exoPlayer.setMediaItem(mediaItem)
                exoPlayer.prepare()
                exoPlayer.play()
            }

            onSurfaceDestroyed { exoPlayer.release() }
        }
    }
}

কোড সম্পর্কে মূল বিষয়গুলো

  • সুরক্ষিত সারফেস: SpatialExternalSurfacesurfaceProtection = SurfaceProtection.Protected সেট করা অপরিহার্য, যাতে এর নিচের Surface DRM কন্টেন্টের জন্য উপযুক্ত সুরক্ষিত বাফার দ্বারা সুরক্ষিত থাকে।
  • ডিআরএম কনফিগারেশন: আপনাকে অবশ্যই ডিআরএম স্কিম (উদাহরণস্বরূপ, C.WIDEVINE_UUID ) এবং আপনার লাইসেন্স সার্ভারের ইউআরআই দিয়ে MediaItem কনফিগার করতে হবে। এক্সোপ্লেয়ার ডিআরএম সেশন পরিচালনা করার জন্য এই তথ্য ব্যবহার করে।
  • সুরক্ষিত কন্টেন্ট: কোনো সুরক্ষিত প্ল্যাটফর্মে রেন্ডার করার সময়, ভিডিও কন্টেন্টটি একটি সুরক্ষিত পথে ডিকোড ও প্রদর্শন করা হয়, যা কন্টেন্ট লাইসেন্সিংয়ের শর্ত পূরণ করতে সাহায্য করে। এটি কন্টেন্টটিকে স্ক্রিন ক্যাপচারে প্রদর্শিত হওয়া থেকেও বিরত রাখে।

অন্যান্য স্থানিক UI উপাদান যোগ করুন

স্পেশিয়াল UI কম্পোনেন্টগুলো আপনার অ্যাপ্লিকেশনের UI হায়ারার্কির যেকোনো স্থানে স্থাপন করা যেতে পারে। এই এলিমেন্টগুলো আপনার 2D UI-তে পুনরায় ব্যবহার করা যায়, এবং এদের স্পেশিয়াল অ্যাট্রিবিউটগুলো কেবল তখনই দৃশ্যমান হবে যখন স্পেশিয়াল ক্যাপাবিলিটিগুলো সক্রিয় করা থাকবে। এর ফলে, আপনার কোড দুবার না লিখেই মেনু, ডায়ালগ এবং অন্যান্য কম্পোনেন্টে এলিভেশন যোগ করা যায়। এই এলিমেন্টগুলো কীভাবে ব্যবহার করতে হয় তা আরও ভালোভাবে বোঝার জন্য স্পেশিয়াল UI-এর নিম্নলিখিত উদাহরণগুলো দেখুন।

UI উপাদান

যখন স্থানিকীকরণ সক্রিয় করা হয়

দ্বিমাত্রিক পরিবেশে

SpatialDialog

উন্নত ডায়ালগ প্রদর্শনের জন্য প্যানেলটি z-গভীরতায় সামান্য পিছিয়ে যাবে।

২ডি Dialog ফিরে যায়।

SpatialPopup

উঁচু পপআপটি দেখানোর জন্য প্যানেলটি z-গভীরতা বরাবর সামান্য পিছিয়ে যাবে।

ফিরে গেলে একটি 2D Popup হয়।

SpatialElevation

উচ্চতা যোগ করার জন্য SpatialElevationLevel সেট করা যেতে পারে।

স্থানিক উচ্চতা ছাড়া দেখানো হয়।

স্থানিক সংলাপ

এটি এমন একটি ডায়ালগের উদাহরণ যা অল্প বিলম্বের পর খোলে। যখন SpatialDialog ব্যবহার করা হয়, তখন ডায়ালগটি স্পেশিয়াল প্যানেলের সমান z-depth-এ প্রদর্শিত হয়, এবং স্পেশিয়ালাইজেশন সক্রিয় থাকলে প্যানেলটি 125dp পিছিয়ে যায়। স্পেশিয়ালাইজেশন সক্রিয় না থাকলেও SpatialDialog ব্যবহার করা যেতে পারে, সেক্ষেত্রে SpatialDialog তার 2D প্রতিরূপ, 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")
                }
            }
        }
    }
}

কোড সম্পর্কে মূল বিষয়গুলো

কাস্টম প্যানেল এবং লেআউট তৈরি করুন

Compose for XR দ্বারা সমর্থিত নয় এমন কাস্টম প্যানেল তৈরি করতে, আপনি SceneCore API ব্যবহার করে সরাসরি PanelEntity ইনস্ট্যান্স এবং সিন গ্রাফের সাথে কাজ করতে পারেন।

মহাকাশ প্যানেল এবং বিন্যাসের সাথে অরবিটারগুলিকে নোঙর করুন

আপনি কম্পোজে ঘোষিত SpatialPanels এবং স্পেশাল লেআউট কম্পোনেন্টগুলিতে একটি অরবিটার অ্যাঙ্কর করতে পারেন। এর জন্য SpatialRow , SpatialColumn বা SpatialBox মতো UI এলিমেন্টের একটি স্পেশাল লেআউটে অরবিটার ঘোষণা করতে হয়। অরবিটারটি আপনার ঘোষণার স্থানের সবচেয়ে কাছের প্যারেন্টের সাথে অ্যাঙ্কর হয়।

অরবিটারটির আচরণ নির্ধারিত হয় আপনি এটিকে কোথায় ঘোষণা করছেন তার উপর:

  • একটি SpatialPanel এ মোড়ানো 2D লেআউটে (যেমনটি পূর্ববর্তী কোড স্নিপেটে দেখানো হয়েছে), অরবিটারটি সেই SpatialPanel সাথে অ্যাঙ্কর করে।
  • একটি Subspace মধ্যে, অরবিটারটি তার নিকটতম প্যারেন্ট এনটিটির সাথে নোঙর করে, যা হলো সেই স্পেশিয়াল লেআউট যেখানে অরবিটারটি ঘোষিত হয়েছে।

নিম্নলিখিত উদাহরণটি দেখায় কিভাবে একটি অরবিটারকে একটি স্থানিক সারিতে নোঙর করতে হয়:

Subspace {
    SpatialRow {
        Orbiter(
            position = ContentEdge.Top,
            offset = 8.dp,
            offsetType = OrbiterOffsetType.InnerEdge,
            shape = SpatialRoundedCornerShape(size = CornerSize(50))
        ) {
            Text(
                "Hello World!",
                style = MaterialTheme.typography.titleMedium,
                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)
            )
        }
    }
}

কোড সম্পর্কে মূল বিষয়গুলো

  • যখন আপনি একটি 2D লেআউটের বাইরে কোনো অরবিটার ডিক্লেয়ার করেন, তখন অরবিটারটি তার নিকটতম প্যারেন্ট এনটিটির সাথে অ্যাঙ্কর করে। এই ক্ষেত্রে, অরবিটারটি যে SpatialRow তে ডিক্লেয়ার করা হয়েছে, তার শীর্ষে অ্যাঙ্কর করে।
  • SpatialRow , SpatialColumn , SpatialBox এর মতো স্পেশিয়াল লেআউটগুলোর সাথে বিষয়বস্তুহীন এনটিটি যুক্ত থাকে। তাই, একটি স্পেশিয়াল লেআউটে ঘোষিত অরবিটার সেই লেআউটটিকে অ্যাঙ্কর করে।

আরও দেখুন