پشتیبانی از اندازه های مختلف نمایشگر، پشتیبانی از اندازه های مختلف نمایشگر

پشتیبانی از اندازه‌های مختلف نمایشگر، دسترسی به برنامه شما را توسط طیف وسیعی از دستگاه‌ها و بیشترین تعداد کاربران امکان‌پذیر می‌کند.

برای پشتیبانی از حداکثر اندازه‌های نمایشگر - چه صفحه نمایش دستگاه‌های مختلف و چه پنجره‌های مختلف برنامه در حالت چند پنجره‌ای - طرح‌بندی برنامه خود را طوری طراحی کنید که واکنش‌گرا و تطبیق‌پذیر باشد. طرح‌بندی‌های واکنش‌گرا/تطبیقی، صرف نظر از اندازه نمایشگر، یک تجربه کاربری بهینه ارائه می‌دهند و برنامه شما را قادر می‌سازند تا با تلفن‌ها، تبلت‌ها، دستگاه‌های تاشو، دستگاه‌های ChromeOS، جهت‌های عمودی و افقی و پیکربندی‌های نمایشگر قابل تغییر اندازه مانند حالت تقسیم صفحه و پنجره‌بندی دسکتاپ سازگار شود.

طرح‌بندی‌های واکنش‌گرا/انطباقی بر اساس فضای نمایش موجود تغییر می‌کنند. این تغییرات از تنظیمات کوچک طرح‌بندی که فضا را پر می‌کنند (طراحی واکنش‌گرا) تا جایگزینی کامل یک طرح‌بندی با طرح‌بندی دیگر، به طوری که برنامه شما بتواند به بهترین شکل با اندازه‌های مختلف نمایشگر سازگار شود (طراحی انطباقی) متغیر است.

به عنوان یک جعبه ابزار رابط کاربری اعلانی، Jetpack Compose برای طراحی و پیاده‌سازی طرح‌بندی‌هایی که به صورت پویا تغییر می‌کنند تا محتوا را در اندازه‌های مختلف نمایشگر به طور متفاوت رندر کنند، ایده‌آل است.

ایجاد تغییرات بزرگ در طرح‌بندی برای کامپوننت‌های سطح محتوا به صورت صریح

کامپوننت‌های سطح برنامه و سطح محتوا، تمام فضای نمایش موجود برای برنامه شما را اشغال می‌کنند. برای این نوع کامپوننت‌ها، ممکن است منطقی باشد که طرح کلی برنامه خود را در نمایشگرهای بزرگ تغییر دهید.

از استفاده از مقادیر سخت‌افزار فیزیکی برای تصمیم‌گیری در مورد طرح‌بندی خودداری کنید. ممکن است تصمیم‌گیری بر اساس یک مقدار ملموس ثابت (آیا دستگاه تبلت است؟ آیا صفحه نمایش فیزیکی نسبت ابعاد خاصی دارد؟) وسوسه‌انگیز باشد، اما پاسخ به این سؤالات ممکن است برای تعیین فضای موجود برای رابط کاربری شما مفید نباشد.

شکل ۱. فرم فاکتورهای گوشی، تاشو، تبلت و لپ‌تاپ

در تبلت‌ها، یک برنامه ممکن است در حالت چند پنجره‌ای اجرا شود، به این معنی که ممکن است صفحه را با برنامه دیگری تقسیم کند. در حالت پنجره‌ای دسکتاپ یا در ChromeOS، یک برنامه ممکن است در یک پنجره با اندازه قابل تغییر باشد. حتی ممکن است بیش از یک صفحه فیزیکی وجود داشته باشد، مانند یک دستگاه تاشو. در همه این موارد، اندازه صفحه فیزیکی برای تصمیم‌گیری در مورد نحوه نمایش محتوا اهمیتی ندارد.

در عوض، بر اساس بخش واقعی صفحه نمایش اختصاص داده شده به برنامه خود که توسط معیارهای پنجره فعلی ارائه شده توسط کتابخانه Jetpack WindowManager توصیف شده است، تصمیم گیری کنید. برای مثالی از نحوه استفاده از WindowManager در یک برنامه Compose، به نمونه JetNews مراجعه کنید.

تطبیق طرح‌بندی‌های شما با فضای نمایشگر موجود، میزان رسیدگی ویژه مورد نیاز برای پشتیبانی از پلتفرم‌هایی مانند ChromeOS و فرم‌فکتورهایی مانند تبلت‌ها و دستگاه‌های تاشو را نیز کاهش می‌دهد.

وقتی معیارهای فضای موجود برای برنامه خود را تعیین کردید، اندازه خام را همانطور که در بخش «استفاده از کلاس‌های اندازه پنجره» توضیح داده شده است، به یک کلاس اندازه پنجره تبدیل کنید. کلاس‌های اندازه پنجره، نقاط شکستی هستند که برای ایجاد تعادل بین سادگی منطق برنامه و انعطاف‌پذیری برای بهینه‌سازی برنامه شما برای اکثر اندازه‌های نمایشگر طراحی شده‌اند.

کلاس‌های اندازه پنجره به کل پنجره برنامه شما اشاره دارند، بنابراین از کلاس‌ها برای تصمیمات مربوط به طرح‌بندی که بر طرح‌بندی کلی برنامه شما تأثیر می‌گذارند، استفاده کنید. می‌توانید کلاس‌های اندازه پنجره را به عنوان حالت ارسال کنید، یا می‌توانید منطق اضافی را برای ایجاد حالت مشتق شده برای ارسال به composable های تو در تو انجام دهید.

@Composable
fun MyApp(
    windowSizeClass: WindowSizeClass = currentWindowAdaptiveInfo().windowSizeClass
) {
    // Decide whether to show the top app bar based on window size class.
    val showTopAppBar = windowSizeClass.isHeightAtLeastBreakpoint(WindowSizeClass.HEIGHT_DP_MEDIUM_LOWER_BOUND)

    // MyScreen logic is based on the showTopAppBar boolean flag.
    MyScreen(
        showTopAppBar = showTopAppBar,
        /* ... */
    )
}

یک رویکرد لایه‌ای، منطق اندازه نمایش را به یک مکان واحد محدود می‌کند، به جای اینکه آن را در مکان‌های مختلفی از برنامه که نیاز به همگام‌سازی دارند، پراکنده کند. یک مکان واحد، حالت را تولید می‌کند که می‌تواند به صراحت مانند هر حالت برنامه دیگر به سایر composableها منتقل شود. ارسال صریح حالت، composableهای منفرد را ساده می‌کند، زیرا composableها کلاس اندازه پنجره یا پیکربندی مشخص شده را به همراه سایر داده‌ها می‌گیرند.

کامپوننت‌های تو در تو و انعطاف‌پذیر، قابل استفاده مجدد هستند.

کامپوننت‌های ترکیبی وقتی می‌توانند در مکان‌های متنوعی قرار گیرند، قابلیت استفاده مجدد بیشتری دارند. اگر یک کامپوننت ترکیبی باید در یک مکان خاص با اندازه خاص قرار گیرد، بعید است که در زمینه‌های دیگر قابل استفاده مجدد باشد. این همچنین بدان معنی است که کامپوننت‌های ترکیبی قابل استفاده مجدد و منفرد باید از وابستگی ضمنی به اطلاعات اندازه نمایش جهانی اجتناب کنند.

یک کامپوننت تو در تو را تصور کنید که طرح‌بندی list-detail را پیاده‌سازی می‌کند، که می‌تواند یک یا دو پنل را در کنار هم نشان دهد:

برنامه‌ای که دو پنجره را در کنار هم نشان می‌دهد.
شکل ۲. برنامه‌ای که یک طرح‌بندی معمول لیست-جزئیات را نشان می‌دهد - ۱ ناحیه لیست است؛ ۲ ناحیه جزئیات.

تصمیم مربوط به جزئیات لیست باید بخشی از طرح کلی برنامه باشد، بنابراین این تصمیم از یک کامپوننت سطح محتوا به پایین منتقل می‌شود:

@Composable
fun AdaptivePane(
    showOnePane: Boolean,
    /* ... */
) {
    if (showOnePane) {
        OnePane(/* ... */)
    } else {
        TwoPane(/* ... */)
    }
}

اگر بخواهید یک composable به طور مستقل طرح‌بندی خود را بر اساس فضای نمایش موجود تغییر دهد، مثلاً کارتی که در صورت وجود فضا، جزئیات بیشتری را نشان می‌دهد، چه؟ شما می‌خواهید بر اساس اندازه نمایش موجود، منطقی را اجرا کنید، اما دقیقاً کدام اندازه؟

شکل ۳. کارت باریک که فقط یک آیکون و عنوان را نشان می‌دهد، و کارت پهن‌تر که آیکون، عنوان و توضیحات کوتاه را نشان می‌دهد.

از تلاش برای استفاده از اندازه صفحه نمایش واقعی دستگاه خودداری کنید. این کار برای انواع مختلف صفحه نمایش دقیق نخواهد بود و همچنین اگر برنامه تمام صفحه نباشد، دقیق نخواهد بود.

از آنجا که ترکیب‌پذیر، ترکیب‌پذیر در سطح محتوا نیست، مستقیماً از معیارهای پنجره فعلی استفاده نکنید.

اگر کامپوننت با فاصله‌گذاری (padding) قرار داده شده باشد (مانند inset)، یا اگر برنامه شامل کامپوننت‌هایی مانند navigation rails یا app bar باشد، مقدار فضای نمایش موجود برای composable ممکن است با فضای کلی موجود برای برنامه تفاوت قابل توجهی داشته باشد.

از عرضی که در واقع به composable داده شده است برای رندر کردن خودش استفاده کنید. شما دو گزینه برای بدست آوردن آن عرض دارید:

  • اگر می‌خواهید محل یا نحوه نمایش محتوا را تغییر دهید، از مجموعه‌ای از اصلاح‌کننده‌ها یا یک طرح‌بندی سفارشی برای واکنش‌گرا کردن طرح‌بندی استفاده کنید. این کار می‌تواند به سادگیِ پر کردن تمام فضای موجود توسط یک عنصر فرزند یا چیدمان چند ستونی در صورت وجود فضای کافی باشد.

  • اگر می‌خواهید آنچه را که نشان می‌دهید تغییر دهید، BoxWithConstraints به عنوان یک جایگزین قدرتمندتر استفاده کنید. BoxWithConstraints محدودیت‌های اندازه‌گیری را فراهم می‌کند که می‌توانید از آنها برای فراخوانی composableهای مختلف بر اساس فضای نمایش موجود استفاده کنید. با این حال، این کار هزینه‌ای دارد، زیرا BoxWithConstraints ترکیب را تا مرحله طرح‌بندی، زمانی که این محدودیت‌ها شناخته می‌شوند، به تعویق می‌اندازد و باعث می‌شود کار بیشتری در طول طرح‌بندی انجام شود.

@Composable
fun Card(/* ... */) {
    BoxWithConstraints {
        if (maxWidth < 400.dp) {
            Column {
                Image(/* ... */)
                Title(/* ... */)
            }
        } else {
            Row {
                Column {
                    Title(/* ... */)
                    Description(/* ... */)
                }
                Image(/* ... */)
            }
        }
    }
}

همه داده‌ها را برای اندازه‌های مختلف نمایشگر در دسترس قرار دهید

هنگام پیاده‌سازی یک composable که از فضای نمایش اضافی بهره می‌برد، ممکن است وسوسه شوید که کارآمد باشید و داده‌ها را به عنوان یک اثر جانبی از اندازه نمایش فعلی بارگذاری کنید.

با این حال، انجام این کار برخلاف اصل جریان داده یک‌طرفه است، که در آن داده‌ها می‌توانند جمع‌آوری شده و برای رندر مناسب در اختیار کامپوننت‌ها قرار گیرند. باید داده‌های کافی در اختیار کامپوننت قرار گیرد تا کامپوننت همیشه محتوای کافی برای هر اندازه نمایشگر داشته باشد، حتی اگر بخشی از محتوا همیشه استفاده نشود.

@Composable
fun Card(
    imageUrl: String,
    title: String,
    description: String
) {
    BoxWithConstraints {
        if (maxWidth < 400.dp) {
            Column {
                Image(imageUrl)
                Title(title)
            }
        } else {
            Row {
                Column {
                    Title(title)
                    Description(description)
                }
                Image(imageUrl)
            }
        }
    }
}

با تکیه بر مثال Card ، توجه داشته باشید که description همیشه به Card ارسال می‌شود. اگرچه description فقط زمانی استفاده می‌شود که عرض اجازه نمایش آن را بدهد، Card همیشه به description نیاز دارد، صرف نظر از عرض موجود.

ارسال مداوم محتوای کافی، با کاهش وابستگی به state، طرح‌بندی‌های تطبیقی ​​را ساده‌تر می‌کند و از ایجاد عوارض جانبی هنگام تغییر اندازه صفحه نمایش (که ممکن است به دلیل تغییر اندازه پنجره، تغییر جهت یا تا کردن و باز کردن دستگاه رخ دهد) جلوگیری می‌کند.

این اصل همچنین امکان حفظ وضعیت (state) را در طول تغییرات طرح‌بندی (layout) فراهم می‌کند. با جمع‌آوری اطلاعاتی که ممکن است در همه اندازه‌های نمایشگر استفاده نشوند، می‌توانید وضعیت برنامه را با تغییر اندازه طرح‌بندی حفظ کنید.

برای مثال، می‌توانید یک پرچم بولی showMore را بالا ببرید تا وضعیت برنامه هنگام تغییر اندازه صفحه نمایش که باعث می‌شود طرح‌بندی بین پنهان کردن و نمایش محتوا تغییر کند، حفظ شود:

@Composable
fun Card(
    imageUrl: String,
    title: String,
    description: String
) {
    var showMore by remember { mutableStateOf(false) }

    BoxWithConstraints {
        if (maxWidth < 400.dp) {
            Column {
                Image(imageUrl)
                Title(title)
            }
        } else {
            Row {
                Column {
                    Title(title)
                    Description(
                        description = description,
                        showMore = showMore,
                        onShowMoreToggled = { newValue ->
                            showMore = newValue
                        }
                    )
                }
                Image(imageUrl)
            }
        }
    }
}

بیشتر بدانید

برای کسب اطلاعات بیشتر در مورد طرح‌بندی‌های تطبیقی ​​در Compose، به منابع زیر مراجعه کنید:

برنامه‌های نمونه

  • CanonicalLayouts مخزنی از الگوهای طراحی اثبات‌شده است که تجربه کاربری بهینه‌ای را در نمایشگرهای بزرگ ارائه می‌دهد.
  • جت‌نیوز نشان می‌دهد که چگونه می‌توان اپلیکیشنی طراحی کرد که رابط کاربری خود را برای استفاده از فضای نمایش موجود تطبیق دهد.
  • پاسخ یک نمونه تطبیقی ​​برای پشتیبانی از موبایل، تبلت و دستگاه‌های تاشو است
  • اکنون در اندروید برنامه‌ای وجود دارد که از طرح‌بندی‌های تطبیقی ​​برای پشتیبانی از اندازه‌های مختلف نمایشگر استفاده می‌کند.

ویدیوها