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

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

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

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

डेवलपर को यह करना चाहिए

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

आप इन चीज़ों के बारे में जानेंगे

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

आपको इनकी ज़रूरत होगी

  • अगर आपको ज़्यादा जानना है, तो कंप्यूटर पर Android ऐप्लिकेशन इंस्टॉल होना चाहिए.

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

VoyaGi एक ट्रांज़िट ऐप्लिकेशन है, जिसमें डाइनैमिक थीम का इस्तेमाल पहले से ही किया गया है. बहुत-से ट्रांज़िट सिस्टम के लिए, ट्रेन, बस या ट्राम का रंग एक महत्वपूर्ण संकेतक होता है और इन्हें किसी भी डायनामिक प्राथमिक, द्वितीयक या तृतीयक रंग से बदला नहीं जा सकता. हम रंगीन बस, मेट्रो वगैरह के कार्ड के RecyclerView पर काम करेंगे.

62ff4b2fb6c9e14a.png

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

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

बढ़ाए गए रंग वाले सेक्शन में, आप रंग हटा सकते हैं या उनका नाम बदल सकते हैं.

20cc2cf72efef213.png

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

Topics.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. पसंद के मुताबिक रंग की जांच करना

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

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

}

getColorRoles नाम की MaterialColors क्लास में getColorRoles का इस्तेमाल करके, किसी आर्बिट्रेरी कलर से, मुख्य कलर के चार रोल वापस पाए जा सकते हैं. ऐसा करने से, किसी खास सीड कलर के हिसाब से रनटाइम के दौरान चार कलर रोल का वह सेट बनाया जा सकता है.

public static ColorRoles getColorRoles(
    @NonNull Context context,
    @ColorInt int color
) { /* implementation */ }

इसी तरह, आउटपुट वैल्यू भी कलर की असली वैल्यू होती हैं, न कि उनके लिए पॉइंटर.**

5. कलर हार्मनाइज़ेशन क्या है?

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

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

मटीरियल में रंग आपको रंग, क्रोमा, और टोन से पता चलता है. किसी रंग की विविधता किसी एक रंग श्रेणी बनाम अन्य रंग श्रेणी के सदस्य के रूप में किसी व्यक्ति की समझ से संबंधित होती है. टोन से यह पता चलता है कि वह कितना हल्का या गहरा है और क्रोमा का मतलब है कि वह कितना गहरा है. भाषा और संस्कृति से जुड़े लोगों की वजह से इस भाषा के रंग पर असर पड़ सकता है. जैसे, प्राचीन संस्कृतियों में नीले रंग के लिए कोई शब्द न होने की वजह से, इस शब्द को हरे रंग के परिवार में ही देखा जाता था.

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 ऑब्जेक्ट दिखाता है जो हार्मोनाइज़ किया गया है या सही तरीके से कॉन्फ़िगर नहीं किया गया है.

यह तय किया जा रहा है कि एक जैसे बनाना है या नहीं

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

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 में चालू कॉन्टेक्स्ट के आधार पर, नया कॉन्टेक्स्ट बनाना होगा.

अगर आप किसी रंग को एक जैसा नहीं करना चाहते, तो बस उसे HaronizedOptions में शामिल न करें.

val newContext = DynamicColors.wrapContextIfAvailable(requireContext())


val harmonizedOptions = HarmonizedColorsOptions.Builder()
 .setColorResourceIds(intArrayOf(R.color.custom1, R.color.custom2))
 .build();

harmonizedContext =
 HarmonizedColors.wrapContextIfAvailable(dynamicColorsContext, harmonizedOptions)

इसके लिए, एक जैसे बेस कलर को पहले ही हैंडल कर दिया गया है. इसलिए, MaterialColors.getColorRoles को कॉल करने के लिए onBindViewHolder को अपडेट किया जा सकता है. साथ ही, यह भी बताया जा सकता है कि जवाब में दी गई भूमिकाएं हल्के रंग की होनी चाहिए या गहरे रंग की.

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. खास जानकारी

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

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

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

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