先上酷炫的运行效果
日夜间模式在app上面的使用场景还是很多的,特别是有关阅读类的app,不同的光线条件下让用户选择不同的主题模式那是必须得。其实实现这样的需求也很简单,下面就来看看我的方法吧。
实现前的分析
不同的模式下 View或activity需要设置不同的resource(这里resource可以是color、mipmap、style等资源),那么怎么让resource不同呢? 只需要让resource根据不同的模式设置不同的名称,然后根据不同的名称获取到不同的resourceId就可以了
具体示例
HomeActivity的代码如下:
package com.example.idea.daynightmodechange;
import android.app.Activity;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.example.idea.daynightmodechange.config.Config;
import com.example.idea.daynightmodechange.theme.IThemeable;
import com.example.idea.daynightmodechange.theme.ThemeManager;
import com.example.idea.daynightmodechange.theme.ThemeUtils;
import butterknife.BindView;
import butterknife.ButterKnife;
public class HomeActivity extends Activity implements IThemeable {
@BindView(R.id.ll_container)
LinearLayout ll_container;
@BindView(R.id.tv_text1)
TextView tv_text1;
@BindView(R.id.tv_text2)
TextView tv_text2;
@BindView(R.id.iv_image)
ImageView iv_image;
@BindView(R.id.btn_change_mode)
Button btn_change_mode;
@BindView(R.id.btn_jump)
Button btn_jump;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getThemeManager().applyTheme(HomeActivity.this, R.style.AppTheme);
setContentView(R.layout.act_home);
ButterKnife.bind(this);
initView();
}
private void initView() {
btn_change_mode.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (Config.currentTheme == ThemeManager.THEME.DAY) {
getThemeManager().changeTheme(ThemeManager.THEME.NIGHT);
} else {
getThemeManager().changeTheme(ThemeManager.THEME.DAY);
}
applyTheme();
}
});
btn_jump.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(new Intent(HomeActivity.this,TestStyleActivity.class));
}
});
}
@Override
public void applyTheme() {
getThemeManager().applyBackgroundColor(ll_container, R.color.color_home_bg);
getThemeManager().applyTextColor(tv_text1, R.color.color_text);
getThemeManager().applyImageResource(iv_image, R.drawable.shape_iv_bg);
getThemeManager().applyBackgroundDrawable(btn_jump, R.drawable.selector_text_bg);
getThemeManager().applyBackgroundDrawable(btn_change_mode, R.drawable.selector_text_bg);
}
@Override
public ThemeManager getThemeManager() {
return ThemeManager.getInstance();
}
@Override
public boolean isThemeEnable() {
return true;
}
}
对应的布局文件:
用一个枚举类型来表示你需要切换的模式,这里我只使用了日间模式和夜间模式这两个。
public static enum THEME {
DAY, NIGHT("_night"); //你������可以添加多种模式在这里,不同的模式应该具有不同的后缀
String resPrefix;
private THEME() {
resPrefix = "";
}
private THEME(String prefix) {
resPrefix = prefix;
}
/**
* 资源名称
* @param name
* @return
*/
public String formatResName(String name) {
return name + resPrefix;
}
public boolean hasResPrefix() {
return !TextUtils.isEmpty(resPrefix);
}
}
这里通过切换模式后ll_container的背景颜色的改变来分析
a.我在colors资源里,为ll_container准备了两份颜色值,这里一定要在
"color_home_bg"后面加上"_night",与枚举相对应,为了后面通过资源Id和枚举类型获取对应的资源Id。
#e5e5e5
#80444444
b.当我要切换模式的时候,针对ll_container这个view调用
getThemeManager().applyBackgroundColor(ll_container, R.color.color_home_bg);
c.我们来看一下它具体的方法实现
public ThemeManager applyBackgroundColor(Context context, View view,
int backgroundResourceId) {
view.setBackgroundResource(ThemeUtils.processColorResId(context,
this.theme, backgroundResourceId));
return this;
}
/**
*
* @param view 要设置的view
* @param backgroundResourceId 资源id
* @return
*/
public ThemeManager applyBackgroundColor(View view, int backgroundResourceId) {
return applyBackgroundColor(view.getContext(), view,
backgroundResourceId);
}
d.ThemeUtils.processColorResId(context,this.theme, backgroundResourceId)这个方法的作用是根据当前资源id和theme获取到对应的资源id
public static int processColorResId(Context context,
ThemeManager.THEME theme, int resourceId) {
return processThemeResId(context, theme, "color", resourceId);
}
/**
*
* @param context
* @param theme 当前主题
* @param defType 对应的资源类别
* @param resourceId
* @return
*/
private static int processThemeResId(Context context,
ThemeManager.THEME theme, String defType, int resourceId) {
if (theme.hasResPrefix() && isThemeEnable(context)) {
String strThemeResName = processThemeResName(theme, context
.getResources().getResourceName(resourceId));
return context.getResources().getIdentifier(strThemeResName,
defType, context.getPackageName());
} else {
return resourceId;
}
}
这样就完成了ll_contianer的背景色切换,是不是很easy.
切换别的view的资源与这个类似,这里就不一一列举了。
需要注意的地方
当我们app中使用沉浸式状态栏实现时候,一定希望状态栏和标题栏能在进行模式切换的时候改变样式,但是状态栏和标题栏的样式的切换只能在Activity的setContentView()方法之前执行,上面的方法就不能用来切换状态栏和标题栏的样式了。
想要切换状态栏和标题栏的样式,可以用recreate()方法来重启Activity来实现,Activity重启会有闪烁的问题可以通过动画来过渡。
后续
官方在Android Support Library 23.2开始支持了夜间模式切换,有关23.2的夜间模式切换,请戳这里【还没写】。
源码地址 https://github.com/idea007/DayNightModeChange