Android व्यू के लिए, कलर हार्मनाइज़ेशन का बुनियादी तरीका

1. शुरू करने से पहले

इस कोडलैब में, आपको डाइनैमिक थीम से जनरेट हुए रंगों के साथ, अपने कस्टम कलर को मैच करने का तरीका बताया जाएगा.

ज़रूरी शर्तें

डेवलपर को

  • Android में थीम बनाने के बुनियादी कॉन्सेप्ट के बारे में जानकारी हो
  • Android विजेट व्यू और उनकी प्रॉपर्टी के साथ आसानी से काम कर सकता हो

आपको क्या सीखने को मिलेगा

  • अपने ऐप्लिकेशन में, अलग-अलग तरीकों से कलर हार्मनाइज़ेशन का इस्तेमाल करने का तरीका
  • कलर हारमोनाइज़ेशन की सुविधा कैसे काम करती है और यह रंग को कैसे बदलती है

आपको किन चीज़ों की ज़रूरत होगी

  • अगर आपको साथ-साथ यह तरीका आज़माना है, तो आपके पास Android वाला कंप्यूटर होना चाहिए.

2. ऐप्लिकेशन का अवलोकन

Voyaĝi एक ट्रांज़िट ऐप्लिकेशन है, जो पहले से ही डाइनैमिक थीम का इस्तेमाल करता है. कई ट्रांज़िट सिस्टम के लिए, रंग ट्रेनों, बसों या ट्राम के बारे में अहम जानकारी देते हैं. इन्हें डाइनैमिक प्राइमरी, सेकंडरी या टर्शियरी रंगों से नहीं बदला जा सकता. हमारा फ़ोकस, रंगीन ट्रांज़िट कार्ड के RecyclerView पर होगा.

62ff4b2fb6c9e14a.png

3. थीम जनरेट करना

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

एक्सटेंडेड कलर सेक्शन में, रंगों को हटाया या उनका नाम बदला जा सकता है.

20cc2cf72efef213.png

एक्सपोर्ट मेन्यू में, एक्सपोर्ट करने के कई विकल्प दिखेंगे. यह लेख लिखते समय, Material Theme Builder में कलर हारमोनाइज़ेशन की सेटिंग को खास तरीके से मैनेज करने की सुविधा सिर्फ़ Android व्यू में उपलब्ध है

6c962ad528c09b4.png

एक्सपोर्ट की नई वैल्यू के बारे में जानकारी

इन रंगों और उनसे जुड़ी कलर रोल का इस्तेमाल थीम में किया जा सकता है. इसके लिए, आपको यह तय करना होगा कि रंगों को एक जैसा रखना है या नहीं. एक्सपोर्ट की गई डाउनलोड की गई फ़ाइल में अब 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 के साइड पैनल में ज़ूम करने पर, हमें पता चलता है कि कस्टम रंग जोड़ने पर, एक पैनल दिखता है. इसमें हल्के और गहरे रंग की पैलेट में, चार मुख्य रंग होते हैं.

c6ee942b2b93cd92.png

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 में रंग को ह्यू, क्रोमा, और टोन के हिसाब से दिखाया जाता है. किसी रंग की ह्यू, इस बात से जुड़ी होती है कि कोई व्यक्ति उसे एक रंग की रेंज का सदस्य मानता है या दूसरी रंग की रेंज का. टोन से पता चलता है कि रंग कितना हल्का या गहरा है. क्रोमा से रंग की इंटेंसिटी का पता चलता है. रंग की पहचान पर सांस्कृतिक और भाषाई फ़ैक्टर का असर पड़ सकता है. जैसे, अक्सर यह कहा जाता है कि प्राचीन संस्कृतियों में नीले रंग के लिए कोई शब्द नहीं था. इसके बजाय, इसे हरे रंग के परिवार में शामिल किया जाता था.

57c46d9974c52e4a.pngकिसी रंग को गर्म या ठंडा माना जा सकता है. यह इस बात पर निर्भर करता है कि वह रंग, रंग स्पेक्ट्रम में कहां मौजूद है. लाल, नारंगी या पीले रंग की ओर बढ़ने पर, इमेज को ज़्यादा वॉर्म माना जाता है. वहीं, नीले, हरे या बैंगनी रंग की ओर बढ़ने पर, इमेज को ज़्यादा कूल माना जाता है. गर्म या ठंडे रंगों में भी, आपको गर्म और ठंडे टोन मिलेंगे. नीचे दी गई इमेज में, "वार्मर" पीले रंग में नारंगी रंग का ज़्यादा असर दिखता है, जबकि "कूलर" पीले रंग पर हरे रंग का ज़्यादा असर दिखता है. 597c6428ff6b9669.png

कलर हार्मनाइज़ेशन एल्गोरिदम, बिना बदले रंग की ह्यू और जिस रंग के साथ उसे हार्मनाइज़ किया जाना है उसकी ह्यू की जांच करता है. इससे, वह ऐसी ह्यू का पता लगाता है जो हार्मनाइज़ हो, लेकिन उसके मूल रंग की क्वालिटी में बदलाव न करे. पहले ग्राफ़िक में, स्पेक्ट्रम पर कम सामंजस्य वाले हरे, पीले, और नारंगी रंग दिखाए गए हैं. अगले ग्राफ़िक में, हरे और नारंगी रंग को पीले रंग के साथ मिलाया गया है. नया हरा रंग ज़्यादा गहरा है और नया नारंगी रंग ज़्यादा हल्का है.

नारंगी और हरे रंग के रंगत में बदलाव हुआ है, लेकिन इन्हें अब भी नारंगी और हरे रंग के तौर पर देखा जा सकता है.

766516c321348a7c.png

अगर आपको डिज़ाइन से जुड़े कुछ फ़ैसलों, एक्सप्लोरेशन, और बातों के बारे में ज़्यादा जानना है, तो मेरे सहयोगियों अयान डेनियल और एंड्रयू लू ने इस सेक्शन से ज़्यादा जानकारी वाली ब्लॉग पोस्ट लिखी है.

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);
  }

चार टोन का सेट वापस पाने के लिए, हमें कुछ और काम करना होगा.

सोर्स कलर पहले से मौजूद होने की वजह से, हमें यह करना होगा:

  1. यह तय करना कि इसे एक जैसा किया जाना चाहिए या नहीं,
  2. यह तय करता है कि हम डार्क मोड में हैं या नहीं.
  3. 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 और अडैप्टर का इस्तेमाल करेंगे.

e4555089b065b5a7.png

ट्रांज़िट डेटा सेव करना

हम ट्रांज़िट कार्ड के लिए टेक्स्ट डेटा और रंग की जानकारी सेव करने के लिए, एक डेटा क्लास का इस्तेमाल कर रहे हैं. यह डेटा क्लास, नाम, मंज़िल, और रंग के संसाधन का आईडी सेव करती है.

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 का इस्तेमाल करके, मिलते-जुलते रंगों वाला कॉन्टेक्स्ट वापस पा सकते हैं. इन दोनों तरीकों में एक मुख्य अंतर है. इसके अलावा, हमें एक थीम ओवरले भी देना होगा, जिसमें एक जैसे फ़ील्ड शामिल हों.

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. यूज़र इंटरफ़ेस (यूआई) के उदाहरण

डिफ़ॉल्ट थीम और कस्टम कलर, जिनमें कलर हार्मनाइज़ेशन की सुविधा नहीं होती

a5a02a72aef30529.png

कस्टम रंगों को एक जैसा करना

4ac88011173d6753.png d5084780d2c6b886.png

dd0c8b90eccd8bef.png c51f8a677b22cd54.png

12. खास जानकारी

इस कोडलैब में, आपने इनके बारे में सीखा:

  • कलर हार्मनाइज़ेशन के हमारे एल्गोरिदम के बारे में बुनियादी जानकारी
  • दिए गए रंग से कलर रोल जनरेट करने का तरीका.
  • यूज़र इंटरफ़ेस में किसी रंग को चुनिंदा तौर पर कैसे मिलाया जाए.
  • किसी थीम में एट्रिब्यूट के सेट को एक जैसा कैसे बनाया जाता है.

अगर आपका कोई सवाल है, तो @MaterialDesign on Twitter पर जाकर, हमसे कभी भी पूछें.

डिज़ाइन से जुड़े ज़्यादा कॉन्टेंट और ट्यूटोरियल के लिए, youtube.com/MaterialDesign पर बने रहें