অ্যান্ড্রয়েড ভিউতে বেসিক কালার হারমোনাইজেশন

1. আপনি শুরু করার আগে

এই কোডল্যাবে, আপনি শিখবেন কীভাবে আপনার কাস্টম রঙগুলিকে একটি ডায়নামিক থিম দ্বারা তৈরি করা রঙের সাথে সামঞ্জস্য করতে হয়।

পূর্বশর্ত

ডেভেলপারদের হতে হবে

  • অ্যান্ড্রয়েডে মৌলিক থিমিং ধারণার সাথে পরিচিত
  • অ্যান্ড্রয়েড উইজেট ভিউ এবং তাদের বৈশিষ্ট্যগুলির সাথে কাজ করা আরামদায়ক

আপনি কি শিখবেন

  • একাধিক পদ্ধতি ব্যবহার করে আপনার অ্যাপ্লিকেশনে রঙের সমন্বয় কীভাবে ব্যবহার করবেন
  • কীভাবে সুরেলা কাজ করে এবং কীভাবে এটি রঙ পরিবর্তন করে

আপনি কি প্রয়োজন হবে

  • আপনি যদি অনুসরণ করতে চান তাহলে Android সহ একটি কম্পিউটার ইনস্টল করুন৷

2. অ্যাপ ওভারভিউ

Voyaĝi একটি ট্রানজিট অ্যাপ্লিকেশন যা ইতিমধ্যে একটি গতিশীল থিম ব্যবহার করে৷ অনেক ট্রানজিট সিস্টেমের জন্য, রঙ হল ট্রেন, বাস বা ট্রামের একটি গুরুত্বপূর্ণ সূচক এবং এগুলিকে যে কোন গতিশীল প্রাথমিক, মাধ্যমিক বা তৃতীয় রঙের দ্বারা প্রতিস্থাপন করা যায় না। আমরা রঙিন ট্রানজিট কার্ডের রিসাইক্লারভিউতে আমাদের কাজকে ফোকাস করব।

62ff4b2fb6c9e14a.png

3. একটি থিম তৈরি করা

আমরা একটি Material3 থিম তৈরি করার জন্য আপনার প্রথম স্টপ হিসাবে আমাদের টুল ম্যাটেরিয়াল থিম বিল্ডার ব্যবহার করার পরামর্শ দিই। কাস্টম ট্যাবে, আপনি এখন আপনার থিমে আরও রঙ যোগ করতে পারেন। ডানদিকে, আপনাকে সেই রঙগুলির জন্য রঙের ভূমিকা এবং টোনাল প্যালেটগুলি দেখানো হবে।

বর্ধিত রঙ বিভাগে, আপনি রং অপসারণ বা পুনঃনামকরণ করতে পারেন।

20cc2cf72efef213.png

রপ্তানি মেনু সম্ভাব্য রপ্তানির বিকল্পগুলির একটি সংখ্যা প্রদর্শন করবে। লেখার সময়, মেটেরিয়াল থিম বিল্ডারের সুরেলা সেটিংসের বিশেষ হ্যান্ডলিং শুধুমাত্র অ্যান্ড্রয়েড ভিউতে উপলব্ধ

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> বৈশিষ্ট্যগুলি প্রতিফলিত করে যে ডেভেলপার মেটেরিয়াল থিম বিল্ডারের বিকল্পটি নির্বাচন করেছেন কিনা। এটি মূল থিমের রঙ পরিবর্তন করবে না।

<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

অ্যান্ড্রয়েড ভিউতে, আমরা আপনার জন্য এই রঙগুলি রপ্তানি করি কিন্তু পর্দার আড়ালে সেগুলি 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 সক্ষম প্রসঙ্গটির উপর ভিত্তি করে একটি নতুন প্রসঙ্গ তৈরি করতে হবে।

আপনি যদি একটি রঙের সাথে সামঞ্জস্য করতে না চান তবে এটিকে সুরক্ষিত বিকল্পগুলিতে অন্তর্ভুক্ত করবেন না।

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>

প্রথম স্বয়ংক্রিয় পদ্ধতির মতো, আমরা হারমোনাইজড কালার অপশনে মান প্রদান করতে পারি এবং হারমোনাইজড রঙের সাথে একটি প্রসঙ্গ পুনরুদ্ধার করতে হারমোনাইজড কালার ব্যবহার করতে পারি। দুটি পদ্ধতির মধ্যে একটি মূল পার্থক্য রয়েছে। আমাদের অতিরিক্তভাবে একটি থিম ওভারলে প্রদান করতে হবে যাতে ক্ষেত্রগুলিকে সুরেলা করা যায়।

val dynamicColorsContext = DynamicColors.wrapContextIfAvailable(requireContext())

// Harmonizing individual attributes
val harmonizedColorAttributes = HarmonizedColorAttributes.create(
 intArrayOf(
   R.attr.colorCustom1,
   R.attr.colorOnCustom1,
   R.attr.colorCustom1Container,
   R.attr.colorOnCustom1Container,
   R.attr.colorCustom2,
   R.attr.colorOnCustom2,
   R.attr.colorCustom2Container,
   R.attr.colorOnCustom2Container
 ), R.style.AppTheme_Overlay
)
val harmonizedOptions =
 HarmonizedColorsOptions.Builder().setColorAttributes(harmonizedColorAttributes).build()

val harmonizedContext =
 HarmonizedColors.wrapContextIfAvailable(dynamicColorsContext, harmonizedOptions)

আপনার অ্যাডাপ্টার সুরেলা প্রসঙ্গ ব্যবহার করবে। থিম ওভারলে মানগুলি অসংলগ্ন আলো বা অন্ধকার বৈকল্পিক উল্লেখ করা উচিত।

<style name="AppTheme.Overlay" parent="AppTheme">
   <item name="colorCustom1">@color/harmonized_colorCustom1</item>
   <item name="colorOnCustom1">@color/harmonized_colorOnCustom1</item>
   <item name="colorCustom1Container">@color/harmonized_colorCustom1Container</item>
   <item name="colorOnCustom1Container">@color/harmonized_colorOnCustom1Container</item>

   <item name="colorCustom2">@color/harmonized_colorCustom2</item>
   <item name="colorOnCustom2">@color/harmonized_colorOnCustom2</item>
   <item name="colorCustom2Container">@color/harmonized_colorCustom2Container</item>
   <item name="colorOnCustom2Container">@color/harmonized_colorOnCustom2Container</item>
</style>

XML লেআউট ফাইলের ভিতরে, আমরা সেই সুরেলা বৈশিষ্ট্যগুলিকে স্বাভাবিক হিসাবে ব্যবহার করতে পারি।

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   style="?attr/materialCardViewFilledStyle"
   android:id="@+id/card"
   android:layout_width="80dp"
   android:layout_height="100dp"
   android:layout_marginStart="8dp"
   app:cardBackgroundColor="?attr/colorCustom1Container"
   >

   <androidx.constraintlayout.widget.ConstraintLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:layout_margin="8dp">

       <TextView
           android:id="@+id/transitName"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:textSize="28sp"
           android:textStyle="bold"
           android:textColor="?attr/colorOnCustom1Container"
           app:layout_constraintTop_toTopOf="parent" />

       <TextView
           android:id="@+id/transitDestination"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_marginBottom="4dp"
           android:textColor="?attr/colorOnCustom1Container"
           app:layout_constraintBottom_toBottomOf="parent" />
   </androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>

10. সোর্স কোড

package com.example.voyagi.harmonization.ui.dashboard

import android.content.Context
import android.content.res.Configuration
import android.graphics.Typeface
import android.os.Bundle
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.voyagi.harmonization.R
import com.example.voyagi.harmonization.databinding.FragmentDashboardBinding
import com.example.voyagi.harmonization.ui.home.TransitCardAdapter
import com.example.voyagi.harmonization.ui.home.TransitInfo
import com.google.android.material.card.MaterialCardView
import com.google.android.material.color.ColorRoles
import com.google.android.material.color.DynamicColors
import com.google.android.material.color.HarmonizedColorAttributes
import com.google.android.material.color.HarmonizedColors
import com.google.android.material.color.HarmonizedColorsOptions
import com.google.android.material.color.MaterialColors


class DashboardFragment : Fragment() {

 enum class TransitMode { BUS, TRAIN }
 data class TransitInfo2(val name: String, val destination: String, val mode: TransitMode)

 private lateinit var dashboardViewModel: DashboardViewModel
 private var _binding: FragmentDashboardBinding? = null

 // This property is only valid between onCreateView and
 // onDestroyView.
 private val binding get() = _binding!!

 override fun onCreateView(
   inflater: LayoutInflater,
   container: ViewGroup?,
   savedInstanceState: Bundle?
 ): View? {
   dashboardViewModel =
     ViewModelProvider(this).get(DashboardViewModel::class.java)

   _binding = FragmentDashboardBinding.inflate(inflater, container, false)
   val root: View = binding.root


   val recyclerView = binding.recyclerView

   val transitItems = listOf(
     TransitInfo2("53", "Irvine", TransitMode.BUS),
     TransitInfo2("153", "Brea", TransitMode.BUS),
     TransitInfo2("Orange County Line", "Oceanside", TransitMode.TRAIN),
     TransitInfo2("Pacific Surfliner", "San Diego", TransitMode.TRAIN)
   )
  
   val dynamicColorsContext = DynamicColors.wrapContextIfAvailable(requireContext())

   // Harmonizing individual attributes
   val harmonizedColorAttributes = HarmonizedColorAttributes.create(
     intArrayOf(
       R.attr.colorCustom1,
       R.attr.colorOnCustom1,
       R.attr.colorCustom1Container,
       R.attr.colorOnCustom1Container,
       R.attr.colorCustom2,
       R.attr.colorOnCustom2,
       R.attr.colorCustom2Container,
       R.attr.colorOnCustom2Container
     ), R.style.AppTheme_Overlay
   )
   val harmonizedOptions =
     HarmonizedColorsOptions.Builder().setColorAttributes(harmonizedColorAttributes).build()

   val harmonizedContext =
     HarmonizedColors.wrapContextIfAvailable(dynamicColorsContext, harmonizedOptions)


   val adapter = TransitCardAdapterAttr(transitItems, harmonizedContext)
   recyclerView.adapter = adapter
   recyclerView.layoutManager =
     LinearLayoutManager(harmonizedContext, RecyclerView.HORIZONTAL, false)

   return root
 }

 override fun onDestroyView() {
   super.onDestroyView()
   _binding = null
 }
}

class TransitCardAdapterAttr(val list: List<DashboardFragment.TransitInfo2>, context: Context) :
 RecyclerView.Adapter<RecyclerView.ViewHolder>() {
 val colorRolesMap = mutableMapOf<Int, ColorRoles>()
 private var harmonizedContext: Context? = context

 override fun onCreateViewHolder(
   parent: ViewGroup,
   viewType: Int
 ): RecyclerView.ViewHolder {
   return if (viewType == DashboardFragment.TransitMode.BUS.ordinal) {
     BusViewHolder(LayoutInflater.from(harmonizedContext).inflate(R.layout.transit_item_bus, parent, false))
   } else TrainViewHolder(LayoutInflater.from(harmonizedContext).inflate(R.layout.transit_item_train, parent, false))
 }

 override fun getItemCount(): Int {
   return list.size
 }

 override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
   val item = list[position]
   if (item.mode.ordinal == DashboardFragment.TransitMode.BUS.ordinal) {
     (holder as BusViewHolder).bind(item)
     (holder as TransitBindable).adjustNameLength()
   } else {
       (holder as TrainViewHolder).bind(item)
       (holder as TransitBindable).adjustNameLength()
   }
 }

 override fun getItemViewType(position: Int): Int {
   return list[position].mode.ordinal
 }

 interface TransitBindable {
   val card: MaterialCardView
   var transitName: TextView
   var transitDestination: TextView

   fun bind(item: DashboardFragment.TransitInfo2) {
     transitName.text = item.name
     transitDestination.text = item.destination
   }
   fun Float.toDp(context: Context) =
     TypedValue.applyDimension(
       TypedValue.COMPLEX_UNIT_DIP,
       this,
       context.resources.displayMetrics
     )
   fun adjustNameLength(){
     if (transitName.length() > 4) {
       val layoutParams = card.layoutParams
       layoutParams.width = 100f.toDp(card.context).toInt()
       card.layoutParams = layoutParams
       transitName.setTypeface(Typeface.DEFAULT_BOLD);

       transitName.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16.0f)
     }
   }
 }

 inner class BusViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), TransitBindable {
   override val card: MaterialCardView = itemView.findViewById(R.id.card)
   override var transitName: TextView = itemView.findViewById(R.id.transitName)
   override var transitDestination: TextView = itemView.findViewById(R.id.transitDestination)
 }
 inner class TrainViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), TransitBindable {
   override val card: MaterialCardView = itemView.findViewById(R.id.card)
   override var transitName: TextView = itemView.findViewById(R.id.transitName)
   override var transitDestination: TextView = itemView.findViewById(R.id.transitDestination)
 }
}

11. উদাহরণ UI

কোনো হারমোনাইজেশন ছাড়াই ডিফল্ট থিমিং এবং কাস্টম রং

a5a02a72aef30529.png

সুরেলা কাস্টম রং

4ac88011173d6753.pngd5084780d2c6b886.png

dd0c8b90eccd8bef.pngc51f8a677b22cd54.png

12. সারাংশ

এই কোডল্যাবে, আপনি শিখেছেন:

  • আমাদের রঙ সমন্বয় অ্যালগরিদম মৌলিক
  • প্রদত্ত দেখা রঙ থেকে কীভাবে রঙের ভূমিকা তৈরি করবেন।
  • একটি ব্যবহারকারী ইন্টারফেসে একটি রঙকে কীভাবে বেছে বেছে সুরেলা করা যায়।
  • একটি থিমে বৈশিষ্ট্যগুলির একটি সেটকে কীভাবে সামঞ্জস্য করা যায়।

আপনার যদি প্রশ্ন থাকে, টুইটারে @MaterialDesign ব্যবহার করে যেকোনো সময় নির্দ্বিধায় আমাদের জিজ্ঞাসা করুন।

youtube.com/MaterialDesign- এ আরও ডিজাইনের বিষয়বস্তু এবং টিউটোরিয়ালের জন্য সাথে থাকুন