۱. قبل از شروع
در این آزمایشگاه کد، یاد خواهید گرفت که چگونه رنگهای سفارشی خود را با رنگهای تولید شده توسط یک قالب پویا هماهنگ کنید.
پیشنیازها
توسعهدهندگان باید باشند
- آشنایی با مفاهیم اولیه تم گذاری در اندروید
- کار راحت با Viewهای ویجت اندروید و ویژگیهای آنها
آنچه یاد خواهید گرفت
- نحوه استفاده از هماهنگی رنگ در برنامه خود با استفاده از چندین روش
- چگونه هارمونی کار میکند و چگونه رنگ را تغییر میدهد
آنچه نیاز دارید
- یک کامپیوتر با سیستم عامل اندروید اگر مایل به ادامه هستید.
۲. بررسی اجمالی برنامه
Voyaĝi یک اپلیکیشن حمل و نقل عمومی است که از قبل از یک تم پویا استفاده میکند. برای بسیاری از سیستمهای حمل و نقل عمومی، رنگ یک شاخص مهم برای قطارها، اتوبوسها یا ترامواها است و نمیتوان آنها را با هیچ رنگ پویای اولیه، ثانویه یا ثالثیه موجود جایگزین کرد. ما کار خود را بر روی RecyclerView کارتهای حمل و نقل رنگی متمرکز خواهیم کرد.

۳. تولید یک قالب
ما توصیه میکنیم از ابزار Material Theme Builder به عنوان اولین گام برای ساخت یک تم Material3 استفاده کنید. در تب سفارشی، اکنون میتوانید رنگهای بیشتری به تم خود اضافه کنید. در سمت راست، نقشهای رنگی و پالتهای تُنال برای آن رنگها به شما نشان داده میشود.
در بخش رنگهای توسعهیافته، میتوانید رنگها را حذف یا تغییر نام دهید.

منوی خروجی تعدادی از گزینههای خروجی ممکن را نمایش میدهد. در زمان نگارش این مطلب، تنظیمات ویژه هماهنگسازی Material Theme Builder فقط در Android Views در دسترس است.

درک ارزشهای جدید صادراتی
برای اینکه بتوانید از این رنگها و نقشهای رنگی مرتبط با آنها در قالبهای خود، چه بخواهید هماهنگسازی کنید و چه نه، استفاده کنید، فایل دانلود شدهی اکسپورت شده اکنون شامل یک فایل attrs.xml است که شامل نام نقشهای رنگی برای هر رنگ سفارشی است.
<resources>
<attr name="colorCustom1" format="color" />
<attr name="colorOnCustom1" format="color" />
<attr name="colorCustom1Container" format="color" />
<attr name="colorOnCustom1Container" format="color" />
<attr name="harmonizeCustom1" format="boolean" />
<attr name="colorCustom2" format="color" />
<attr name="colorOnCustom2" format="color" />
<attr name="colorCustom2Container" format="color" />
<attr name="colorOnCustom2Container" format="color" />
<attr name="harmonizeCustom2" format="boolean" />
</resources>
در فایل themes.xml، ما چهار نقش رنگ برای هر رنگ سفارشی ( color<name>, colorOn<name>, color<name>Container, and colorOn<nameContainer> ) ایجاد کردهایم. ویژگیهای harmonize<name> نشان میدهند که آیا توسعهدهنده این گزینه را در Material Theme Builder انتخاب کرده است یا خیر. این گزینه، رنگ را در قالب اصلی تغییر نخواهد داد.
<resources>
<style name="AppTheme" parent="Theme.Material3.Light.NoActionBar">
<!--- Normal theme attributes ... -->
<item name="colorCustom1">#006876</item>
<item name="colorOnCustom1">#ffffff</item>
<item name="colorCustom1Container">#97f0ff</item>
<item name="colorOnCustom1Container">#001f24</item>
<item name="harmonizeCustom1">false</item>
<item name="colorCustom2">#016e00</item>
<item name="colorOnCustom2">#ffffff</item>
<item name="colorCustom2Container">#78ff57</item>
<item name="colorOnCustom2Container">#002200</item>
<item name="harmonizeCustom2">false</item>
</style>
</resources>
در فایل colors.xml ، رنگهای اولیهای که برای تولید نقشهای رنگی ذکر شده در بالا استفاده میشوند، به همراه مقادیر بولی برای اینکه آیا پالت رنگ جابجا خواهد شد یا خیر، مشخص شدهاند.
<resources>
<!-- other colors used in theme -->
<color name="custom1">#1AC9E0</color>
<color name="custom2">#32D312</color>
</resources>
۴. بررسی رنگ سفارشی
با بزرگنمایی پنل کناری Material Theme Builder، میتوانیم ببینیم که اضافه کردن یک رنگ سفارشی، پنلی با چهار نقش رنگ کلیدی در یک پالت روشن و تیره را نمایش میدهد.

در نمای اندروید، ما این رنگها را برای شما صادر میکنیم، اما در پشت صحنه میتوانند توسط نمونهای از شیء ColorRoles نمایش داده شوند.
کلاس ColorRoles چهار ویژگی دارد: accent ، onAccent ، accentContainer و onAccentContainer . این ویژگیها نمایش صحیح چهار رنگ هگزادسیمال هستند.
public final class ColorRoles {
private final int accent;
private final int onAccent;
private final int accentContainer;
private final int onAccentContainer;
// truncated code
}
شما میتوانید چهار نقش رنگ کلیدی را از یک رنگ دلخواه در زمان اجرا با استفاده از getColorRoles در کلاس MaterialColors به نام getColorRoles بازیابی کنید که به شما امکان میدهد آن مجموعه از چهار نقش رنگ را در زمان اجرا با توجه به یک رنگ دانه خاص ایجاد کنید.
public static ColorRoles getColorRoles(
@NonNull Context context,
@ColorInt int color
) { /* implementation */ }
به همین ترتیب، مقادیر خروجی، مقادیر واقعی رنگ هستند، نه اشارهگر به آنها.**
۵. هماهنگی رنگ چیست؟
سیستم رنگ جدید Material از نظر طراحی الگوریتمی است و رنگهای اولیه، ثانویه، ثالثیه و خنثی را از یک رنگ پایه مشخص تولید میکند. یکی از نکات نگرانکنندهای که هنگام صحبت با شرکای داخلی و خارجی زیاد دریافت کردیم، چگونگی پذیرش رنگ پویا در عین حفظ کنترل بر برخی رنگها بود.
این رنگها اغلب در کاربرد، معنا یا مفهوم خاصی را حمل میکنند که اگر با یک رنگ تصادفی جایگزین شوند، از بین میروند. از طرف دیگر، اگر به همین شکل باقی بمانند، ممکن است از نظر بصری ناموزون یا نامناسب به نظر برسند.
رنگ در متریال شما با فام (hue)، کروما (chroma) و تُن (tone) توصیف میشود. تُن یک رنگ به درک فرد از آن به عنوان عضوی از یک طیف رنگی در مقابل طیف رنگی دیگر مربوط میشود. تُن میزان روشنی یا تیرگی آن را توصیف میکند و کروما شدت رنگ است. درک فام میتواند تحت تأثیر عوامل فرهنگی و زبانی باشد، مانند فقدان مکرر کلمهای برای آبی در فرهنگهای باستانی که در عوض در همان خانواده سبز دیده میشود.
یک رنگ خاص را میتوان بسته به جایگاهش در طیف رنگ، گرم یا سرد در نظر گرفت. تغییر به سمت رنگ قرمز، نارنجی یا زرد معمولاً آن را گرمتر و به سمت آبی، سبز یا بنفش آن را سردتر در نظر میگیرد. حتی در میان رنگهای گرم یا سرد، تُنهای گرم و سرد وجود دارد. در زیر، زرد «گرمتر» بیشتر به رنگ نارنجی متمایل است در حالی که زرد «سردتر» بیشتر تحت تأثیر سبز قرار دارد. 
الگوریتم هماهنگسازی رنگ، رنگِ تغییرنیافته و رنگی که باید با آن هماهنگ شود را بررسی میکند تا رنگی را پیدا کند که هماهنگ باشد اما ویژگیهای رنگی زیرین آن را تغییر ندهد. در نمودار اول، رنگهای سبز، زرد و نارنجی با هماهنگی کمتر روی یک طیف ترسیم شدهاند. در نمودار بعدی، سبز و نارنجی با رنگ زرد هماهنگ شدهاند. سبز جدید گرمتر و نارنجی جدید سردتر است.
رنگ نارنجی و سبز تغییر کرده است اما هنوز هم میتوان آنها را نارنجی و سبز درک کرد.

اگر مایلید درباره برخی از تصمیمات، کاوشها و ملاحظات طراحی بیشتر بدانید، همکارانم آیان دنیلز و اندرو لو در وبلاگی مطلبی نوشتهاند که کمی عمیقتر از این بخش است.
۶. هماهنگسازی دستی رنگها
برای هماهنگسازی یک تُن واحد، دو تابع در MaterialColors وجود دارد: harmonize و harmonizeWithPrimary .
harmonizeWithPrimary از Context به عنوان وسیلهای برای دسترسی به تم فعلی و متعاقباً رنگ اصلی آن استفاده میکند.
@ColorInt
public static int harmonizeWithPrimary(@NonNull Context context, @ColorInt int colorToHarmonize) {
return harmonize(
colorToHarmonize,
getColor(context, R.attr.colorPrimary, MaterialColors.class.getCanonicalName()));
}
@ColorInt
public static int harmonize(@ColorInt int colorToHarmonize, @ColorInt int colorToHarmonizeWith) {
return Blend.harmonize(colorToHarmonize, colorToHarmonizeWith);
}
برای بازیابی مجموعه چهار تُن، باید کمی بیشتر کار کنیم.
با توجه به اینکه رنگ منبع را از قبل داریم، باید:
- تعیین کنید که آیا باید هماهنگ شود یا خیر،
- تعیین اینکه آیا در حالت تاریک هستیم یا خیر، و
- یک شیء
ColorRolesهماهنگ یا غیر هماهنگ را برمیگرداند.
تعیین اینکه آیا باید هماهنگسازی انجام شود یا خیر
در قالب خروجی گرفته شده از Material Theme Builder، ما با استفاده از نامگذاری harmonize<Color> ، ویژگیهای بولی را وارد کردیم. در زیر یک تابع برای دسترسی به آن مقدار آمده است.
اگر پیدا شود، مقدار آن را برمیگرداند؛ در غیر این صورت تعیین میکند که نباید رنگ را هماهنگ کند.
// Looks for associated harmonization attribute based on the color id
// custom1 ===> harmonizeCustom1
fun shouldHarmonize(context: Context, colorId: Int): Boolean {
val root = context.resources.getResourceEntryName(colorId)
val harmonizedId = "harmonize" + root.replaceFirstChar { it.uppercaseChar() }
val identifier = context.resources.getIdentifier(
harmonizedId, "bool", context.packageName)
return if (identifier != 0) context.resources.getBoolean(identifier) else false
}
ایجاد یک شیء Harmonized ColorRoles
retrieveHarmonizedColorRoles تابع دیگری است که تمام مراحل ذکر شده را به هم متصل میکند: مقدار رنگ را برای یک منبع نامگذاری شده بازیابی میکند، سعی میکند یک ویژگی بولی را برای تعیین هماهنگی حل کند و یک شیء ColorRoles را که از رنگ اصلی یا ترکیبی (با توجه به طرح روشن یا تیره) مشتق شده است، برمیگرداند.
fun retrieveHarmonizedColorRoles(
view: View,
customId: Int,
isLight: Boolean
): ColorRoles {
val context = view.context
val custom = context.getColor(customId);
val shouldHarmonize = shouldHarmonize(context, customId)
if (shouldHarmonize) {
val blended = MaterialColors.harmonizeWithPrimary(context, custom)
return MaterialColors.getColorRoles(blended, isLight)
} else return MaterialColors.getColorRoles(custom, isLight)
}
۷. پر کردن کارتهای حمل و نقل عمومی
همانطور که قبلاً اشاره شد، ما از یک RecyclerView و آداپتور برای پر کردن و رنگآمیزی مجموعه کارتهای حمل و نقل عمومی استفاده خواهیم کرد.

ذخیره دادههای حمل و نقل
برای ذخیره دادههای متنی و اطلاعات رنگ کارتهای حمل و نقل عمومی، از یک کلاس داده استفاده میکنیم که نام، مقصد و شناسه منبع رنگ را ذخیره میکند.
data class TransitInfo(val name: String, val destination: String, val colorId: Int)
/* truncated code */
val transitItems = listOf(
TransitInfo("53", "Irvine", R.color.custom1),
TransitInfo("153", "Brea", R.color.custom1),
TransitInfo("Orange County Line", "Oceanside", R.color.custom2),
TransitInfo("Pacific Surfliner", "San Diego", R.color.custom2)
)
ما از این رنگ برای تولید تُنهای مورد نیازمان در لحظه استفاده خواهیم کرد.
شما میتوانید در زمان اجرا با تابع onBindViewHolder زیر هماهنگسازی کنید.
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val transitInfo = list.get(position)
val color = transitInfo.colorId
if (!colorRolesMap.containsKey(color)) {
val roles = retrieveHarmonizedColorRoles(
holder.itemView, color,
!isNightMode(holder.itemView.context)
)
colorRolesMap.put(color, roles)
}
val card = holder.card
holder.transitName.text = transitInfo.name
holder.transitDestination.text = transitInfo.destination
val colorRoles = colorRolesMap.get(color)
if (colorRoles != null) {
holder.card.setCardBackgroundColor(colorRoles.accentContainer)
holder.transitName.setTextColor(colorRoles.onAccentContainer)
holder.transitDestination.setTextColor(colorRoles.onAccentContainer)
}
}
۸. هماهنگسازی خودکار رنگها
یک جایگزین برای مدیریت دستی هماهنگسازی، این است که میتوانید آن را برای خود مدیریت کنید. HarmonizedColorOptions یک کلاس سازنده است که به شما امکان میدهد بسیاری از کارهایی را که تاکنون به صورت دستی انجام دادهایم، مشخص کنید.
پس از بازیابی زمینه فعلی و دسترسی به طرح پویای فعلی، باید رنگهای پایهای را که میخواهید هماهنگ کنید مشخص کنید و یک زمینه جدید بر اساس شیء HarmonizedColorOptions و زمینه فعالشده DynamicColors ایجاد کنید.
اگر نمیخواهید رنگی را هماهنگ کنید، آن را در harmonyizedOptions قرار ندهید.
val newContext = DynamicColors.wrapContextIfAvailable(requireContext())
val harmonizedOptions = HarmonizedColorsOptions.Builder()
.setColorResourceIds(intArrayOf(R.color.custom1, R.color.custom2))
.build();
harmonizedContext =
HarmonizedColors.wrapContextIfAvailable(dynamicColorsContext, harmonizedOptions)
با رنگ پایه هماهنگ که از قبل مدیریت شده است، میتوانید onBindViewHolder خود را بهروزرسانی کنید تا به سادگی MaterialColors.getColorRoles را فراخوانی کرده و مشخص کنید که آیا نقشهای برگشتی باید روشن یا تیره باشند.
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
/*...*/
val color = transitInfo.colorId
if (!colorRolesMap.containsKey(color)) {
val roles = MaterialColors.getColorRoles(context.getColor(color), !isNightMode(context))
)
colorRolesMap.put(color, roles)
}
val card = holder.card
holder.transitName.text = transitInfo.name
holder.transitDestination.text = transitInfo.destination
val colorRoles = colorRolesMap.get(color)
if (colorRoles != null) {
holder.card.setCardBackgroundColor(colorRoles.accentContainer)
holder.transitName.setTextColor(colorRoles.onAccentContainer)
holder.transitDestination.setTextColor(colorRoles.onAccentContainer)
}
}
۹. هماهنگسازی خودکار ویژگیهای قالب
روشهایی که تاکنون نشان داده شدهاند، بر بازیابی نقشهای رنگی از یک رنگ منفرد متکی هستند. این روش برای نشان دادن اینکه یک تُن واقعی تولید میشود اما برای اکثر برنامههای موجود واقعبینانه نیست، عالی است. احتمالاً شما مستقیماً یک رنگ را استخراج نمیکنید، بلکه از یک ویژگی تم موجود استفاده میکنید.
پیش از این در این آزمایشگاه کد، در مورد اکسپورت کردن ویژگیهای قالب صحبت کردیم.
<resources>
<style name="AppTheme" parent="Theme.Material3.Light.NoActionBar">
<!--- Normal theme attributes ... -->
<item name="colorCustom1">#006876</item>
<item name="colorOnCustom1">#ffffff</item>
<item name="colorCustom1Container">#97f0ff</item>
<item name="colorOnCustom1Container">#001f24</item>
<item name="harmonizeCustom1">false</item>
<item name="colorCustom2">#016e00</item>
<item name="colorOnCustom2">#ffffff</item>
<item name="colorCustom2Container">#78ff57</item>
<item name="colorOnCustom2Container">#002200</item>
<item name="harmonizeCustom2">false</item>
</style>
</resources>
مشابه روش خودکار اول، میتوانیم مقادیری را به HarmonizedColorOptions ارائه دهیم و از HarmonizedColors برای بازیابی یک Context با رنگهای هماهنگ استفاده کنیم. یک تفاوت کلیدی بین این دو روش وجود دارد. ما علاوه بر این، باید یک پوشش تم شامل فیلدهایی که باید هماهنگ شوند، ارائه دهیم.
val dynamicColorsContext = DynamicColors.wrapContextIfAvailable(requireContext())
// Harmonizing individual attributes
val harmonizedColorAttributes = HarmonizedColorAttributes.create(
intArrayOf(
R.attr.colorCustom1,
R.attr.colorOnCustom1,
R.attr.colorCustom1Container,
R.attr.colorOnCustom1Container,
R.attr.colorCustom2,
R.attr.colorOnCustom2,
R.attr.colorCustom2Container,
R.attr.colorOnCustom2Container
), R.style.AppTheme_Overlay
)
val harmonizedOptions =
HarmonizedColorsOptions.Builder().setColorAttributes(harmonizedColorAttributes).build()
val harmonizedContext =
HarmonizedColors.wrapContextIfAvailable(dynamicColorsContext, harmonizedOptions)
آداپتور شما از زمینه هماهنگ استفاده میکند. مقادیر موجود در پوسته باید به نوع روشن یا تیره ناهماهنگ اشاره کنند.
<style name="AppTheme.Overlay" parent="AppTheme">
<item name="colorCustom1">@color/harmonized_colorCustom1</item>
<item name="colorOnCustom1">@color/harmonized_colorOnCustom1</item>
<item name="colorCustom1Container">@color/harmonized_colorCustom1Container</item>
<item name="colorOnCustom1Container">@color/harmonized_colorOnCustom1Container</item>
<item name="colorCustom2">@color/harmonized_colorCustom2</item>
<item name="colorOnCustom2">@color/harmonized_colorOnCustom2</item>
<item name="colorCustom2Container">@color/harmonized_colorCustom2Container</item>
<item name="colorOnCustom2Container">@color/harmonized_colorOnCustom2Container</item>
</style>
درون فایل طرحبندی XML، میتوانیم از آن ویژگیهای هماهنگشده به صورت عادی استفاده کنیم.
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
style="?attr/materialCardViewFilledStyle"
android:id="@+id/card"
android:layout_width="80dp"
android:layout_height="100dp"
android:layout_marginStart="8dp"
app:cardBackgroundColor="?attr/colorCustom1Container"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dp">
<TextView
android:id="@+id/transitName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="28sp"
android:textStyle="bold"
android:textColor="?attr/colorOnCustom1Container"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/transitDestination"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:textColor="?attr/colorOnCustom1Container"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
۱۰. کد منبع
package com.example.voyagi.harmonization.ui.dashboard
import android.content.Context
import android.content.res.Configuration
import android.graphics.Typeface
import android.os.Bundle
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.voyagi.harmonization.R
import com.example.voyagi.harmonization.databinding.FragmentDashboardBinding
import com.example.voyagi.harmonization.ui.home.TransitCardAdapter
import com.example.voyagi.harmonization.ui.home.TransitInfo
import com.google.android.material.card.MaterialCardView
import com.google.android.material.color.ColorRoles
import com.google.android.material.color.DynamicColors
import com.google.android.material.color.HarmonizedColorAttributes
import com.google.android.material.color.HarmonizedColors
import com.google.android.material.color.HarmonizedColorsOptions
import com.google.android.material.color.MaterialColors
class DashboardFragment : Fragment() {
enum class TransitMode { BUS, TRAIN }
data class TransitInfo2(val name: String, val destination: String, val mode: TransitMode)
private lateinit var dashboardViewModel: DashboardViewModel
private var _binding: FragmentDashboardBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
dashboardViewModel =
ViewModelProvider(this).get(DashboardViewModel::class.java)
_binding = FragmentDashboardBinding.inflate(inflater, container, false)
val root: View = binding.root
val recyclerView = binding.recyclerView
val transitItems = listOf(
TransitInfo2("53", "Irvine", TransitMode.BUS),
TransitInfo2("153", "Brea", TransitMode.BUS),
TransitInfo2("Orange County Line", "Oceanside", TransitMode.TRAIN),
TransitInfo2("Pacific Surfliner", "San Diego", TransitMode.TRAIN)
)
val dynamicColorsContext = DynamicColors.wrapContextIfAvailable(requireContext())
// Harmonizing individual attributes
val harmonizedColorAttributes = HarmonizedColorAttributes.create(
intArrayOf(
R.attr.colorCustom1,
R.attr.colorOnCustom1,
R.attr.colorCustom1Container,
R.attr.colorOnCustom1Container,
R.attr.colorCustom2,
R.attr.colorOnCustom2,
R.attr.colorCustom2Container,
R.attr.colorOnCustom2Container
), R.style.AppTheme_Overlay
)
val harmonizedOptions =
HarmonizedColorsOptions.Builder().setColorAttributes(harmonizedColorAttributes).build()
val harmonizedContext =
HarmonizedColors.wrapContextIfAvailable(dynamicColorsContext, harmonizedOptions)
val adapter = TransitCardAdapterAttr(transitItems, harmonizedContext)
recyclerView.adapter = adapter
recyclerView.layoutManager =
LinearLayoutManager(harmonizedContext, RecyclerView.HORIZONTAL, false)
return root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
class TransitCardAdapterAttr(val list: List<DashboardFragment.TransitInfo2>, context: Context) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
val colorRolesMap = mutableMapOf<Int, ColorRoles>()
private var harmonizedContext: Context? = context
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): RecyclerView.ViewHolder {
return if (viewType == DashboardFragment.TransitMode.BUS.ordinal) {
BusViewHolder(LayoutInflater.from(harmonizedContext).inflate(R.layout.transit_item_bus, parent, false))
} else TrainViewHolder(LayoutInflater.from(harmonizedContext).inflate(R.layout.transit_item_train, parent, false))
}
override fun getItemCount(): Int {
return list.size
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = list[position]
if (item.mode.ordinal == DashboardFragment.TransitMode.BUS.ordinal) {
(holder as BusViewHolder).bind(item)
(holder as TransitBindable).adjustNameLength()
} else {
(holder as TrainViewHolder).bind(item)
(holder as TransitBindable).adjustNameLength()
}
}
override fun getItemViewType(position: Int): Int {
return list[position].mode.ordinal
}
interface TransitBindable {
val card: MaterialCardView
var transitName: TextView
var transitDestination: TextView
fun bind(item: DashboardFragment.TransitInfo2) {
transitName.text = item.name
transitDestination.text = item.destination
}
fun Float.toDp(context: Context) =
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
this,
context.resources.displayMetrics
)
fun adjustNameLength(){
if (transitName.length() > 4) {
val layoutParams = card.layoutParams
layoutParams.width = 100f.toDp(card.context).toInt()
card.layoutParams = layoutParams
transitName.setTypeface(Typeface.DEFAULT_BOLD);
transitName.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16.0f)
}
}
}
inner class BusViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), TransitBindable {
override val card: MaterialCardView = itemView.findViewById(R.id.card)
override var transitName: TextView = itemView.findViewById(R.id.transitName)
override var transitDestination: TextView = itemView.findViewById(R.id.transitDestination)
}
inner class TrainViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), TransitBindable {
override val card: MaterialCardView = itemView.findViewById(R.id.card)
override var transitName: TextView = itemView.findViewById(R.id.transitName)
override var transitDestination: TextView = itemView.findViewById(R.id.transitDestination)
}
}
۱۱. رابطهای کاربری نمونه
قالببندی پیشفرض و رنگهای سفارشی بدون هماهنگی

رنگهای سفارشی هماهنگ




۱۲. خلاصه
در این آزمایشگاه کد، شما موارد زیر را آموختهاید:
- اصول اولیه الگوریتم هماهنگسازی رنگ ما
- چگونه میتوان نقشهای رنگی را از یک رنگ دیده شدهی مشخص تولید کرد.
- چگونه میتوان به صورت انتخابی یک رنگ را در رابط کاربری هماهنگ کرد.
- چگونه مجموعهای از ویژگیها را در یک قالب هماهنگ کنیم.
اگر سوالی دارید، میتوانید هر زمان که خواستید با استفاده از @MaterialDesign در توییتر از ما بپرسید.
برای مطالب و آموزشهای بیشتر در زمینه طراحی، در youtube.com/MaterialDesign با ما همراه باشید.