1. לפני שמתחילים
ב-Codelab הזה תלמדו איך ליצור הרמוניה לצבעים המותאמים אישית שלכם עם אלה שנוצרו על ידי עיצוב דינמי.
דרישות מוקדמות
המפתחים צריכים להיות
- הכרת המושגים הבסיסיים של נושאי עיצוב ב-Android
- עבודה נוחה עם תצוגות בווידג'ט של Android והמאפיינים שלהן
מה תלמדו
- איך להשתמש בהרמוניזציה של צבעים באפליקציה באמצעות מספר שיטות
- איך ההרמוניה פועלת ואיך היא משנה את הצבעים
למה תזדקק?
- מחשב שמותקנת בו מערכת Android, אם אתם רוצים לעקוב אחריו.
2. סקירה כללית של היישום
Voyaëi היא אפליקציית תחבורה ציבורית שכבר משתמשת בעיצוב דינמי. בהרבה מערכות תחבורה ציבורית, הצבע הוא אינדיקציה חשובה לרכבות, לאוטובוסים או לחשמליות, ואי אפשר להחליף אותם בצבעים דינמיים של ראשי, משני או שלישוני. נתמקד בעבודה שלנו ב-RecyclerView של כרטיסים צבעוניים לתחבורה ציבורית.
3. יצירת עיצוב
בתור התחנה הראשונה ליצירת עיצוב Material3, מומלץ להשתמש בכלי שלנו, Material Design Builder. בכרטיסייה 'בהתאמה אישית', אפשר עכשיו להוסיף עוד צבעים לעיצוב. בצד שמאל יוצגו תפקידי הצבעים ולוחות הצבעים הטונלים של הצבעים האלה.
בקטע 'צבע מורחב' אפשר להסיר צבעים או לשנות את השם שלהם.
בתפריט הייצוא יוצגו כמה אפשרויות ייצוא. נכון לזמן הכתיבה, הטיפול המיוחד של Material Design Builder בהגדרות ההרמוניה זמין רק בתצוגות ב-Android
הסבר על ערכי הייצוא החדשים
כדי לאפשר לך להשתמש בצבעים האלה ובתפקידי הצבע המשויכים אליהם בעיצובים, גם אם תבחר לא ליהנות מהרמוניה, ההורדה המיוצאת כוללת עכשיו קובץ 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>
בקובץ theme.xml, יצרנו את ארבעת תפקידי הצבעים לכל צבע מותאם אישית (color<name>, colorOn<name>, color<name>Container, and colorOn<nameContainer>
). המאפיינים של harmonize<name>
משקפים אם המפתח בחר באפשרות הזו בכלי ליצירת עיצובים מהותיים. הצבע לא ישתנה בעיצוב המרכזי.
<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>
4. בדיקת צבע מותאם אישית
כשאנחנו מגדילים את החלונית הצדדית של הכלי Material Design, אפשר לראות שהוספת צבע מותאם אישית מציגה לוח עם ארבעת התפקידים המרכזיים בלוח צבעים בהיר כהים.
ב-Android View, אנחנו מייצאים את הצבעים האלה עבורכם, אבל מאחורי הקלעים הם יכולים לייצג אותם על ידי מופע של האובייקט 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
. כך אפשר ליצור את הקבוצה של 4 תפקידי הצבע בזמן ריצה בהינתן צבע בסיס ספציפי.
public static ColorRoles getColorRoles(
@NonNull Context context,
@ColorInt int color
) { /* implementation */ }
בדומה לכך, ערכי הפלט הם ערכי הצבעים בפועל, ולא מצביעים אליהם.**
5. מהי הרמוניזציה של צבעים?
מערכת הצבעים החדשה של חומר מבוססת על אלגוריתם, ויוצרת צבעים ראשיים, משניים, שלישיים וניטרליים מצבע מקור נתון. אחד מהחששות שקיבלנו הרבה כששוחחנו עם שותפים פנימיים וחיצוניים היה השימוש בצבעים דינמיים תוך שמירה על שליטה בצבעים מסוימים.
לצבעים האלה יש בדרך כלל משמעות או הקשר מסוימים באפליקציה, שיאבדו אם הם יוחלפו בצבע אקראי. לחלופין, אם תשאירו את התמונה כפי שהיא, הצבעים האלה עלולים להיראות צורכים או לא נראים לעין.
צבע ב'חומר': מתואר לפי גוון, כרומה וגוון. גוון של צבע מסוים קשור לתפיסה של אדם מסוים לגביו כשייך לטווח צבעים אחד לעומת אחר. 'טון' מתאר כמה בהיר או כהה הוא נראה ועוצמת הצבע היא 'כרומה'. גורמים תרבותיים ולשוניים יכולים להשפיע על תפיסת הגוון, למשל אם בתרבויות עתיקות אין מילה לכחול, אלא יש את אותה משפחה כירוק.
גוון מסוים יכול להיחשב כחם או קר, בהתאם למיקום שלו על ספקטרום הגוונים. שינוי לגוון אדום, כתום או צהוב נחשב בדרך כלל לגוון חם יותר, ונאמר שצבעו של כחול, ירוק או סגול מרענן את הגוון. גם בצבעים החמימים או הקרירים, יישמעו גוונים חמים וקרים. למטה מופיע הכיתוב "החם" צהוב הוא בגוון כתום יותר ואילו "קירור" צהוב מושפע יותר מירוק.
האלגוריתם של תהליך הרמוניזציה של צבעים בוחן את הגוון של הצבע שלא השתנה ואת הצבע שצריך להתאים לו כדי למצוא גוון הרמוני שלא משנה את תכונות הצבע הבסיסיות שלו. בגרפיקה הראשונה מוצגים גוונים פחות הרמוניים של ירוק, צהוב וכתום על ספקטרום. בגרפיקה הבאה, הירוק והכתום הושתלבו עם הגוון הצהוב. הירוק החדש חם יותר והכתום החדש קריר יותר.
הגוון השתנה על הכתום והירוק, אבל עדיין אפשר לראות אותם ככתום וירוק.
כדי לקבל מידע נוסף על חלק מההחלטות, החקירות והשיקולים בעיצוב, העמיתים שלי, איאן דניאלס ואנדרו לו, כתבו פוסט בבלוג עם קצת יותר מעמיק מהקטע הזה.
6. איזון צבעים באופן ידני
כדי ליצור הרמוניה לטון אחד, יש שתי פונקציות ב-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 Design 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
}
יצירת אובייקט 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)
}
7. הכרטיסים לתחבורה ציבורית בתהליך אכלוס
כפי שציינו קודם, אנחנו נשתמש ב-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)
}
}
8. איזון צבעים באופן אוטומטי
כחלופה לטיפול בהרמוניה באופן ידני, אפשר לטפל בזה בשבילכם. HarmonizedColorOptions היא מחלקה של ה-builder שמאפשרת לציין הרבה ממה שעשינו עד עכשיו ידנית.
לאחר אחזור ההקשר הנוכחי כדי שתהיה לך גישה לסכמה הדינמית הנוכחית, עליך לציין את צבעי הבסיס שברצונך ליצור הרמוניה וליצור הקשר חדש שמבוסס על האובייקט HarmonizedColorOptions וההקשר שמופעל על ידי DynamicColors.
אם אתם לא רוצים ליצור הרמוניה של צבע, פשוט אל תכללו אותו ב-HaronizedOptions.
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)
}
}
9. איזון אוטומטי של מאפייני העיצוב
השיטות שהוצגו עד עכשיו מסתמכות על אחזור תפקידי הצבעים מצבע ספציפי. היא יכולה לעשות את זה כדי להוכיח שנוצר טון אמיתי, אבל הוא לא מציאותי לרוב האפליקציות הקיימות. סביר להניח שלא גזירה של צבע באופן ישיר, אלא משתמשים במאפיין קיים של עיצוב.
מוקדם יותר ב-Codelab הזה דיברנו על ייצוא מאפייני עיצוב.
<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 כדי לאחזר הקשר עם הצבעים ההרמוניים. יש הבדל עיקרי אחד בין שתי השיטות. אנחנו גם צריכים לספק שכבת-על של עיצוב שמכילה את השדות כדי ליצור הרמוניה.
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>
10. קוד מקור
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)
}
}
11. ממשקי משתמש לדוגמה
עיצוב ברירת המחדל וצבעים בהתאמה אישית ללא הרמוניזציה
צבעים משתלבים בהתאמה אישית
12. סיכום
ב-Codelab הזה למדת:
- העקרונות הבסיסיים של האלגוריתם להרמוניית הצבעים שלנו
- איך יוצרים תפקידי צבע מצבע נתון שמוצג.
- איך ליצור הרמוניה של צבע באופן סלקטיבי בממשק משתמש
- איך ליצור הרמוניה של קבוצת מאפיינים בעיצוב
אם יש לכם שאלות, אתם מוזמנים לשאול אותנו בכל שלב באמצעות @Material Design ב-Twitter.
כדאי להמשיך להתעדכן בתכנים נוספים ובמדריכים בנושא עיצוב בכתובת youtube.com/MaterialDesign