১. শুরু করার আগে
এই কোডল্যাবে, আপনি শিখবেন কিভাবে আপনার কাস্টম রঙগুলিকে একটি গতিশীল থিম দ্বারা তৈরি রঙগুলির সাথে সামঞ্জস্যপূর্ণ করতে হয়।
পূর্বশর্ত
ডেভেলপারদের উচিত
- অ্যান্ড্রয়েডে মৌলিক থিমিং ধারণাগুলির সাথে পরিচিত।
- অ্যান্ড্রয়েড উইজেট ভিউ এবং তাদের বৈশিষ্ট্যগুলির সাথে কাজ করা আরামদায়ক
তুমি কি শিখবে
- একাধিক পদ্ধতি ব্যবহার করে আপনার অ্যাপ্লিকেশনে রঙের সমন্বয় কীভাবে ব্যবহার করবেন
- কিভাবে সুরেলাকরণ কাজ করে এবং কিভাবে এটি রঙ পরিবর্তন করে
তোমার যা লাগবে
- যদি আপনি চান, তাহলে অ্যান্ড্রয়েড ইনস্টল করা একটি কম্পিউটার।
2. অ্যাপ ওভারভিউ
Voyaĝi একটি ট্রানজিট অ্যাপ্লিকেশন যা ইতিমধ্যেই একটি গতিশীল থিম ব্যবহার করে। অনেক পরিবহন ব্যবস্থার জন্য, রঙ ট্রেন, বাস বা ট্রামের একটি গুরুত্বপূর্ণ সূচক এবং এগুলিকে যেকোনো গতিশীল প্রাথমিক, মাধ্যমিক বা তৃতীয় রঙ দ্বারা প্রতিস্থাপন করা যায় না। আমরা রঙিন ট্রানজিট কার্ডের RecyclerView-এর উপর আমাদের কাজ ফোকাস করব।

৩. একটি থিম তৈরি করা
আমরা আপনাকে প্রথমে Material3 থিম তৈরি করার জন্য আমাদের Material Theme Builder টুলটি ব্যবহার করার পরামর্শ দিচ্ছি। কাস্টম ট্যাবে, আপনি এখন আপনার থিমে আরও রঙ যোগ করতে পারেন। ডানদিকে, আপনাকে সেই রঙগুলির জন্য রঙের ভূমিকা এবং টোনাল প্যালেটগুলি দেখানো হবে।
বর্ধিত রঙ বিভাগে, আপনি রঙগুলি সরাতে বা পুনঃনামকরণ করতে পারেন।

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

নতুন রপ্তানি মূল্য বোঝা
আপনার থিমগুলিতে এই রঙগুলি এবং তাদের সাথে সম্পর্কিত রঙের ভূমিকাগুলি ব্যবহার করার অনুমতি দেওয়ার জন্য, আপনি সুরেলা করতে চান বা না চান, এক্সপোর্ট করা ডাউনলোডে এখন একটি 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>
৪. কাস্টম রঙ পরীক্ষা করা
ম্যাটেরিয়াল থিম বিল্ডারের সাইড প্যানেলে জুম করে আমরা দেখতে পাচ্ছি যে একটি কাস্টম রঙ যোগ করলে একটি প্যানেল দেখা যাবে যেখানে চারটি মূল রঙের ভূমিকা থাকবে, যার মধ্যে একটি হালকা এবং একটি গাঢ় প্যালেট থাকবে।

অ্যান্ড্রয়েড ভিউতে, আমরা আপনার জন্য এই রঙগুলি রপ্তানি করি কিন্তু পর্দার আড়ালে এগুলিকে 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 */ }
একইভাবে আউটপুট মানগুলি প্রকৃত রঙের মান, তাদের নির্দেশক নয়।**
৫. রঙ সমন্বয় কী?
ম্যাটেরিয়ালের নতুন রঙ ব্যবস্থাটি নকশা অনুসারে অ্যালগরিদমিক, একটি নির্দিষ্ট বীজ রঙ থেকে প্রাথমিক, মাধ্যমিক, তৃতীয় এবং নিরপেক্ষ রঙ তৈরি করে। অভ্যন্তরীণ এবং বহিরাগত অংশীদারদের সাথে কথা বলার সময় আমরা যে উদ্বেগের বিষয়টি অনেক পেয়েছি তা হল কিছু রঙের উপর নিয়ন্ত্রণ রেখে কীভাবে গতিশীল রঙ গ্রহণ করা যায়।
এই রঙগুলি প্রায়শই অ্যাপ্লিকেশনে একটি নির্দিষ্ট অর্থ বা প্রেক্ষাপট বহন করে যা যদি এলোমেলো রঙ দ্বারা প্রতিস্থাপিত হয় তবে তা হারিয়ে যাবে। বিকল্পভাবে, যদি এই রঙগুলি যেমন আছে তেমনই রেখে দেওয়া হয়, তাহলে এই রঙগুলি দৃশ্যত বিরক্তিকর বা স্থানের বাইরে দেখাতে পারে।
উপাদানে রঙ "তুমি" কে রঙ, ক্রোমা এবং স্বর দ্বারা বর্ণনা করা হয়। একটি রঙের রঙ অন্য রঙের পরিসরের তুলনায় অন্য রঙের সদস্য হিসাবে এটি সম্পর্কে তার ধারণার সাথে সম্পর্কিত। স্বর বর্ণনা করে যে এটি কতটা হালকা বা গাঢ় দেখাচ্ছে এবং ক্রোমা হল রঙের তীব্রতা। রঙের উপলব্ধি সাংস্কৃতিক এবং ভাষাগত কারণগুলির দ্বারা প্রভাবিত হতে পারে, যেমন প্রাচীন সংস্কৃতিতে নীলের জন্য একটি শব্দের অভাব প্রায়শই উল্লেখ করা হয়েছিল যেখানে এটি সবুজের মতো একই পরিবারে দেখা হত।
রঙের বর্ণালীতে কোন নির্দিষ্ট রঙটি কোথায় অবস্থিত তার উপর নির্ভর করে তাকে উষ্ণ বা শীতল বলে বিবেচনা করা যেতে পারে। লাল, কমলা বা হলুদ রঙের দিকে সরে গেলে এটি উষ্ণতর হয়ে ওঠে এবং নীল, সবুজ বা বেগুনি রঙের দিকে সরে গেলে এটি শীতল হয়ে ওঠে বলে মনে করা হয়। এমনকি উষ্ণ বা শীতল রঙের মধ্যেও, আপনার উষ্ণ এবং শীতল টোন থাকবে। নীচে, "উষ্ণ" হলুদটি আরও কমলা রঙের যেখানে "শীতল" হলুদটি সবুজ দ্বারা বেশি প্রভাবিত হয়। 
রঙের সমন্বয় অ্যালগরিদম অপরিবর্তিত রঙের রঙ এবং এর সাথে সামঞ্জস্যপূর্ণ রঙের পরীক্ষা করে এমন একটি রঙ খুঁজে বের করে যা সুরেলা কিন্তু এর অন্তর্নিহিত রঙের গুণাবলী পরিবর্তন করে না। প্রথম গ্রাফিকে, একটি বর্ণালীতে কম সুরেলা সবুজ, হলুদ এবং কমলা রঙ রয়েছে। পরবর্তী গ্রাফিকে, সবুজ এবং কমলা হলুদ রঙের সাথে সামঞ্জস্যপূর্ণ করা হয়েছে। নতুন সবুজ আরও উষ্ণ এবং নতুন কমলা আরও শীতল।
কমলা এবং সবুজ রঙের রঙ বদলে গেছে, কিন্তু এখনও কমলা এবং সবুজ হিসেবেই দেখা যায়।

যদি আপনি কিছু নকশা সিদ্ধান্ত, অনুসন্ধান এবং বিবেচনা সম্পর্কে আরও জানতে চান, তাহলে আমার সহকর্মী আয়ান ড্যানিয়েলস এবং অ্যান্ড্রু লু এই বিভাগের চেয়ে আরও গভীরভাবে একটি ব্লগ পোস্ট লিখেছেন।
৬. ম্যানুয়ালি রঙের সমন্বয় করা
একটি একক স্বরকে সুরেলা করার জন্য, 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বস্তু ফেরত পাঠান।
সমন্বয় সাধন করা হবে কিনা তা নির্ধারণ করা
ম্যাটেরিয়াল থিম বিল্ডার থেকে এক্সপোর্ট করা থিমে, আমরা নামকরণ 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)
}
৭. ট্রানজিট কার্ড তৈরি করা
আগেই উল্লেখ করা হয়েছে, আমরা ট্রানজিট কার্ডের সংগ্রহ পূরণ এবং রঙ করার জন্য একটি 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)
}
}
৮. স্বয়ংক্রিয়ভাবে রঙ সমন্বয় করা
ম্যানুয়ালি হারমোনাইজেশন পরিচালনার বিকল্প হিসেবে, আপনি এটি আপনার জন্য পরিচালনা করতে পারেন। হারমোনাইজডকালারঅপশনস হল একটি বিল্ডার ক্লাস যা আপনাকে এখন পর্যন্ত আমরা যা করেছি তার বেশিরভাগই হাতে করে নির্দিষ্ট করতে দেয়।
বর্তমান প্রেক্ষাপট পুনরুদ্ধার করার পর যাতে আপনি বর্তমান গতিশীল স্কিমে অ্যাক্সেস পেতে পারেন, আপনাকে যে মৌলিক রঙগুলিকে সামঞ্জস্য করতে চান তা নির্দিষ্ট করতে হবে এবং সেই 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)
}
}
৯. থিম বৈশিষ্ট্যগুলিকে স্বয়ংক্রিয়ভাবে সমন্বয় করা
The methods shown up until now rely on retrieving the color roles from an individual color. That's great for showing that a real tone is being generated but not realistic to most existing applications. You will be likely not deriving a color directly but instead using an existing theme attribute.
এই কোডল্যাবে এর আগে, আমরা থিম অ্যাট্রিবিউট এক্সপোর্ট করার বিষয়ে কথা বলেছিলাম।
<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 লেআউট ফাইলের ভিতরে, আমরা সেই সুরেলা বৈশিষ্ট্যগুলিকে স্বাভাবিকভাবে ব্যবহার করতে পারি।
<?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>
১০. সোর্স কোড
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)
}
}
১১. উদাহরণ UI গুলি
ডিফল্ট থিমিং এবং কাস্টম রঙ কোন সুরেলাকরণ ছাড়াই

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




১২. সারাংশ
এই কোডল্যাবে, আপনি শিখেছেন:
- আমাদের রঙ সমন্বয় অ্যালগরিদমের মূল বিষয়গুলি
- একটি প্রদত্ত দেখা রঙ থেকে রঙের ভূমিকা কীভাবে তৈরি করবেন।
- একটি ইউজার ইন্টারফেসে একটি রঙ কীভাবে বেছে বেছে সমন্বয় করা যায়।
- একটি থিমের বৈশিষ্ট্যের একটি সেট কীভাবে সামঞ্জস্যপূর্ণ করা যায়।
যদি আপনার কোন প্রশ্ন থাকে, তাহলে টুইটারে @MaterialDesign ব্যবহার করে যেকোনো সময় আমাদের জিজ্ঞাসা করতে দ্বিধা করবেন না।
আরও ডিজাইন কন্টেন্ট এবং টিউটোরিয়ালের জন্য youtube.com/MaterialDesign- এ আমাদের সাথেই থাকুন।