如果在网上搜换肤,方案五花八门,但是根据app的需求,以及无设计师的情况下,基本上简约风格app,这种风格下只需要几个颜色就行了,根本不需要动态从磁盘加载皮肤apk,而且通过反射操作侵入性太强,因此attr大法才是最适合目前的我所做的app实现。
网上的换肤方法侵入性太强,而纯色app不需要各种花式的皮肤,基本上2三套颜色就行了,主色,次色,而其他则非黑即白。深色模式实现就更简单了,用着色tint就实现了。
经过了几天的研究发现,动态修改setTheme
是有bug的, bug就是状态栏颜色和actionbar在未在activity定义attr背景的情况下实现修改actionbar的就有这个bug,2015年在stackoverflow网站上就有人提出解决方法,这个解决方法是没有办法的方法,需要在baseactivity进行判断,如果有actionbar就设置actionbar颜色,
以及设置状态栏颜色。
关于偏见
偏见源于他们对主题的掌握度不够,因为所有的地方都需要设置颜色,实际上基本是不需要单独设置的,只有个别控件的着色要单独设置除外,也就是基本上可以在全局主题里面几行代码可以搞定,网上却吐槽非常麻烦需要单独设置attr?,大言不惭的贬低官方的技术,试问自己对主题的各种属性的作用又了解多少呢?
达到的效果
在颜色选择器中是可以直接使用实现的属性?attr
而在drawable,则需要包裹一层drawable引用color,直接使用color
属性也是会报错的,
正常的按钮,对话框,控件不需要再通过代码,xml设置颜色,
而网上的操作性太麻烦,正规的写法就是官方的写法,能不在view xml定义的就不在view xml定义
这样侵入性比较小。
颜色主题需要注意的事项
drawable要使用颜色属性只能包裹一层shape
第三方自定义ui 有的是只支持颜色不支持attr的。则需要进行额外适配处理,
其处理方式就是根据属性查找对应的颜色 或者id (colorres)
颜色属性找颜色
public static int attrFetchColor(Context context,int attrColor) {
int[] attribute = new int[]{attrColor};
TypedArray array =context.getTheme().obtainStyledAttributes(attribute);
int color = array.getColor(0, AppContext.getInstance().getResources().getColor(R.color.themeColor));
array.recycle();
return color;
}
查找id
public static int attrFetchColorId(Context context,int attrColor) {
int[] attribute = new int[]{attrColor};
TypedArray array = context.obtainStyledAttributes(attribute);
int colorID = array.getResourceId(0, R.color.themeColor);
array.recycle();
return colorID;
}
在baseactivity onCreate中修改状态栏颜色
public static void updateActionBarAndStatusColor(Activity activity) {
if (activity instanceof AppCompatActivity) {
AppCompatActivity appCompatActivity = (AppCompatActivity) activity;
ActionBar supportActionBar = appCompatActivity.getSupportActionBar();
if (supportActionBar != null) {
int i = AppUtils.attrFetchColor(activity,R.attr.defaultThemeColor);
supportActionBar.setBackgroundDrawable(new ColorDrawable(i));
}
}
android.app.ActionBar actionBar = activity.getActionBar();
if (actionBar != null) {
int color = AppUtils.attrFetchColor(activity,R.attr.defaultThemeColor);
actionBar.setBackgroundDrawable(new ColorDrawable(color));
}
if (Build.VERSION.SDK_INT >= 21) {
// getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
// getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
int statusBarBackground = AppUtils.attrFetchColor(activity,R.attr.colorPrimaryDark);
Window window = activity.getWindow();
// window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
// window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
// window.setStatusBarColor(backgroundColor);
window.setStatusBarColor(statusBarBackground);
}
}
主题修改代码
getBinding().rlMyThemeSetting.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
int currentMode = NightModeUtils.Companion.getSpfThemeMode(AppContext.getInstance());
ThemeMode[] values = ThemeMode.values();
CharSequence[] items=new CharSequence[values.length];
for (int i = 0; i < values.length; i++) {
String title = AppContext.getInstance().getString(values[i].getStringValueId());
items[i]=title;
}
builder.setSingleChoiceItems(items, currentMode, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (which != currentMode) {
dialog.dismiss();
ThemeMode value = values[which];
NightModeUtils.getInstance(AppContext.getInstance()).setThemeMode(value);
EventBus.getDefault().post(new ThemeChangeEvent().themeChange());
}
}
});
builder.setTitle(AppContext.getStr(R.string.deep_color_theme_setting));
builder.show();
}
});
收到事件进行recreate();
,我这里是在mainact,mainact可直接recreate
,无需finish
适配起来挺快的,通过各种批量替换
但是需要注意的是,第三方对颜色适配做的很烂,哪怕大家众所周知的smartrefresh也不支持属性引用颜色。 因此主要的适配就是处理第三方框架的问题,
这个适配工作基本上一天就搞定了。
另外花的实际是纠结研究为啥actionbar和状态栏颜色通过setTheme无效花费了很多时间,在attach里面替换,以及研究theme的各种方法,以及引用再引用的方式也解决不了问题,
最后既然无效我只能另外走方法了,强制设置。
attr.xml定义
其实现在每一个主题都实现了,
下面附上theme.xml 仔细看 都实现了defaultThemeColor defaultThemeColorSecond 属性,
root.
color实现
#5768FE
#f2c4c4
#5766FF
@color/themeColor
#4F60FE
#536DFE
@color/themeColor
@color/white
@color/light_blue_disable
@color/teal_700
@color/black
@color/purple_300
#E040FB
@color/purple_300
@color/purple_300
#7B1FA2
@color/purple_300
@color/white
#FF9800
#FF9800
#F57C00
#FF9800
#FF9800
#FF9800
@color/white
@color/green_700
@color/green_700
@color/green_700
#388E3C
#4CAF50
@color/green_700
@color/white
@color/blue_900
@color/blue_900
@color/blue_900
@color/blue_900
#448AFF
@color/blue_900
@color/white
@color/quantum_black_100
@color/quantum_black_100
@color/quantum_black_100
#5D4037
@color/quantum_black_100
@color/white
#795548
#795548
#9E9E9E
#5D4037
@color/black_overlay
@color/black
#009688
#757575
#00BCD4
#00796B
@color/black_overlay
@color/black
#00BCD4
#0097A7
#009688
#0097A7
#B2EBF2
#FFFFFF
#FFC107
#FFC107
#FFC107
#FFA000
#212121
#212121
#FF5722
#FF5722
#FF4081
#E64A19
#212121
#212121
#E64A19
#607D8B
#757575
#9E9E9E
#455A64
@color/black_overlay
@color/black
深色主题values-night themes.xml
把正常颜色的拷贝过来然后指定颜色就行了,
修改主题的代码 baseactivityonCreate调用在setContentView之前
fun applySetting(context: Context?): Int {
var themeId = 0;
// 检查主题
val themeModeInt = getSpfThemeMode()
val themeMode = ThemeMode.parseOfInt(themeModeInt)
var disableActionBar: Boolean = false;
var actName: String = "";
var needActionBar:Int =-1;
if (context is BaseActivity) {
needActionBar = context.needActionBar()
if( needActionBar == 1) {
disableActionBar = false;
}else if( needActionBar == 0) {
disableActionBar = true;
}
}
actName = context?.javaClass?.simpleName + " ,是否禁用actionBar " + disableActionBar;
if (context is AppCompatActivity) {
if (needActionBar==-1) {
var act: AppCompatActivity = context;
disableActionBar = if(act.supportActionBar!=null) true else true;//act.supportActionBar is WindowDecorActionBar
}
}
Log.w("ThemeStyle", "主题模式 ${themeMode.name} act $actName");
if (themeMode == ThemeMode.BLUE_THEME) {
if (disableActionBar) {
themeId = R.style.Theme_MyApplication_Blue_NoActionBar
} else {
themeId = R.style.Theme_MyApplication_Blue
}
} else if (themeMode == ThemeMode.YELLOW_THEME) {
if (disableActionBar) {
themeId = R.style.Theme_MyApplication_Yellow_NoActionBar
} else {
themeId = R.style.Theme_MyApplication_Yellow
}
} else if (themeMode == ThemeMode.PURPLE_THEME) {
if (disableActionBar) {
themeId = R.style.Theme_MyApplication_PURPLE_NoActionBar
} else {
themeId = R.style.Theme_MyApplication_PURPLE
}
} else if (themeMode == ThemeMode.GREEN_THEME) {
if (disableActionBar) {
themeId = R.style.Theme_MyApplication_GREEN_NoActionBar;
} else {
themeId = R.style.Theme_MyApplication_GREEN
}
} else if (themeMode == ThemeMode.GREY_THEME) {
if (disableActionBar) {
themeId = R.style.Theme_MyApplication_Grey_NoActionBar;
} else {
themeId = R.style.Theme_MyApplication_Grey
}
} else if (themeMode == ThemeMode.BROWN_THEME) {
if (disableActionBar) {
themeId = R.style.Theme_MyApplication_Brown_NoActionBar
} else {
themeId = R.style.Theme_MyApplication_Brown
}
} else if (themeMode == ThemeMode.DEEP_ORANGE_THEME) {
if (disableActionBar) {
themeId = R.style.Theme_MyApplication_DeepOrange_NoActionBar
} else {
themeId = R.style.Theme_MyApplication_DeepOrange
}
} else if (themeMode == ThemeMode.AMBER_THEME) {
if (disableActionBar) {
themeId = R.style.Theme_MyApplication_Amber_NoActionBar
} else {
themeId = R.style.Theme_MyApplication_Amber
}
} else if (themeMode == ThemeMode.CYAN_THEME) {
if (disableActionBar) {
themeId = R.style.Theme_MyApplication_Cyan_NoActionBar
} else {
themeId = R.style.Theme_MyApplication_Cyan
}
} else if (themeMode == ThemeMode.BLACK_THEME) {
if (disableActionBar) {
themeId = R.style.Theme_MyApplication_BLACK_NoActionBar;
} else {
themeId = R.style.Theme_MyApplication_BLACK;
}
} else {
if (disableActionBar) {//如果appcontext设置actionbar,act manifest.xml没有声明解除actionbar,然后调用setSupportBar则会抛出异常,因此需要处理适配。
themeId = R.style.Theme_MyApplication_DefaultBlue_NoActionBar
} else {
themeId = R.style.Theme_MyApplication_DefaultBlue
}
}
if (themeId != 0) {
context?.setTheme(themeId)
// return themeId;
}
AppCompatDelegate.setDefaultNightMode(
when (themeMode) {
ThemeMode.MODE_ALWAYS_ON -> AppCompatDelegate.MODE_NIGHT_YES
ThemeMode.MODE_ALWAYS_OFF -> AppCompatDelegate.MODE_NIGHT_NO
ThemeMode.MODE_FOLLOW_SYSTEM -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
else -> {
AppCompatDelegate.MODE_NIGHT_NO
}
}
)
return themeId
}
如果不为0,
if (color != 0) {
// getTheme().applyStyle(color, true);
AppUtils.updateActionBarAndStatusColor(this);
}
另外补充一点,按钮这些一般我都没有修改在每一个activity xml设置的,遵循material design以及theme的写法,全局就可以进行操作的,在
就完成了适配,
主要的适配工作还是处理selector的颜色以及第三方框架引用了颜色的情况,另外处理深色兼容的适配。
另外关于重启生效的事情,recreate也是只能修改部分颜色而在我自己曾经写的一款app没有这样的问题,经过比较发现,我的那个app没有一个app是额外指定了主题的,所以状态栏颜色等都能自动马上生效,而且不需要通过代码修改状态栏颜色,因此这个bug是act的xml定义的主题和代码设置的主题冲突bug.
关于This Activity already has an action bar supplie
This Activity already has an action bar supplied by the window decor. Do not request Window.FEATURE_SUPPORT_ACTION_BAR and set windowActionBar to false in your theme to use a Toolbar instead.
at androidx.appcompat.app.AppCompatDelegateImpl.setSupportActionBar(Ap
如果在actionbar已经在xml中指定的情况(如果act没有指定则从application节点继承),在代码里面继续设置是会报错的,可在setContentView代码之前调用setTheme指定不包含actionbar的解除actionbar
则调用setSupportActionBar
不会抛出已设置的错误。
setSupportActionBar的作用是把布局xml中的toolbar 依附到act中。
关于状态栏通过setTheme recreate有些东西不生效 ,重启不生效的原因 最根本原因是使用了MaterialComponents 主题而不是Theme.AppCompat,但是不使用MaterialComponents主题会导致google的组件样式无法全局生效,这是个矛盾,而且导致我的app大量崩溃,都是因为google的组件问题。
然后我继续找问题,还是改成了MaterialComponents主题,发现recreate又没有bug了,可以不重启全部更新。 之前又不行,我搞的精神崩溃了,也许是编译缓存问题??。。
总之最后通过recreate可以实现当前界面更新状态栏,不过第二个界面则不行,需要代码设置。不过体验比之前好了,不需要彻底重启app,由于保存了状态,基本上切换主题就感觉没有重启act的感觉,实际上是重建了的。
最后的最后我发现深色切换 回来或者切换过去,会导致很多地方不生效,比如状态栏,部分背景,
在recreate执行之前重新设置一遍主题可以解决此问题
if (which != currentMode) {
dialog.dismiss();
ThemeMode value = values[which];
NightModeUtils.getInstance(AppContext.getInstance()).setThemeMode(value);
if (currentMode == 0 || which == 0) {//如果是从深色切换过来的就需要先执行setTheme,也就是执行了2次。
NightModeUtils.getInstance(getContext()).applySetting(getContext());
getActivity().recreate();
} else {
EventBus.getDefault().post(new ThemeChangeEvent().themeChange());
}
}
最后的最后是这样的
ThemeMode value = values[which];
NightModeUtils.getInstance(AppContext.getInstance()).setThemeMode(value);
if (currentMode == 0 || which == 0) {
//call setTheme
NightModeUtils.getInstance(getContext()).applySetting(getContext());
//如果重启会发现部分没生效,深色模式的切换是不需要重启就能生效的
} else {
getActivity().recreate();
}
完整代码
import android.app.Activity
import android.content.Context
import android.content.res.TypedArray
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import com.sotrun.app.R
import com.sotrun.app.base.BaseActivity
import com.sotrun.app.utils.DateUtils
import java.io.Serializable
import java.util.*
open class NightModeUtils constructor(context: Context) {
private val mSpf =
context.applicationContext.getSharedPreferences(SP_THEME_NAME, Context.MODE_PRIVATE)
companion object {
@Volatile
private var INSTANCE: NightModeUtils? = null
fun getSpfThemeMode(context: Context): Int =
context.getSharedPreferences(SP_THEME_NAME, Context.MODE_PRIVATE)
.getInt(SPF_THEME_MODE, ThemeMode.MODE_FOLLOW_SYSTEM.intValue)
@JvmStatic
fun getInstance(context: Context): NightModeUtils =
INSTANCE ?: synchronized(this) {
INSTANCE ?: NightModeUtils(context).also {
INSTANCE = it
}
}
private const val SPF_THEME_MODE = "theme_mode"
private const val SP_THEME_NAME = "config_data"
private const val SPF_THEME_TIMER = "theme_timer"
}
/** 获取设置 */
private fun getSpfThemeMode(): Int =
mSpf.getInt(SPF_THEME_MODE, ThemeMode.MODE_FOLLOW_SYSTEM.intValue)
/** 设置 */
private fun setSpfThemeMode(mode: Int) {
/*
注意,当两个编辑器同时修改首选项时
*时间,最后一个调用commit的人获胜。
*
*/
mSpf.edit().putInt(SPF_THEME_MODE, mode).commit()
}
/** 获取时间设置 */
private fun getSpfThemeTimer(): String = mSpf.getString(SPF_THEME_TIMER, null) ?: "18:00~08:00"
/** 设置时间 */
private fun saveSpfThemeTimer(timer: String) {
mSpf.edit().putString(SPF_THEME_TIMER, timer).apply()
}
/** 获取设置好的ThemeMode */
fun getThemeMode(): ThemeMode = ThemeMode.parseOfInt(getSpfThemeMode())
/** 设置模式 */
fun setThemeMode(mode: ThemeMode) {
setSpfThemeMode(mode.intValue)
}
/** 获取设置好的定时时间 */
fun getThemeTime(): ThemeTime {
val timeStr = getSpfThemeTimer()
return ThemeTime(
beginHour = timeStr.split("~")[0].split(":")[0].toInt(),
beginMinute = timeStr.split("~")[0].split(":")[1].toInt(),
endHour = timeStr.split("~")[1].split(":")[0].toInt(),
endMinute = timeStr.split("~")[1].split(":")[1].toInt()
)
}
/** 设置定时时间 */
fun setThemeTime(time: ThemeTime) {
saveSpfThemeTimer(time.toTimerString())
}
/**
* 应用存储的设置
*/
fun applySetting() {
applySetting(null);
}
fun applySetting(context: Context?): Int {
var themeId = 0;
// 检查主题
val themeModeInt = getSpfThemeMode()
val themeMode = ThemeMode.parseOfInt(themeModeInt)
var disableActionBar: Boolean = false;
var actName: String = "";
var needActionBar:Int =-1;
if (context is BaseActivity) {
needActionBar = context.needActionBar()
if( needActionBar == 1) {
disableActionBar = false;
}else if( needActionBar == 0) {
disableActionBar = true;
}
}
actName = context?.javaClass?.simpleName + " ,是否禁用actionBar " + disableActionBar;
if (context is AppCompatActivity) {
if (needActionBar==-1) {
var act: AppCompatActivity = context;
val a: TypedArray = act.obtainStyledAttributes(R.styleable.AppCompatTheme)
val needActionBar = a.getBoolean(R.styleable.AppCompatTheme_windowActionBar, false)
disableActionBar = !needActionBar
// disableActionBar = if(act.supportActionBar!=null) true else true;//act.supportActionBar is WindowDecorActionBar
}
}
Log.w("ThemeStyle", "主题模式 ${themeMode.name} act $actName");
if (themeMode == ThemeMode.BLUE_THEME) {
if (disableActionBar) {
themeId = R.style.Theme_MyApplication_Blue_NoActionBar
} else {
themeId = R.style.Theme_MyApplication_Blue
}
} else if (themeMode == ThemeMode.YELLOW_THEME) {
if (disableActionBar) {
themeId = R.style.Theme_MyApplication_Yellow_NoActionBar
} else {
themeId = R.style.Theme_MyApplication_Yellow
}
} else if (themeMode == ThemeMode.PURPLE_THEME) {
if (disableActionBar) {
themeId = R.style.Theme_MyApplication_PURPLE_NoActionBar
} else {
themeId = R.style.Theme_MyApplication_PURPLE
}
} else if (themeMode == ThemeMode.GREEN_THEME) {
if (disableActionBar) {
themeId = R.style.Theme_MyApplication_GREEN_NoActionBar;
} else {
themeId = R.style.Theme_MyApplication_GREEN
}
} else if (themeMode == ThemeMode.GREY_THEME) {
if (disableActionBar) {
themeId = R.style.Theme_MyApplication_Grey_NoActionBar;
} else {
themeId = R.style.Theme_MyApplication_Grey
}
} else if (themeMode == ThemeMode.BROWN_THEME) {
if (disableActionBar) {
themeId = R.style.Theme_MyApplication_Brown_NoActionBar
} else {
themeId = R.style.Theme_MyApplication_Brown
}
} else if (themeMode == ThemeMode.DEEP_ORANGE_THEME) {
if (disableActionBar) {
themeId = R.style.Theme_MyApplication_DeepOrange_NoActionBar
} else {
themeId = R.style.Theme_MyApplication_DeepOrange
}
} else if (themeMode == ThemeMode.AMBER_THEME) {
if (disableActionBar) {
themeId = R.style.Theme_MyApplication_Amber_NoActionBar
} else {
themeId = R.style.Theme_MyApplication_Amber
}
} else if (themeMode == ThemeMode.CYAN_THEME) {
if (disableActionBar) {
themeId = R.style.Theme_MyApplication_Cyan_NoActionBar
} else {
themeId = R.style.Theme_MyApplication_Cyan
}
} else if (themeMode == ThemeMode.BLACK_THEME) {
if (disableActionBar) {
themeId = R.style.Theme_MyApplication_BLACK_NoActionBar;
} else {
themeId = R.style.Theme_MyApplication_BLACK;
}
} else {
if (disableActionBar) {//如果appcontext设置actionbar,act manifest.xml没有声明解除actionbar,然后调用setSupportBar则会抛出异常,因此需要处理适配。
themeId = R.style.Theme_MyApplication_DefaultBlue_NoActionBar
} else {
themeId = R.style.Theme_MyApplication_DefaultBlue
}
}
if (themeId != 0) {
if(context is Activity){
context?.setTheme(themeId)
}else{
// context?.theme?.applyStyle(themeId, true)
}
// return themeId;
}
AppCompatDelegate.setDefaultNightMode(
when (themeMode) {
ThemeMode.MODE_ALWAYS_ON -> AppCompatDelegate.MODE_NIGHT_YES
ThemeMode.MODE_ALWAYS_OFF -> AppCompatDelegate.MODE_NIGHT_NO
ThemeMode.MODE_FOLLOW_SYSTEM -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
else -> {
AppCompatDelegate.MODE_NIGHT_NO
}
}
)
return themeId
}
/**
* 定时开启
*/
private fun clock(): Int {
val time = getThemeTime()
val startTime = time.startTime.replace(":", ".").toFloat()
val stopTime = time.stopTime.replace(":", ".").toFloat()
val curTime = DateUtils.getFormatDate("HH.mm", Date()).toFloat()
return if (stopTime > startTime) {
// 结束时间和开始时间都在同一天
if (curTime > startTime && curTime < stopTime) {
AppCompatDelegate.MODE_NIGHT_YES
} else {
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
}
} else {
// 结束时间在开始时间的后一天
if (curTime > startTime || curTime < stopTime) {
AppCompatDelegate.MODE_NIGHT_YES
} else {
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
}
}
}
}
/**
* 主题设置时间
*/
data class ThemeTime(
// 开始小时
val beginHour: Int,
// 开始分钟
val beginMinute: Int,
// 结束小时
val endHour: Int,
// 结束分钟
val endMinute: Int,
// 开始时间
var startTime: String = "00:00",
// 结束时间
var stopTime: String = "00:00"
) : Serializable {
init {
startTime = "${upTo2String(beginHour)}:${upTo2String(beginMinute)}"
stopTime = "${upTo2String(endHour)}:${upTo2String(endMinute)}"
}
fun toTimerString(): String = "$startTime~$stopTime"
// 保存成长度为2的字符串
private fun upTo2String(value: Int): String {
val valueStr = "$value"
val sb = StringBuilder()
if (valueStr.length < 2) {
for (i in 0 until 2 - valueStr.length) {
sb.append("0")
}
}
sb.append(valueStr)
return sb.toString().substring(sb.length - 2)
}
}
设置代码
getBinding().rlMyThemeSetting.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
int currentMode = NightModeUtils.Companion.getSpfThemeMode(AppContext.getInstance());
ThemeMode[] values = ThemeMode.values();
ThemeColorSelectAdapter.Bean[] items = new ThemeColorSelectAdapter.Bean[values.length];
for (int i = 0; i < values.length; i++) {
String title = AppContext.getInstance().getString(values[i].getStringValueId());
ThemeColorSelectAdapter.Bean bean = new ThemeColorSelectAdapter.Bean();
bean.title = title;
bean.color = values[i].getColor();
items[i] = bean;
}
ThemeColorSelectAdapter arrayAdapter = new ThemeColorSelectAdapter(
getActivity(), R.layout.view_color_check_item, items);
builder.setSingleChoiceItems(arrayAdapter, currentMode, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (which != currentMode) {
dialog.dismiss();
ThemeMode value = values[which];
NightModeUtils.getInstance(AppContext.getInstance()).setThemeMode(value);
if (currentMode == 0 || which == 0) {
//call setTheme
NightModeUtils.getInstance(getContext()).applySetting(getContext());
//如果重启会发现部分没生效,深色模式的切换是不需要重启就能生效的,如果不先调用切换模式代码,调用recreate也是没用的。
} else {//setTheme没用
getActivity().recreate();
}
updateThemeStatusTextView();
}
}
});
builder.setTitle(AppContext.getStr(R.string.deep_color_theme_setting));
builder.show();
}
});
thememode
enum class ThemeMode(val intValue: Int, val stringValueId: Int,val color:Int) {
MODE_ALWAYS_ON(0, R.string.theme_mode_darkmode,R.color.black_no_dark),
MODE_ALWAYS_OFF(1, R.string.theme_mode_default_blue,R.color.themeColorNoDark),
MODE_FOLLOW_SYSTEM(2, R.string.theme_mode_dark_follow_system,R.color.themeColorNoDark),
BLUE_THEME(3, R.string.theme_blue,R.color.themeColorBlueNoDark),
PURPLE_THEME(4, R.string.theme_purple,R.color.themeColorPurpleNoDark),
GREEN_THEME(5, R.string.theme_green,R.color.themeColorGreenNoDark),
YELLOW_THEME(6, R.string.yellow,R.color.themeColorYellowNoDark),
BROWN_THEME(7, R.string.theme_brown,R.color.themeColorBrownNoDark),
GREY_THEME(8, R.string.theme_grey,R.color.themeColorGreyNoDark),
AMBER_THEME(9, R.string.theme_amber,R.color.themeColorAmberNoDark),
CYAN_THEME(10, R.string.theme_cyan,R.color.themeColorCyanNoDark),
DEEP_ORANGE_THEME(11, R.string.theme_deep_orange,R.color.themeColorDeepOrangeNoDark),
BLACK_THEME(12, R.string.theme_black,R.color.black_no_dark);
// MODE_TIMER(30, R.string.theme_mode_3);
companion object {
@JvmStatic
fun parseOfInt(intValue: Int) : ThemeMode {
return when(intValue) {
MODE_ALWAYS_ON.intValue -> MODE_ALWAYS_ON
MODE_ALWAYS_OFF.intValue -> MODE_ALWAYS_OFF
MODE_FOLLOW_SYSTEM.intValue -> MODE_FOLLOW_SYSTEM
// MODE_TIMER.intValue -> MODE_TIMER
BLUE_THEME.intValue -> BLUE_THEME
YELLOW_THEME.intValue -> YELLOW_THEME
PURPLE_THEME.intValue -> PURPLE_THEME
GREEN_THEME.intValue -> GREEN_THEME
BROWN_THEME.intValue -> BROWN_THEME
GREY_THEME.intValue -> GREY_THEME
CYAN_THEME.intValue -> CYAN_THEME
AMBER_THEME.intValue -> AMBER_THEME
DEEP_ORANGE_THEME.intValue -> DEEP_ORANGE_THEME
BLACK_THEME.intValue -> BLACK_THEME
// BLACK_THEME.intValue,!in 0.. 30-> BLACK_THEME
else -> MODE_FOLLOW_SYSTEM
}
}
}
}
最后的总结就是 更换主题 需要重启然后调用setTheme,但是更换深色模式则需要调用 AppCompatDelegate.setDefaultNightMode不能重启.
配色参考
http://mcg.mbitson.com/#!?mcgpalette0=%235768fe
https://www.materialpalette.com/light-blue/blue-grey
https://codecrafted.net/randommaterial