1. Hinweis
In diesem Codelab erfahren Sie, wie Sie Ihre benutzerdefinierten Farben mit den Farben eines dynamischen Designs abstimmen.
Vorbereitung
Entwickler sollten
- Mit den grundlegenden Konzepten für das Erstellen von Designs in Android vertraut sein
- Vertrautheit mit Android-Widget-Ansichten und ihren Eigenschaften
Lerninhalte
- Farben in Ihrer Anwendung mit verschiedenen Methoden harmonisieren
- So funktioniert die Harmonisierung und so werden Farben verschoben
Voraussetzungen
- Einen Computer, auf dem Android installiert ist, wenn Sie die Schritte nachvollziehen möchten.
2. App-Übersicht
Voyaĝi ist eine Transit-App, die bereits ein dynamisches Design verwendet. Bei vielen öffentlichen Verkehrsmitteln ist die Farbe ein wichtiger Indikator für Züge, Busse oder Straßenbahnen. Diese können nicht durch dynamische Primär-, Sekundär- oder Tertiärfarben ersetzt werden. Wir konzentrieren uns auf die RecyclerView der farbigen Transitkarten.

3. Design generieren
Wir empfehlen, zuerst unser Tool Material Theme Builder zu verwenden, um ein Material3-Theme zu erstellen. Auf dem benutzerdefinierten Tab können Sie Ihrem Design jetzt weitere Farben hinzufügen. Rechts werden die Farbrollen und Tonpaletten für diese Farben angezeigt.
Im erweiterten Farbbereich können Sie Farben entfernen oder umbenennen.

Im Exportmenü werden verschiedene Exportoptionen angezeigt. Zum Zeitpunkt der Erstellung dieses Dokuments ist die spezielle Verarbeitung von Harmonisierungseinstellungen durch Material Theme Builder nur in Android-Ansichten verfügbar.

Neue Exportwerte
Damit Sie diese Farben und die zugehörigen Farbrollen in Ihren Designs verwenden können, unabhängig davon, ob Sie die Farben harmonisieren möchten, enthält der exportierte Download jetzt eine Datei „attrs.xml“ mit den Namen der Farbrollen für jede benutzerdefinierte Farbe.
<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>
In „themes.xml“ haben wir die vier Farbrollen für jede benutzerdefinierte Farbe generiert (color<name>, colorOn<name>, color<name>Container, and colorOn<nameContainer>). Die harmonize<name>-Attribute geben an, ob der Entwickler die Option im Material Theme Builder ausgewählt hat. Die Farbe im Kerndesign wird dadurch nicht geändert.
<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>
In der Datei colors.xml werden die Ausgangsfarben, die zum Generieren der oben aufgeführten Farbrollen verwendet werden, zusammen mit booleschen Werten dafür angegeben, ob die Palette der Farbe verschoben wird oder nicht.
<resources>
<!-- other colors used in theme -->
<color name="custom1">#1AC9E0</color>
<color name="custom2">#32D312</color>
</resources>
4. Benutzerdefinierte Farbe untersuchen
Wenn wir in die Seitenleiste von Material Theme Builder hineinzoomen, sehen wir, dass beim Hinzufügen einer benutzerdefinierten Farbe ein Bereich mit den vier wichtigsten Farbrollen in einer hellen und einer dunklen Palette angezeigt wird.

In Android Views werden diese Farben für Sie exportiert, aber im Hintergrund können sie durch eine Instanz des ColorRoles-Objekts dargestellt werden.
Die Klasse „ColorRoles“ hat vier Attribute: accent, onAccent, accentContainer und onAccentContainer. Diese Eigenschaften sind die Ganzzahldarstellung der vier hexadezimalen Farben.
public final class ColorRoles {
private final int accent;
private final int onAccent;
private final int accentContainer;
private final int onAccentContainer;
// truncated code
}
Sie können die vier wichtigen Farbrollen zur Laufzeit aus einer beliebigen Farbe abrufen. Dazu verwenden Sie getColorRoles in der MaterialColors-Klasse mit dem Namen getColorRoles. Damit können Sie die vier Farbrollen zur Laufzeit anhand einer bestimmten Ausgangsfarbe erstellen.
public static ColorRoles getColorRoles(
@NonNull Context context,
@ColorInt int color
) { /* implementation */ }
Ebenso sind die Ausgabewerte die tatsächlichen Farbwerte und KEINE Zeiger darauf.**
5. Was ist die Farbabstimmung?
Das neue Farbsystem von Material ist algorithmisch konzipiert und generiert Primär-, Sekundär-, Tertiär- und neutrale Farben aus einer bestimmten Ausgangsfarbe. Ein Problem, das uns bei Gesprächen mit internen und externen Partnern häufig genannt wurde, war, wie man dynamische Farben nutzen und gleichzeitig die Kontrolle über bestimmte Farben behalten kann.
Diese Farben haben in der Anwendung oft eine bestimmte Bedeutung oder einen bestimmten Kontext, der verloren ginge, wenn sie durch eine zufällige Farbe ersetzt würden. Andernfalls können diese Farben visuell störend oder fehl am Platz wirken.
Farben in Material You werden durch Farbton, Chroma und Ton beschrieben. Der Farbton einer Farbe bezieht sich auf die Wahrnehmung als Teil eines bestimmten Farbbereichs. Der Farbton beschreibt, wie hell oder dunkel die Farbe erscheint, und die Chroma-Stufe gibt die Intensität der Farbe an. Die Wahrnehmung von Farbtönen kann durch kulturelle und sprachliche Faktoren beeinflusst werden. Ein oft genanntes Beispiel ist das Fehlen eines Wortes für Blau in alten Kulturen, in denen Blau stattdessen in derselben Kategorie wie Grün gesehen wurde.
Ein bestimmter Farbton kann je nach Position im Farbspektrum als warm oder kühl eingestuft werden. Wenn Sie die Farbe in Richtung Rot, Orange oder Gelb verschieben, wird das Bild im Allgemeinen als wärmer empfunden. Wenn Sie die Farbe in Richtung Blau, Grün oder Violett verschieben, wird das Bild als kühler empfunden. Auch innerhalb der warmen oder kühlen Farben gibt es warme und kühle Töne. Unten ist das „wärmere“ Gelb eher orangefarben, während das „kühlere“ Gelb eher grün ist. 
Der Algorithmus zur Farbabstimmung untersucht den Farbton der nicht verschobenen Farbe und der Farbe, mit der sie abgestimmt werden soll, um einen Farbton zu finden, der harmonisch ist, aber die zugrunde liegenden Farbeigenschaften nicht verändert. In der ersten Grafik sind weniger harmonische Grün-, Gelb- und Orangetöne in einem Spektrum dargestellt. In der nächsten Grafik wurden die Farben Grün und Orange an den Gelbton angepasst. Das neue Grün ist wärmer und das neue Orange kühler.
Der Farbton hat sich bei Orange und Grün verschoben, aber die Farben können immer noch als Orange und Grün wahrgenommen werden.

Wenn Sie mehr über einige der Designentscheidungen, Tests und Überlegungen erfahren möchten, haben meine Kollegen Ayan Daniels und Andrew Lu einen Blogpost geschrieben, der etwas mehr in die Tiefe geht als dieser Abschnitt.
6. Farbe manuell abstimmen
Zum Harmonisieren eines einzelnen Tons gibt es zwei Funktionen in MaterialColors, harmonize und harmonizeWithPrimary.
harmonizeWithPrimary verwendet Context, um auf das aktuelle Design und anschließend auf die Primärfarbe zuzugreifen.
@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);
}
Um die vier Töne abzurufen, müssen wir noch etwas mehr tun.
Da wir die Quellfarbe bereits haben, müssen wir Folgendes tun:
- feststellen, ob sie harmonisiert werden sollte,
- feststellen, ob wir uns im dunklen Modus befinden, und
- Gibt entweder ein harmonisiertes oder ein nicht harmonisiertes
ColorRoles-Objekt zurück.
Entscheiden, ob harmonisiert werden soll
Im exportierten Theme aus Material Theme Builder haben wir boolesche Attribute mit der Nomenklatur harmonize<Color> eingefügt. Unten finden Sie eine praktische Funktion für den Zugriff auf diesen Wert.
Wenn der Wert gefunden wird, wird er zurückgegeben. Andernfalls wird bestimmt, dass die Farbe nicht harmonisiert werden soll.
// 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
}
Harmonisiertes ColorRoles-Objekt erstellen
retrieveHarmonizedColorRoles ist eine weitere praktische Funktion, die alle oben genannten Schritte zusammenfasst: Sie ruft den Farbwert für eine benannte Ressource ab, versucht, ein boolesches Attribut aufzulösen, um die Harmonisierung zu bestimmen, und gibt ein ColorRoles-Objekt zurück, das von der ursprünglichen oder gemischten Farbe abgeleitet ist (je nach hellem oder dunklem Schema).
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. Fahrkarten werden angezeigt
Wie bereits erwähnt, verwenden wir eine RecyclerView und einen Adapter, um die Sammlung von Transitkarten zu füllen und zu färben.

Transitdaten speichern
Zum Speichern der Textdaten und Farbinformationen für die Fahrkarten verwenden wir eine Datenklasse, in der der Name, das Ziel und die Farbressourcen-ID gespeichert werden.
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)
)
Anhand dieser Farbe werden die benötigten Töne in Echtzeit generiert.
Sie können die Harmonisierung zur Laufzeit mit der folgenden onBindViewHolder-Funktion vornehmen.
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. Farben automatisch abstimmen
Alternativ zur manuellen Bearbeitung der Harmonisierung können Sie sie auch für sich erledigen lassen. HarmonizedColorOptions ist eine Builder-Klasse, mit der Sie vieles von dem angeben können, was wir bisher manuell erledigt haben.
Nachdem Sie den aktuellen Kontext abgerufen haben, um auf das aktuelle dynamische Schema zuzugreifen, müssen Sie die Basisfarben angeben, die Sie abstimmen möchten, und einen neuen Kontext basierend auf dem HarmonizedColorOptions-Objekt und dem Kontext mit aktivierten DynamicColors erstellen.
Wenn Sie eine Farbe nicht abstimmen möchten, schließen Sie sie einfach nicht in „harmonizedOptions“ ein.
val newContext = DynamicColors.wrapContextIfAvailable(requireContext())
val harmonizedOptions = HarmonizedColorsOptions.Builder()
.setColorResourceIds(intArrayOf(R.color.custom1, R.color.custom2))
.build();
harmonizedContext =
HarmonizedColors.wrapContextIfAvailable(dynamicColorsContext, harmonizedOptions)
Da die harmonisierte Grundfarbe bereits berücksichtigt wurde, können Sie in Ihrer onBindViewHolder-Methode einfach MaterialColors.getColorRoles aufrufen und angeben, ob die zurückgegebenen Rollen hell oder dunkel sein sollen.
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. Designattribute automatisch abstimmen
Die bisher gezeigten Methoden basieren auf dem Abrufen der Farbrollen aus einer einzelnen Farbe. Das ist zwar gut, um zu zeigen, dass ein echter Ton generiert wird, aber für die meisten bestehenden Anwendungen nicht realistisch. Wahrscheinlich leiten Sie keine Farbe direkt ab, sondern verwenden stattdessen ein vorhandenes Designattribut.
Weiter oben in diesem Codelab haben wir über das Exportieren von Attributen gesprochen.
<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>
Ähnlich wie bei der ersten automatischen Methode können wir Werte für „HarmonizedColorOptions“ angeben und „HarmonizedColors“ verwenden, um einen Kontext mit den harmonisierten Farben abzurufen. Es gibt einen wichtigen Unterschied zwischen den beiden Methoden. Außerdem müssen wir ein Theme-Overlay mit den zu harmonisierenden Feldern bereitstellen.
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)
Ihr Adapter würde den harmonisierten Kontext verwenden. Die Werte im Themen-Overlay sollten sich auf die nicht harmonisierte helle oder dunkle Variante beziehen.
<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>
In der XML-Layoutdatei können wir diese harmonisierten Attribute wie gewohnt verwenden.
<?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. Quellcode
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. Beispiel-UIs
Standarddesign und benutzerdefinierte Farben ohne Harmonisierung

Harmonisierte benutzerdefinierte Farben


12. Zusammenfassung
In diesem Codelab haben Sie Folgendes gelernt:
- Grundlagen unseres Algorithmus zur Farbabstimmung
- So generieren Sie Farbrollen aus einer bestimmten gesehenen Farbe.
- So harmonieren Sie eine Farbe in einer Benutzeroberfläche selektiv.
- Eine Reihe von Attributen in einem Theme harmonisieren
Wenn Sie Fragen haben, können Sie uns jederzeit über @MaterialDesign auf Twitter kontaktieren.
Weitere Designinhalte und Tutorials findest du auf youtube.com/MaterialDesign.