پیاده سازی تم Dark در برنامه های اندرویدی

امروز تصمیم گرفتم تا برای اپلیکیشن بخشنامه تم تاریک (همون Dark Theme) رو پیاده سازی کنم.متاسفانه گوگل همیشه کثیف کاری داره و برای انجام دادن یه کار ساده باید هزارتا بدبختی بکشی!خلاصه اینکه برای پیاده سازی تم تاریک روش های مختلفی هست که هر کدوم معایب خودشو داره مثلا اگه از اندروید Q استفاده کنید خیلی راحت میتونید تم تاریک رو پیاده سازی کنید ولی فقط رو اندروید Q به بالا پشتیبانی میشه و متاسفانه بازهم گوگل توجهی به Backward-Compatiblity نداره.اما من روش متفاوتی رو در پیش گرفتم به این صورت که برای هر ویو یه ویو دارک ایجاد کردم و موقع اجرای ویو، متناسب با تم انتخاب شده ویو متناظر رو اجرا کردم.اینجا نحوه پیاده سازیش رو توضیح میدم و برای بررسی دقیق تر میتونید کدهای من رو بررسی کنید.اول از همه برای هر لایوت یه لایوت ایجاد کنید (به عنوان مثال لایوت اصلی اسمش mainactivity.xml باشه اسم لایوت دارک رو mainactivity_dark بزارید. البته اجباری نیستش)دوم محتوای mainactivity رو کپی کنید و داخل mainactivity_dark پیست کنید.سوم شروع کنید رنگ کنترل های داخل mainactivity_dark رو به رنگ دلخواهتون که اکثرا باید تیره باشه تغییر بدین.این کار رو باید برای هر لایوت که قصد دارین از حالت تیره پشتیبانی کنه انجام بدین.حالا ما باید وضعیت تم فعلی رو یجایی ذخیره کنیم و هنگام اجرای برنامه اون رو چک کنیم. برای این کار میتونیم از sharedpreferences استفاده کنیم.بنابر این یه تابع میسازیم تا تغییر تم رو ذخیره کنه: public void setThemeMode(int value) {SharedPreferences.Editor editor = getSharedPreferences("themeMode", MODE_PRIVATE).edit(); editor.putInt("KEY_NightMode", value); editor.commit(); }یه سوییچ باتونی چیزی روی لایوت قرار بدین و داخل رویدادش (چک شده یا نه یا کلیک و...) کدهای زیر رو بنویسیدlvTheme.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if(isChecked) setThemeMode(ThemeMode.Dark.ordinal()); else setThemeMode(ThemeMode.Light.ordinal()); restart(); } });در کد بالا چک کردیم اگه تم تاریک رو کاربر فعال کرده بود حالت تم رو روی تیره قرار دادیم در غیر این صورت روی روشن و در آخر برنامه رو ریستارت میکنیم.اما ThemeMode از کجا اومد؟ به جای اینکه درگیر 0 و 1 بشم و در طول مسیر یادم بره که 0 روشن بود یا تاریک و... اومدم یه enum ساختم تا کدنویسی خوانا تر بشه.public enum ThemeMode { Dark, Light }و کدهای ریستارت هم به این صورت:public void restart(){ Intent res = new Intent(getActivity(), MainActivity.class); startActivity(res); finish(); }حالا تنها چیزی که میمونه این هست که موقع اجرا تم مناسب رو با توجه به تنظیمات ذخیره شده اجرا کنیم.تو متد onCreate باید با یه شرط تم درخواستی رو بررسی کنیم به این صورت:اول تابع دریافت مقدار تم: public int getThemeMode() {SharedPreferences.Editor editor = getSharedPreferences("themeMode", MODE_PRIVATE).edit(); return editor.getInt("KEY_NightMode", ThemeMode.Light.ordinal()); }بعد: if (Prefs.getThemeMode() == ThemeMode.Light.ordinal()) { setContentView(R.layout.activity_main); } else { setContentView(R.layout.activity_main_dark); }به همین راحتی میتونیم حالت تاریک رو پیاده سازی کنیم البته یکم شاید زمانبر باشه ولی خب محدودیت خاصی نداره و رو همه ی نسخه ها بدون مشکل اجرا میشه.اگرم نیاز به استایل خاصی داشتید میتونید استایل های متفاوت تعریف کنید و موقع اجرای برنامه استایل مرتبط رو لود کنید به این صورت: <style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar"> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </style> <style name="AppThemeDark" parent="Theme.MaterialComponents.Light.NoActionBar"> <item name="colorPrimary">@color/accent_light_dark</item> <item name="colorPrimaryDark">@color/accent_dark</item> <item name="colorAccent">@color/accent_light_dark</item> <item name="md_background_color">@color/background_dark</item> <item name="md_title_color">#fff</item> <item name="md_content_color">#fff</item> <item name="md_negative_color">#fff</item> </style>و داخل شرطی که تم رو چک میکنیم میتونیم استایل رو هم به این صورت لود کنیم:setTheme(R.style.AppThemeDark);کار تمومه!البته یکمی زیادی خلاصه شد اگر سوالی داشتید بپرسید.درضمن کدها روی برنچ اصلی پروژه منتشر شده و میتونید از اونجا دقیقتر بررسی کنید.