1. शुरू करने से पहले
इस कोडलैब में, आपको डाइनैमिक थीम से जनरेट हुए रंगों के साथ, अपने पसंद के हिसाब से रंग को मैच करने का तरीका बताया जाएगा.
ज़रूरी शर्तें
डेवलपर को
- Android में थीम बनाने के बुनियादी कॉन्सेप्ट के बारे में जानकारी हो
- Android विजेट व्यू और उनकी प्रॉपर्टी के साथ आसानी से काम कर सकता हो
आपको क्या सीखने को मिलेगा
- एक से ज़्यादा तरीकों का इस्तेमाल करके, अपने ऐप्लिकेशन में कलर हारमोनाइज़ेशन की सुविधा इस्तेमाल करने का तरीका
- कलर हारमोनाइज़ेशन की सुविधा कैसे काम करती है और यह रंग को कैसे बदलती है
आपको किन चीज़ों की ज़रूरत होगी
- अगर आपको साथ-साथ यह तरीका आज़माना है, तो आपके पास Android वाला कंप्यूटर होना चाहिए.
2. ऐप्लिकेशन का अवलोकन
Voyaĝi एक ट्रांज़िट ऐप्लिकेशन है, जो पहले से ही डाइनैमिक थीम का इस्तेमाल करता है. कई ट्रांज़िट सिस्टम के लिए, रंग ट्रेनों, बसों या ट्राम के बारे में अहम जानकारी देते हैं. इन्हें उपलब्ध किसी भी डाइनैमिक प्राइमरी, सेकंडरी या टर्शियरी रंग से नहीं बदला जा सकता. हमारा फ़ोकस, रंगीन ट्रांज़िट कार्ड के RecyclerView पर होगा.

3. थीम जनरेट करना
हमारा सुझाव है कि Material3 थीम बनाने के लिए, सबसे पहले हमारे टूल Material Theme Builder का इस्तेमाल करें. कस्टम टैब पर जाकर, अब अपनी थीम में ज़्यादा रंग जोड़े जा सकते हैं. दाईं ओर, आपको उन रंगों के लिए कलर रोल और टोनल पैलेट दिखाए जाएंगे.
एक्सटेंडेड कलर सेक्शन में, रंगों को हटाया या उनका नाम बदला जा सकता है.

एक्सपोर्ट मेन्यू में, एक्सपोर्ट करने के कई विकल्प दिखेंगे. यह लेख लिखते समय, Material Theme 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>
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>
4. कस्टम कलर की जांच करना
Material Theme Builder के साइड पैनल में ज़ूम करने पर, हमें पता चलता है कि कस्टम रंग जोड़ने से, हल्के और गहरे रंग की पैलेट में चार मुख्य रंग की भूमिकाओं वाला पैनल दिखता है.

Android व्यू में, हम इन रंगों को आपके लिए एक्सपोर्ट करते हैं. हालांकि, पर्दे के पीछे इन्हें 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
}
MaterialColors क्लास में मौजूद getColorRoles का इस्तेमाल करके, रनटाइम के दौरान किसी भी रंग से चार मुख्य रंग भूमिकाएं वापस पाई जा सकती हैं. getColorRoles की मदद से, किसी खास सीड कलर के लिए रनटाइम के दौरान चार रंग भूमिकाओं का सेट बनाया जा सकता है.
public static ColorRoles getColorRoles(
@NonNull Context context,
@ColorInt int color
) { /* implementation */ }
इसी तरह, आउटपुट वैल्यू, रंग की असल वैल्यू होती हैं. ये उनके पॉइंटर नहीं होतीं.**
5. कलर हारमोनाइज़ेशन क्या है?
Material का नया कलर सिस्टम, एल्गोरिदम पर आधारित है. यह दिए गए सीड कलर से प्राइमरी, सेकंडरी, टर्शियरी, और न्यूट्रल कलर जनरेट करता है. हमें अपने इंटरनल और बाहरी पार्टनर से एक समस्या के बारे में काफ़ी जानकारी मिली. यह समस्या, कुछ रंगों को कंट्रोल करते हुए डाइनैमिक कलर को इस्तेमाल करने से जुड़ी थी.
इन रंगों का इस्तेमाल अक्सर ऐप्लिकेशन में किसी खास मतलब या कॉन्टेक्स्ट के लिए किया जाता है. अगर इन्हें किसी भी रंग से बदल दिया जाए, तो इनका मतलब या कॉन्टेक्स्ट खत्म हो जाएगा. इसके अलावा, अगर इन रंगों को ऐसे ही छोड़ दिया जाता है, तो हो सकता है कि ये देखने में अजीब लगें या सही न दिखें.
Material You में रंग को ह्यू, क्रोमा, और टोन के हिसाब से बताया जाता है. किसी रंग का रंगत, इस बात से जुड़ा होता है कि कोई व्यक्ति उसे एक रंग की रेंज का सदस्य मानता है या दूसरी रंग की रेंज का. टोन से पता चलता है कि रंग कितना हल्का या गहरा है. क्रोमा से पता चलता है कि रंग कितना चटख है. रंग की पहचान पर सांस्कृतिक और भाषाई फ़ैक्टर का असर पड़ सकता है. जैसे, अक्सर यह कहा जाता है कि प्राचीन संस्कृतियों में नीले रंग के लिए कोई शब्द नहीं था. इसे हरे रंग के जैसा ही माना जाता था.
किसी रंग को गर्म या ठंडा माना जा सकता है. यह इस बात पर निर्भर करता है कि वह रंग, रंग के स्पेक्ट्रम में कहां मौजूद है. आम तौर पर, लाल, नारंगी या पीले रंग की ओर बढ़ने पर, इमेज को वॉर्म माना जाता है. वहीं, नीले, हरे या बैंगनी रंग की ओर बढ़ने पर, इमेज को कूल माना जाता है. वॉर्म या कूल कलर में भी, आपको वॉर्म और कूल टोन मिलेंगे. यहां, "वार्म" पीले रंग में नारंगी रंग का ज़्यादा असर दिखता है, जबकि "कूल" पीले रंग पर हरे रंग का ज़्यादा असर दिखता है. 
कलर हारमोनाइज़ेशन एल्गोरिदम, बिना बदले रंग की ह्यू और जिस रंग के साथ उसे हारमोनाइज़ किया जाना है उसकी ह्यू की जांच करता है. इससे, वह ऐसी ह्यू का पता लगाता है जो हारमोनियस हो, लेकिन उसके मूल रंग की क्वालिटी में बदलाव न करे. पहले ग्राफ़िक में, स्पेक्ट्रम पर हरे, पीले, और नारंगी रंग के कम सामंजस्य वाले शेड दिखाए गए हैं. अगले ग्राफ़िक में, हरे और नारंगी रंग को पीले रंग के साथ मिलाया गया है. नया हरा रंग ज़्यादा गहरा है और नया नारंगी रंग ज़्यादा हल्का है.
नारंगी और हरे रंग की चमक में बदलाव हुआ है, लेकिन इन्हें अब भी नारंगी और हरे रंग के तौर पर देखा जा सकता है.

अगर आपको डिज़ाइन से जुड़े कुछ फ़ैसलों, एक्सप्लोरेशन, और बातों के बारे में ज़्यादा जानना है, तो मेरे सहयोगियों अयान डेनियल और एंड्रयू लू ने इस सेक्शन से ज़्यादा जानकारी वाली ब्लॉग पोस्ट लिखी है.
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 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)
}
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 एक बिल्डर क्लास है. इसकी मदद से, अब तक मैन्युअल तरीके से किए गए ज़्यादातर काम किए जा सकते हैं.
मौजूदा कॉन्टेक्स्ट को वापस पाने के बाद, आपके पास मौजूदा डाइनैमिक स्कीम का ऐक्सेस होता है. इसके बाद, आपको उन मुख्य रंगों के बारे में बताना होता है जिन्हें आपको एक जैसा बनाना है. साथ ही, उस HarmonizedColorOptions ऑब्जेक्ट और DynamicColors की सुविधा चालू किए गए कॉन्टेक्स्ट के आधार पर, एक नया कॉन्टेक्स्ट बनाना होता है.
अगर आपको किसी रंग को एक जैसा नहीं करना है, तो उसे harmonizedOptions में शामिल न करें.
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. थीम एट्रिब्यूट को अपने-आप एक जैसा करना
अब तक दिखाए गए तरीकों में, किसी रंग से कलर रोल वापस पाने पर भरोसा किया जाता है. इससे यह पता चलता है कि असली टोन जनरेट की जा रही है. हालांकि, यह ज़्यादातर मौजूदा ऐप्लिकेशन के लिए सही नहीं है. ऐसा हो सकता है कि आपको सीधे तौर पर कोई रंग न मिल रहा हो, लेकिन इसके बजाय किसी मौजूदा थीम एट्रिब्यूट का इस्तेमाल किया जा रहा हो.
इस कोडलैब में हमने थीम एट्रिब्यूट एक्सपोर्ट करने के बारे में बताया था.
<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 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. खास जानकारी
इस कोडलैब में, आपने इनके बारे में सीखा:
- कलर हार्मनाइज़ेशन एल्गोरिदम के बारे में बुनियादी जानकारी
- दिए गए रंग से कलर रोल जनरेट करने का तरीका.
- यूज़र इंटरफ़ेस में किसी रंग को चुनिंदा तौर पर कैसे एक जैसा किया जाए.
- किसी थीम में एट्रिब्यूट के सेट को एक जैसा कैसे बनाएं.
अगर आपका कोई सवाल है, तो @MaterialDesign on Twitter का इस्तेमाल करके, किसी भी समय हमसे पूछें.
डिज़ाइन से जुड़े ज़्यादा कॉन्टेंट और ट्यूटोरियल के लिए, youtube.com/MaterialDesign पर बने रहें