Wear OS-এর জন্য Compose-এ আপনার UI-এর প্রিভিউ দেখা

Android Studio Compose Preview আপনাকে বিভিন্ন ওয়াচ ডিসপ্লে সাইজ, গোলাকার বেজেল ও ফন্ট স্কেল জুড়ে আপনার Wear OS কম্পোজেবল সরাসরি IDE-তে পরিদর্শন ও যাচাই করতে দেয়—কোনও ফিজিক্যাল ওয়াচ বা এমুলেটরে আপনার অ্যাপ ডিপ্লয় না করেই।

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")
}

কী প্রিভিউ করবেন তা বেছে নিন: স্ক্রিন বনাম কম্পোনেন্ট

আপনি কীভাবে প্রিভিউ কনফিগার করবেন তা নির্ভর করে আপনি কোনও ফুল-স্ক্রিন বা আলাদা UI কম্পোনেন্ট প্রিভিউ করছেন কিনা তার উপর।

ফুল স্ক্রিন প্রিভিউ (AppScaffold + ScreenScaffold)

সম্পূর্ণ স্ক্রিনের প্রিভিউ দেখার সময়, Wear ডিভাইস প্রিভিউ অ্যানোটেশন ব্যবহার করে সবসময় আপনার স্ক্রিন কম্পোজ করার উপযুক্ত আইটেমকে AppScaffold ও ScreenScaffold-এর মধ্যে র‍্যাপ করুন। এটি বৃত্তাকার ওয়াচ ডিসপ্লে রেন্ডার করে এবং নিশ্চিত করে যে:

  • 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 রেন্ডার করা হয়েছে

ছোট গোলাকার (১৯২x১৯২dp)

WearDevices.LARGE_ROUND-এ WorkoutScreenPreview রেন্ডার করা হয়েছে

বড় গোলাকার (২২৭x২২৭dp)

আলাদা করা কম্পোনেন্টের প্রিভিউ

কাস্টম 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-এ ১টি প্রিভিউ (১৯২x১৯২dp)। সবচেয়ে সীমাবদ্ধ বৃত্তাকার ডিসপ্লে সাইজে দ্রুত ইটারেশন।
@WearPreviewLargeRound WearDevices.LARGE_ROUND-এ ১টি প্রিভিউ (২২৭x২২৭dp)। আরও বড় ওয়াচে লেআউট ডেনসিটি ও অতিরিক্ত স্পেসিং চেক করা।
@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", ১৯২x১৯২dp)
  • WearDevices.LARGE_ROUND ("id:wearos_large_round", 227x227dp)

অতিরিক্ত বড় গোলাকার ডিসপ্লেতে (যেমন, ৪৪ মিমি–৪৫ মিমি ওয়াচ বা ২৪০x২৪০dp-তে 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
স্ট্যান্ডার্ড বড় গোলাকার প্রিভিউ

১. স্ট্যান্ডার্ড বড় গোলাকার

সবচেয়ে বড় ফন্ট স্কেল সহ এক্সট্রিম ছোট রাউন্ড

২. এক্সট্রিম স্মল রাউন্ড (সবচেয়ে বড় ফন্ট + জার্মান)


স্ক্রল করা কলামের প্রিভিউ (TransformingLazyColumn)

ডিফল্ট হিসেবে, TransformingLazyColumn প্রথম আইটেম দিয়ে শুরু হয় (index = 0) যেটি স্ক্রিনের উপরে পিন করা থাকে। তবে, Wear OS-এ, আইটেমগুলি স্ক্রিনের উপরে ও নিচের দিকে বাঁকানো প্রান্তের কাছাকাছি পৌঁছানোর সাথে সাথে সেগুলির উচ্চতা ও গোলাকার কোণ (SurfaceTransformation) পরিবর্তিত হয় এবং EdgeButton নিচের দিকে স্ক্রল করলেই শুধুমাত্র দেখা যায়।

আপনার তালিকা অর্ধেক স্ক্রল করা হলে বা তালিকার নিচে পৌঁছালে সেটি কেমন দেখতে লাগবে তা প্রিভিউ করতে:

ধাপ ১: আপনার স্ক্রিন কম্পোজেবলে TransformingLazyColumnState হোয়েস্ট করুন

আপনার স্ক্রিন কম্পোজ করার উপযুক্ত হলে, rememberTransformingLazyColumnState() ডিফল্ট ভ্যালু সহ TransformingLazyColumnState প্যারামিটার গ্রহণ করার অনুমতি দিন:

@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)

ইনবক্স স্ক্রিন স্ক্রল করে ৩ নম্বর মিডল ইনডেক্সে গেছে

মাঝখানে (initialAnchorItemIndex = 3)

EdgeButton বড় করা অবস্থায় InboxScreen স্ক্রল করে নিচে যাওয়া হয়েছে

নিচে (EdgeButton বড় করা হয়েছে)

পরামর্শ: এছাড়াও, আপনি যেকোনও @Preview Android Studio-তে ইন্টার‍্যাক্টিভ মোড শুরু করুন বিকল্পে ক্লিক করে আপনার মাউস বা ট্র্যাকপ্যাড দিয়ে TransformingLazyColumn লাইভ স্ক্রল করতে এবং SurfaceTransformation মরফিং, EdgeButton এন্ট্রান্স অ্যানিমেশন ও ScrollIndicator রিয়েল-টাইমে মুভমেন্ট চেক করতে পারবেন।

স্ক্রল ক্যাপচার করার সময় Guard ScrollIndicator (LocalScrollCaptureInProgress)

সিস্টেম স্ক্রল ক্যাপচার (লম্বা স্ক্রিনশট) বা মাল্টি-ফ্রেম স্ক্রিনশট টেস্টিং টুল, স্ক্রল করা TransformingLazyColumn, কম্পোজ সেট LocalScrollCaptureInProgress.current থেকে true ক্যাপচার করার সময় এবং একাধিক ভিউপোর্ট টাইল ভার্টিক্যালি স্টিচ করার সময়।

কারণ, স্ক্রল ক্যাপচার করার সময় ScreenScaffold অটোমেটিক নিজের scrollIndicator লুকিয়ে রাখে না। আপনি স্পষ্টভাবে !LocalScrollCaptureInProgress.current দিয়ে এটি রক্ষা না করলে, বড় স্ক্রিনশটের প্রতিটি স্টিচ করা টাইলে ফ্লোটিং স্ক্রলবার ওভারলে বারবার দেখা যাবে:

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