Compose for Wear OS में अपने यूज़र इंटरफ़ेस (यूआई) की झलक देखना

Android Studio में Compose Previews की मदद से, Wear OS के कंपोज़ेबल की जांच की जा सकती है. साथ ही, अलग-अलग साइज़ की वॉच डिस्प्ले, गोल बेज़ल, और फ़ॉन्ट स्केल पर उनकी पुष्टि की जा सकती है. इसके लिए, आपको अपने ऐप्लिकेशन को किसी फ़िज़िकल वॉच या एम्युलेटर पर डिप्लॉय करने की ज़रूरत नहीं होती.

Wear OS डिवाइसों में गोल डिसप्ले होते हैं. इनमें कोनों पर कॉन्टेंट कट जाता है. साथ ही, TimeText और ScrollIndicator जैसे सिस्टम ओवरले, स्क्रीन के किनारे पर मुड़ जाते हैं. इसलिए, Wear OS के लिए खास तौर पर झलक कॉन्फ़िगर करना ज़रूरी है, ताकि लेआउट से जुड़ी समस्याओं का पता पहले ही चल जाए.


झलक की डिपेंडेंसी सेट अप करना

Wear OS Compose की झलक देखने के लिए एनोटेशन और डिवाइस की परिभाषाओं का इस्तेमाल करने के लिए, अपने मॉड्यूल की build.gradle.kts फ़ाइल में ये डिपेंडेंसी जोड़ें:

dependencies {
    // Provides @WearPreview* multipreview annotations
    // (such as @WearPreviewDevices and @WearPreviewFontScales)
    implementation("androidx.wear.compose:compose-ui-tooling:1.7.0")

    // Provides WearDevices constants
    // (such as WearDevices.SMALL_ROUND and WearDevices.LARGE_ROUND)
    implementation("androidx.wear:wear-tooling-preview:1.0.0")

    // Standard Compose preview support and interactive/animation inspection
    implementation("androidx.compose.ui:ui-tooling-preview")
    debugImplementation("androidx.compose.ui:ui-tooling")
}

चुनें कि आपको क्या देखना है: स्क्रीन या कॉम्पोनेंट

झलक को कॉन्फ़िगर करने का तरीका इस बात पर निर्भर करता है कि आपको फ़ुल स्क्रीन या आइसोलेटेड यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट की झलक देखनी है.

फ़ुल स्क्रीन की झलक देखना (AppScaffold + ScreenScaffold)

पूरी स्क्रीन की झलक देखते समय, हमेशा अपने स्क्रीन कंपोज़ेबल को AppScaffold और ScreenScaffold, दोनों में रैप करें. इसके लिए, Wear डिवाइस की झलक दिखाने वाले एनोटेशन का इस्तेमाल करें. इससे स्मार्टवॉच का डिसप्ले गोल आकार में दिखता है. साथ ही, यह पक्का होता है कि:

  • TimeText, वॉच फ़ेस के सबसे ऊपर वाले घुमावदार किनारे पर रेंडर होता है.
  • ScrollIndicator, दाईं ओर के बेज़ल के साथ दिखता है.
  • EdgeButton को सही जगह पर रखा गया है और इसे सबसे नीचे वाले कर्व पर काटा गया है.
  • कॉन्टेंट पैडिंग और सर्कुलर स्क्रीन क्लिपिंग, स्मार्टवॉच के हार्डवेयर को सटीक तरीके से दिखाती है.
@WearPreviewDevices
@Composable
fun WorkoutScreenPreview() {
    MaterialTheme {
        // AppScaffold provides the top-level TimeText overlay
        AppScaffold {
            // WorkoutScreen contains its own ScreenScaffold and content
            WorkoutScreen(
                heartRate = 142,
                elapsedTime = "12:45"
            )
        }
    }
}
WearDevices.SMALL_ROUND पर रेंडर की गई WorkoutScreenPreview

छोटा गोल (192x192 डीपी)

WearDevices.LARGE_ROUND पर WorkoutScreenPreview रेंडर किया गया

लार्ज राउंड (227x227dp)

अलग-अलग कॉम्पोनेंट की झलक देखना

कस्टम Card, Button या स्टेटस चिप जैसे अलग-अलग कॉम्पोनेंट की झलक देखते समय, device पैरामीटर को शामिल न करें. इसके बजाय, गहरे रंग के बैकग्राउंड वाले स्टैंडर्ड @Preview का इस्तेमाल करें. इससे यह पक्का किया जाता है कि Wear Material 3 के रंग और कंट्रास्ट सही तरीके से दिखें. इसके लिए, पूरी गोल घड़ी का डिसप्ले रेंडर करने की ज़रूरत नहीं होती:

@Preview(
    showBackground = true,
    backgroundColor = 0xFF000000
)
@Composable
fun HeartRateCardPreview() {
    MaterialTheme {
        HeartRateCard(bpm = 142, zone = "Aerobic")
    }
}
वॉच फ़्रेम के बिना, HeartRateCardPreview की झलक दिखाने वाला अलग कॉम्पोनेंट

अलग किए गए कॉम्पोनेंट की झलक (कोई डिवाइस फ़्रेम नहीं).


मल्टीप्रीव्यू की पहले से मौजूद टिप्पणियां

androidx.wear.compose.ui.tooling.preview पैकेज में पहले से मौजूद एनोटेशन होते हैं. ये एनोटेशन, गहरे रंग वाले बैकग्राउंड (backgroundColor = 0xFF000000, showBackground = true) और गोल स्मार्टवॉच डिवाइस के डाइमेंशन को अपने-आप कॉन्फ़िगर करते हैं:

टिप्पणी यह क्या रेंडर करता है कब इस्तेमाल करें
@WearPreviewSmallRound WearDevices.SMALL_ROUND की एक झलक (192x192 डीपी). सबसे छोटे साइज़ वाले गोलाकार डिसप्ले पर तेज़ी से बदलाव किया जा सकता है.
@WearPreviewLargeRound WearDevices.LARGE_ROUND पर एक झलक (227x227 डीपी). बड़ी स्मार्टवॉच पर लेआउट डेंसिटी और अतिरिक्त स्पेस की जांच करना.
@WearPreviewDevices दो झलक: SMALL_ROUND और LARGE_ROUND. हर स्क्रीन कंपोज़ेबल के लिए, स्टैंडर्ड मल्टीडिवाइस जांच.
@WearPreviewFontScales Wear के सभी फ़ॉन्ट स्केल में, SMALL_ROUND पर छह प्रीव्यू: छोटा (0.94f), सामान्य (1.0f), मीडियम (1.06f), बड़ा (1.12f), ज़्यादा बड़ा (1.18f), और सबसे बड़ा (1.24f). टेक्स्ट रैपिंग, टेक्स्ट छोटा करने के लिए तीन बिंदुओं का इस्तेमाल, और बटन की ऊंचाई बढ़ाने की सुविधा की जांच की जा रही है.

टेस्ट मैट्रिक्स जनरेट करने के लिए, एक ही प्रीव्यू फ़ंक्शन पर @WearPreviewDevices और @WearPreviewFontScales को स्टैक किया जा सकता है:

@WearPreviewDevices
@WearPreviewFontScales
@Composable
fun MessageDetailScreenPreview() {
    MaterialTheme {
        AppScaffold {
            MessageDetailScreen(
                sender = "Alex",
                body = "Running 5 mins late!"
            )
        }
    }
}

कस्टम प्रीव्यू एनोटेशन और हार्डवेयर की खास बातें

जब आपको ज़्यादा कंट्रोल की ज़रूरत हो, तब @Preview को सीधे तौर पर कॉन्फ़िगर किया जा सकता है. इसके अलावा, अपने हिसाब से कस्टम मल्टीप्रीव्यू एनोटेशन तय किए जा सकते हैं. जैसे, किसी खास हार्डवेयर डाइमेंशन, लंबी स्थानीय भाषा वाली स्ट्रिंग या सबसे खराब कॉम्बिनेशन की टेस्टिंग करना.

उपलब्ध WearDevices कॉन्स्टेंट और कस्टम हार्डवेयर की खास बातें

androidx.wear.tooling.preview.devices.WearDevices ऑब्जेक्ट, स्टैंडर्ड डिवाइस आईडी उपलब्ध कराता है:

  • WearDevices.SMALL_ROUND ("id:wearos_small_round", 192x192 डीपी)
  • WearDevices.LARGE_ROUND ("id:wearos_large_round", 227x227dp)

ज़्यादा बड़े साइज़ वाले गोल डिसप्ले (जैसे, 44 मि॰मी॰–45 मि॰मी॰ वाली घड़ियां या 240x240dp वाले Ultra मॉडल) पर झलक देखने के लिए, device पैरामीटर को कस्टम spec: स्ट्रिंग पास करें:

@Preview(
    name = "XL Round Watch (240dp)",
    device = "spec:width=240dp,height=240dp,dpi=320,isRound=true",
    showBackground = true,
    backgroundColor = 0xFF000000
)
@Composable
fun WorkoutScreenXlPreview() {
    MaterialTheme {
        AppScaffold {
            WorkoutScreen(heartRate = 142, elapsedTime = "12:45")
        }
    }
}

कस्टम मल्टी-प्रीव्यू एनोटेशन बनाना

किसी मुश्किल स्थिति की जांच करने के लिए, कस्टम मल्टी-प्रीव्यू एनोटेशन बनाएं. इसमें सबसे छोटी गोल स्क्रीन को सबसे बड़े फ़ॉन्ट स्केल के साथ जोड़ा गया हो. साथ ही, इसमें स्टैंडर्ड बड़ी गोल स्क्रीन के साथ-साथ, ज़्यादा शब्दों वाला कोई स्थानीय भाषा (जैसे, जर्मन) इस्तेमाल की गई हो:

@Preview(
    name = "1. Standard Large Round",
    group = "Layout extremes",
    device = WearDevices.LARGE_ROUND,
    backgroundColor = 0xFF000000,
    showBackground = true
)
@Preview(
    name = "2. Extreme Small Round (Largest Font + German)",
    group = "Layout extremes",
    device = WearDevices.SMALL_ROUND,
    fontScale = 1.24f,
    locale = "de-rDE",
    backgroundColor = 0xFF000000,
    showBackground = true
)
annotation class WearPreviewExtremes
स्टैंडर्ड लार्ज राउंड की झलक

1. स्टैंडर्ड लार्ज राउंड

सबसे बड़े फ़ॉन्ट स्केल के साथ बहुत छोटा गोल आइकॉन

2. एक्सट्रीम स्मॉल राउंड (सबसे बड़ा फ़ॉन्ट + जर्मन)


स्क्रोल किए जा सकने वाले कॉलम की झलक देखना (TransformingLazyColumn)

डिफ़ॉल्ट रूप से, TransformingLazyColumn को पहले आइटम (index = 0) के साथ शुरू किया जाता है. यह आइटम, स्क्रीन पर सबसे ऊपर पिन किया जाता है. हालांकि, Wear OS पर आइटम , स्क्रीन के ऊपर और नीचे के घुमावदार किनारों के पास पहुंचने पर, अपनी ऊंचाई और गोल किनारों (SurfaceTransformation) को बदल लेते हैं. साथ ही, EdgeButton सिर्फ़ तब दिखता है, जब सबसे नीचे स्क्रोल किया जाता है.

सूची को थोड़ा नीचे या सबसे नीचे स्क्रोल करने पर, आपकी सूची कैसी दिखेगी, इसकी झलक देखने के लिए:

पहला चरण: अपने स्क्रीन कंपोज़ेबल में TransformingLazyColumnState को ऊपर ले जाएं

अपने स्क्रीन कंपोज़ेबल को TransformingLazyColumnState पैरामीटर स्वीकार करने की अनुमति दें. इसकी डिफ़ॉल्ट वैल्यू rememberTransformingLazyColumnState() है:

@Composable
fun InboxScreen(
    messages: List<Message>,
    columnState: TransformingLazyColumnState = rememberTransformingLazyColumnState(),
) {
    val transformationSpec = rememberTransformationSpec()

    ScreenScaffold(
        scrollState = columnState,
        edgeButton = {
            EdgeButton(onClick = { /* Compose new */ }) {
                Text("New message")
            }
        }
    ) { contentPadding ->
        TransformingLazyColumn(
            state = columnState,
            contentPadding = contentPadding,
        ) {
            items(messages.size) { index ->
                Card(
                    onClick = {},
                    modifier = Modifier
                        .fillMaxWidth()
                        .transformedHeight(this, transformationSpec)
                        .minimumVerticalContentPadding(
                            CardDefaults.minimumVerticalListContentPadding
                        ),
                    transformation = SurfaceTransformation(transformationSpec),
                ) {
                    Text(messages[index].subject)
                }
            }
        }
    }
}

दूसरा चरण: अपने @Preview में initialAnchorItemIndex पास करना

rememberTransformingLazyColumnState दो वैकल्पिक शुरुआती स्क्रोल पैरामीटर स्वीकार करता है:

  • initialAnchorItemIndex: Int: जब इसे नॉन-नेगेटिव इंडेक्स (उदाहरण के लिए, 3) पर सेट किया जाता है, तो सूची उस आइटम के साथ शुरू होती है जो वॉच व्यूपोर्ट के बीच में होता है.
  • initialAnchorItemScrollOffset: Int: यह बीच में मौजूद ऐंकर आइटम के हिसाब से, पिक्सल ऑफ़सेट को लागू करने का विकल्प है.

एक ही स्क्रीन की ऊपर, बीच (स्क्रोल किया गया), और नीचे (EdgeButton दिखता है) स्थितियों को दिखाने वाली साइड-बाय-साइड झलक बनाई जा सकती हैं:

@WearPreviewLargeRound
@Composable
fun InboxScreenTopPreview() {
    MaterialTheme {
        AppScaffold {
            // Default (-1): Pinned to top of list (index 0)
            InboxScreen(messages = sampleMessages)
        }
    }
}

@WearPreviewLargeRound
@Composable
fun InboxScreenScrolledMiddlePreview() {
    MaterialTheme {
        AppScaffold {
            // Centers item index 3 in the viewport, showing top/bottom item morphing
            InboxScreen(
                messages = sampleMessages,
                columnState = rememberTransformingLazyColumnState(
                    initialAnchorItemIndex = 3
                )
            )
        }
    }
}

@WearPreviewLargeRound
@Composable
fun InboxScreenBottomEdgeButtonPreview() {
    MaterialTheme {
        AppScaffold {
            // Anchors on the last item so the EdgeButton is visible at the bottom
            InboxScreen(
                messages = sampleMessages,
                columnState = rememberTransformingLazyColumnState(
                    initialAnchorItemIndex = sampleMessages.lastIndex
                )
            )
        }
    }
}
InboxScreen को सूची में सबसे ऊपर पिन किया गया

सबसे ऊपर (डिफ़ॉल्ट -1)

InboxScreen को बीच के इंडेक्स 3 पर स्क्रोल किया गया

बीच में (initialAnchorItemIndex = 3)

इनबॉक्सस्क्रीन को सबसे नीचे तक स्क्रोल किया गया है और EdgeButton को बड़ा किया गया है

सबसे नीचे (EdgeButton बड़ा किया गया)

अहम जानकारी: Android Studio में किसी भी @Preview पर इंटरैक्टिव मोड शुरू करें पर क्लिक करके भी, TransformingLazyColumn को अपने माउस या ट्रैकपैड से स्क्रोल किया जा सकता है. साथ ही, रीयल टाइम में SurfaceTransformation मॉर्फ़िंग, EdgeButton एंट्रेंस ऐनिमेशन, और ScrollIndicator मूवमेंट की जांच की जा सकती है.

स्क्रोल कैप्चर के दौरान गार्ड ScrollIndicator (LocalScrollCaptureInProgress)

जब सिस्टम के स्क्रोल कैप्चर (बड़े स्क्रीनशॉट) या मल्टी-फ़्रेम स्क्रीनशॉट टेस्टिंग टूल, स्क्रोल करने की सुविधा वाले TransformingLazyColumn को कैप्चर करते हैं, तब Compose, LocalScrollCaptureInProgress.current को true पर सेट करता है. ऐसा तब होता है, जब वह कई व्यूपोर्ट टाइल को वर्टिकल तरीके से कैप्चर और स्टिच कर रहा होता है.

स्क्रोल कैप्चर करने के दौरान, ScreenScaffold अपने scrollIndicator को अपने-आप नहीं छिपाता है. इसलिए, लंबे स्क्रीनशॉट की हर स्टिच की गई टाइल पर फ़्लोटिंग स्क्रोलबार ओवरले बार-बार दिखेगा. ऐसा तब तक होगा, जब तक कि !LocalScrollCaptureInProgress.current का इस्तेमाल करके इसे साफ़ तौर पर छिपाया न जाए:

ScreenScaffold(
    scrollState = columnState,
    scrollIndicator = {
        if (!LocalScrollCaptureInProgress.current) {
            ScrollIndicator(state = columnState)
        }
    }
) { contentPadding ->
    // TransformingLazyColumn content...
    // ...
}