Material Design 2 در Compose

Jetpack Compose پیاده‌سازی از طراحی متریال (Material Design)، یک سیستم طراحی جامع برای ایجاد رابط‌های دیجیتال، را ارائه می‌دهد. اجزای طراحی متریال (دکمه‌ها، کارت‌ها، سوئیچ‌ها و غیره) بر اساس قالب‌بندی متریال (Material Theming ) ساخته شده‌اند که روشی سیستماتیک برای سفارشی‌سازی طراحی متریال برای انعکاس بهتر برند محصول شما است. یک قالب متریال شامل ویژگی‌های رنگ ، تایپوگرافی و شکل است. هنگامی که این ویژگی‌ها را سفارشی می‌کنید، تغییرات شما به طور خودکار در اجزایی که برای ساخت برنامه خود استفاده می‌کنید، منعکس می‌شود.

Jetpack Compose این مفاهیم را با MaterialTheme composable پیاده‌سازی می‌کند:

MaterialTheme(
    colors = // ...
    typography = // ...
    shapes = // ...
) {
    // app content
}

پارامترهایی را که برای تم‌بندی برنامه‌تان به MaterialTheme ارسال می‌کنید، پیکربندی کنید.

دو اسکرین‌شات متضاد. اولی از استایل پیش‌فرض MaterialTheme استفاده می‌کند، و دومی از استایل اصلاح‌شده.
شکل ۱. تصویر اول برنامه‌ای را نشان می‌دهد که `MaterialTheme` را پیکربندی نکرده و بنابراین از استایل‌بندی پیش‌فرض استفاده می‌کند. تصویر دوم برنامه‌ای را نشان می‌دهد که پارامترهایی را برای سفارشی‌سازی استایل‌بندی به `MaterialTheme` ارسال می‌کند.

رنگ

رنگ‌ها در Compose با کلاس Color ، یک کلاس نگهدارنده داده، مدل‌سازی می‌شوند.

val Red = Color(0xffff0000)
val Blue = Color(red = 0f, green = 0f, blue = 1f)

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

نمونه‌ای از پالت رنگ تم
شکل ۲. سیستم رنگ متریال.

Compose کلاس Colors را برای مدل‌سازی سیستم رنگ Material ارائه می‌دهد. Colors توابع سازنده را برای ایجاد مجموعه‌هایی از رنگ‌های روشن یا تیره فراهم می‌کند:

private val Yellow200 = Color(0xffffeb46)
private val Blue200 = Color(0xff91a4fc)
// ...

private val DarkColors = darkColors(
    primary = Yellow200,
    secondary = Blue200,
    // ...
)
private val LightColors = lightColors(
    primary = Yellow500,
    primaryVariant = Yellow400,
    secondary = Blue700,
    // ...
)

بعد از اینکه Colors خود را تعریف کردید، می‌توانید آنها را به MaterialTheme ارسال کنید:

MaterialTheme(
    colors = if (darkTheme) DarkColors else LightColors
) {
    // app content
}

از رنگ‌های تم استفاده کنید

شما می‌توانید Colors ارائه شده به MaterialTheme composable را با استفاده از MaterialTheme.colors بازیابی کنید.

Text(
    text = "Hello theming",
    color = MaterialTheme.colors.primary
)

رنگ سطح و محتوا

بسیاری از کامپوننت‌ها یک جفت رنگ و رنگ محتوا را می‌پذیرند:

Surface(
    color = MaterialTheme.colors.surface,
    contentColor = contentColorFor(color),
    // ...
) { /* ... */ }

TopAppBar(
    backgroundColor = MaterialTheme.colors.primarySurface,
    contentColor = contentColorFor(backgroundColor),
    // ...
) { /* ... */ }

این به شما امکان می‌دهد نه تنها رنگ یک composable را تنظیم کنید، بلکه یک رنگ پیش‌فرض برای محتوا، composableهای موجود در آن، نیز ارائه دهید. بسیاری از composableها به طور پیش‌فرض از این رنگ محتوا استفاده می‌کنند. به عنوان مثال، Text رنگ خود را بر اساس رنگ محتوای والد خود تعیین می‌کند و Icon از آن رنگ برای تنظیم ته رنگ خود استفاده می‌کند.

دو نمونه از یک بنر، با رنگ‌های متفاوت
شکل ۳. تنظیم رنگ‌های پس‌زمینه مختلف، رنگ‌های متن و آیکون متفاوتی ایجاد می‌کند.

متد contentColorFor() رنگ مناسب "روشن" را برای هر رنگ تم بازیابی می‌کند. برای مثال، اگر یک رنگ پس‌زمینه primary را روی Surface تنظیم کنید، از این تابع برای تنظیم onPrimary به عنوان رنگ محتوا استفاده می‌کند. اگر یک رنگ پس‌زمینه غیر تم تنظیم کنید، باید یک رنگ محتوای مناسب نیز مشخص کنید. از LocalContentColor برای بازیابی رنگ محتوای ترجیحی برای پس‌زمینه فعلی، در یک موقعیت مشخص در سلسله مراتب، استفاده کنید.

آلفای محتوا

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

Jetpack Compose این را با استفاده از LocalContentAlpha پیاده‌سازی می‌کند. شما می‌توانید با ارائه مقداری برای CompositionLocal یک آلفای محتوا برای یک سلسله مراتب مشخص کنید. Composableهای تو در تو می‌توانند از این مقدار برای اعمال آلفا به محتوای خود استفاده کنند. به عنوان مثال، Text و Icon به طور پیش‌فرض از ترکیب LocalContentColor تنظیم شده برای استفاده از LocalContentAlpha استفاده می‌کنند. Material برخی از مقادیر آلفای استاندارد ( high ، medium ، disabled ) را مشخص می‌کند که توسط شیء ContentAlpha مدل‌سازی می‌شوند.

// By default, both Icon & Text use the combination of LocalContentColor &
// LocalContentAlpha. De-emphasize content by setting content alpha
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
    Text(
        // ...
    )
}
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) {
    Icon(
        // ...
    )
    Text(
        // ...
    )
}

برای کسب اطلاعات بیشتر در مورد CompositionLocal ، به داده‌های محلی با CompositionLocal مراجعه کنید.

تصویر صفحه عنوان مقاله، که سطوح مختلف تأکید متن را نشان می‌دهد
شکل ۴. سطوح مختلف تأکید را برای متن اعمال کنید تا سلسله مراتب اطلاعات را به صورت بصری منتقل کنید. خط اول متن عنوان است و مهمترین اطلاعات را دارد، بنابراین از ContentAlpha.high استفاده می‌کند. خط دوم شامل فراداده‌های کم‌اهمیت‌تر است، بنابراین از ContentAlpha.medium استفاده می‌کند.

تم تاریک

در Compose، شما با ارائه مجموعه‌های مختلفی از Colors به MaterialTheme composable، تم‌های روشن و تیره را پیاده‌سازی می‌کنید:

@Composable
fun MyTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    MaterialTheme(
        colors = if (darkTheme) DarkColors else LightColors,
        /*...*/
        content = content
    )
}

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

می‌توانید از کدی مانند این برای بررسی روشن یا تیره بودن Colors فعلی استفاده کنید:

val isLightTheme = MaterialTheme.colors.isLight
Icon(
    painterResource(
        id = if (isLightTheme) {
            R.drawable.ic_sun_24
        } else {
            R.drawable.ic_moon_24
        }
    ),
    contentDescription = "Theme"
)

پوشش‌های ارتفاعی

در متریال، سطوح در تم‌های تیره با ارتفاعات بالاتر، پوشش‌های ارتفاعی دریافت می‌کنند که پس‌زمینه آنها را روشن‌تر می‌کند. هرچه ارتفاع یک سطح بیشتر باشد (آن را به یک منبع نور ضمنی نزدیک‌تر کند)، آن سطح روشن‌تر می‌شود.

کامپوننت Surface هنگام استفاده از رنگ‌های تیره، این پوشش‌ها را به طور خودکار اعمال می‌کند، و هر کامپوننت Material دیگری که از یک سطح استفاده می‌کند نیز به همین صورت عمل می‌کند:

Surface(
    elevation = 2.dp,
    color = MaterialTheme.colors.surface, // color will be adjusted for elevation
    /*...*/
) { /*...*/ }

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

برای سناریوهای سفارشی که شامل Surface نمی‌شوند، از LocalElevationOverlay ، یک CompositionLocal حاوی ElevationOverlay مورد استفاده توسط اجزای Surface استفاده کنید:

// Elevation overlays
// Implemented in Surface (and any components that use it)
val color = MaterialTheme.colors.surface
val elevation = 4.dp
val overlaidColor = LocalElevationOverlay.current?.apply(
    color, elevation
)

برای غیرفعال کردن همپوشانی‌های ارتفاعی، در نقطه انتخاب شده در یک سلسله مراتب قابل ترکیب، مقدار null وارد کنید:

MyTheme {
    CompositionLocalProvider(LocalElevationOverlay provides null) {
        // Content without elevation overlays
    }
}

رنگ‌های محدود

متریال توصیه می‌کند که برای تم‌های تیره، با ترجیح استفاده از رنگ surface به جای رنگ primary در بیشتر موارد، از رنگ‌های محدود استفاده شود. کامپوننت‌های متریال مانند TopAppBar و BottomNavigation این رفتار را به طور پیش‌فرض پیاده‌سازی می‌کنند.

اسکرین‌شات از تم تیره متریال، که نوار بالای برنامه را با استفاده از رنگ سطحی به جای رنگ اصلی برای تأکید بر رنگ‌های محدود نشان می‌دهد.
شکل ۶. تم تیره متریال با لهجه‌های رنگی محدود. نوار بالای برنامه در تم روشن از رنگ اصلی و در تم تیره از رنگ سطح استفاده می‌کند.

برای سناریوهای سفارشی، از ویژگی افزونه primarySurface استفاده کنید:

Surface(
    // Switches between primary in light theme and surface in dark theme
    color = MaterialTheme.colors.primarySurface,
    /*...*/
) { /*...*/ }

تایپوگرافی

Material یک سیستم نوع تعریف می‌کند و شما را تشویق می‌کند که از تعداد کمی از سبک‌های نامگذاری‌شده از نظر معنایی استفاده کنید.

نمونه‌ای از چندین فونت مختلف در سبک‌های مختلف
شکل ۷. سیستم نوع مواد.

Compose سیستم نوع را با کلاس‌های Typography ، TextStyle و کلاس‌های مرتبط با فونت پیاده‌سازی می‌کند. سازنده‌ی Typography برای هر سبک پیش‌فرض‌هایی ارائه می‌دهد، بنابراین می‌توانید هر کدام را که نمی‌خواهید سفارشی کنید، حذف کنید:

val raleway = FontFamily(
    Font(R.font.raleway_regular),
    Font(R.font.raleway_medium, FontWeight.W500),
    Font(R.font.raleway_semibold, FontWeight.SemiBold)
)

val myTypography = Typography(
    h1 = TextStyle(
        fontFamily = raleway,
        fontWeight = FontWeight.W300,
        fontSize = 96.sp
    ),
    body1 = TextStyle(
        fontFamily = raleway,
        fontWeight = FontWeight.W600,
        fontSize = 16.sp
    )
    /*...*/
)
MaterialTheme(typography = myTypography, /*...*/) {
    /*...*/
}

اگر می‌خواهید از یک فونت در کل متن استفاده کنید، پارامتر defaultFontFamily را مشخص کنید و fontFamily مربوط به هر عنصر TextStyle را حذف کنید:

val typography = Typography(defaultFontFamily = raleway)
MaterialTheme(typography = typography, /*...*/) {
    /*...*/
}

استفاده از سبک‌های متن

عناصر TextStyle با استفاده از MaterialTheme.typography قابل دسترسی هستند. عناصر TextStyle را به صورت زیر بازیابی کنید:

Text(
    text = "Subtitle2 styled",
    style = MaterialTheme.typography.subtitle2
)

تصویر صفحه‌ای که ترکیبی از فونت‌های مختلف را برای اهداف مختلف نشان می‌دهد
شکل ۸. از مجموعه‌ای از فونت‌ها و سبک‌ها برای بیان برند خود استفاده کنید.

شکل

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

انواع شکل‌های طراحی متریال را نشان می‌دهد
شکل ۹. سیستم شکل‌دهی مواد.

Compose سیستم شکل را با کلاس Shapes پیاده‌سازی می‌کند، که به شما امکان می‌دهد برای هر دسته اندازه، یک CornerBasedShape مشخص کنید:

val shapes = Shapes(
    small = RoundedCornerShape(percent = 50),
    medium = RoundedCornerShape(0f),
    large = CutCornerShape(
        topStart = 16.dp,
        topEnd = 0.dp,
        bottomEnd = 0.dp,
        bottomStart = 16.dp
    )
)

MaterialTheme(shapes = shapes, /*...*/) {
    /*...*/
}

بسیاری از کامپوننت‌ها به طور پیش‌فرض از این شکل‌ها استفاده می‌کنند. برای مثال، Button ، TextField و FloatingActionButton به طور پیش‌فرض کوچک، AlertDialog به طور پیش‌فرض متوسط ​​و ModalDrawer به طور پیش‌فرض بزرگ هستند - برای نگاشت کامل به مرجع طرح شکل مراجعه کنید.

از شکل‌ها استفاده کنید

عناصر Shape با استفاده از MaterialTheme.shapes قابل دسترسی هستند. عناصر Shape را با کدی مانند این بازیابی کنید:

Surface(
    shape = MaterialTheme.shapes.medium, /*...*/
) {
    /*...*/
}

تصویر برنامه‌ای که از اشکال متریال برای نمایش وضعیت یک عنصر استفاده می‌کند
شکل ۱۰. از اشکال برای بیان برند یا حالت استفاده کنید.

سبک‌های پیش‌فرض

هیچ مفهوم معادلی در Compose برای استایل‌های پیش‌فرض از Android Views وجود ندارد. شما می‌توانید با ایجاد توابع Composable overload خودتان که کامپوننت‌های Material را در بر می‌گیرند، عملکرد مشابهی را ارائه دهید. به عنوان مثال، برای ایجاد استایل دکمه، یک دکمه را در تابع Composable خودتان در بر بگیرید، پارامترهایی را که می‌خواهید یا نیاز به تغییر دارند، مستقیماً تنظیم کنید و سایر پارامترها را به عنوان پارامترهایی در Composable حاوی آن قرار دهید.

@Composable
fun MyButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    content: @Composable RowScope.() -> Unit
) {
    Button(
        colors = ButtonDefaults.buttonColors(
            backgroundColor = MaterialTheme.colors.secondary
        ),
        onClick = onClick,
        modifier = modifier,
        content = content
    )
}

پوشش‌های تم

شما می‌توانید با تودرتو کردن Composableهای MaterialTheme ، معادل پوشش‌های تم از Android Views را در Compose به دست آورید. از آنجا که MaterialTheme رنگ‌ها، تایپوگرافی و شکل‌ها را به طور پیش‌فرض روی مقدار تم فعلی تنظیم می‌کند، تمام پارامترهای دیگر وقتی یک تم فقط یکی از آن پارامترها را تنظیم می‌کند، مقادیر پیش‌فرض خود را حفظ می‌کنند.

علاوه بر این، هنگام انتقال صفحات نمایش مبتنی بر View به Compose، مراقب کاربردهای ویژگی android:theme باشید. احتمالاً به یک MaterialTheme جدید در آن بخش از درخت رابط کاربری Compose نیاز دارید.

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

تصویر صفحه برنامه‌ای که تم‌های تو در تو را نشان می‌دهد، با تم صورتی برای صفحه اصلی و تم آبی برای بخش مرتبط
شکل ۱۱. قالب‌های تو در تو.

@Composable
fun DetailsScreen(/* ... */) {
    PinkTheme {
        // other content
        RelatedSection()
    }
}

@Composable
fun RelatedSection(/* ... */) {
    BlueTheme {
        // content
    }
}

حالت‌های کامپوننت

اجزای متریال که می‌توانند با آنها تعامل داشته باشند (کلیک شوند، تغییر وضعیت دهند و غیره) می‌توانند در حالت‌های بصری مختلفی باشند. حالت‌ها شامل فعال، غیرفعال، فشرده شده و غیره می‌شوند.

کامپوننت‌های ترکیبی اغلب یک پارامتر enabled دارند. تنظیم آن روی false از تعامل جلوگیری می‌کند و ویژگی‌هایی مانند رنگ و ارتفاع را تغییر می‌دهد تا حالت کامپوننت را به صورت بصری منتقل کند.

تصویر دو دکمه: یکی فعال، یکی غیرفعال، که حالت‌های بصری مختلف آنها را نشان می‌دهد
شکل ۱۲. دکمه‌ای با enabled = true (چپ) و enabled = false (راست).

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

Button(
    onClick = { /* ... */ },
    enabled = true,
    // Custom colors for different states
    colors = ButtonDefaults.buttonColors(
        backgroundColor = MaterialTheme.colors.secondary,
        disabledBackgroundColor = MaterialTheme.colors.onBackground
            .copy(alpha = 0.2f)
            .compositeOver(MaterialTheme.colors.background)
        // Also contentColor and disabledContentColor
    ),
    // Custom elevation for different states
    elevation = ButtonDefaults.elevation(
        defaultElevation = 8.dp,
        disabledElevation = 2.dp,
        // Also pressedElevation
    )
) { /* ... */ }

تصویر دو دکمه با رنگ و ارتفاع تنظیم‌شده برای حالت‌های فعال و غیرفعال
شکل ۱۳. دکمه‌ای با enabled = true (چپ) و enabled = false (راست)، به همراه مقادیر رنگ و ارتفاع تنظیم‌شده.

موج‌ها

اجزای متریال از موج‌ها برای نشان دادن تعامل با خود استفاده می‌کنند. اگر در سلسله مراتب خود از MaterialTheme استفاده می‌کنید، یک Ripple به عنوان Indication پیش‌فرض درون اصلاح‌کننده‌هایی مانند clickable و indication استفاده می‌شود.

در بیشتر موارد می‌توانید به Ripple پیش‌فرض اعتماد کنید. اگر نیاز به پیکربندی ظاهر آنها دارید، می‌توانید از RippleTheme برای تغییر ویژگی‌هایی مانند رنگ و آلفا استفاده کنید.

شما می‌توانید RippleTheme بسط دهید و از توابع کاربردی defaultRippleColor و defaultRippleAlpha استفاده کنید. سپس می‌توانید با استفاده از LocalRippleTheme ، تم ripple سفارشی خود را در سلسله مراتب خود ارائه دهید:

@Composable
fun MyApp() {
    MaterialTheme {
        CompositionLocalProvider(
            LocalRippleTheme provides SecondaryRippleTheme
        ) {
            // App content
        }
    }
}

@Immutable
private object SecondaryRippleTheme : RippleTheme {
    @Composable
    override fun defaultColor() = RippleTheme.defaultRippleColor(
        contentColor = MaterialTheme.colors.secondary,
        lightTheme = MaterialTheme.colors.isLight
    )

    @Composable
    override fun rippleAlpha() = RippleTheme.defaultRippleAlpha(
        contentColor = MaterialTheme.colors.secondary,
        lightTheme = MaterialTheme.colors.isLight
    )
}

انیمیشن GIF که دکمه‌هایی با جلوه‌های موجی مختلف هنگام ضربه زدن را نشان می‌دهد
شکل ۱۴. دکمه‌هایی با مقادیر موج متفاوت که با استفاده از RippleTheme ارائه شده‌اند.

بیشتر بدانید

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

کدلبز

ویدیوها

{% کلمه به کلمه %} {% فعل کمکی %} {% کلمه به کلمه %} {% فعل کمکی %}