很多应用都提供设置主题的功能, 这里介绍一种设置主题的方法. 这种方法通过设置一个基类Activity来管理主题切换的功能, 然后所有的activity继承它, 并实现其中切换主题的接口, 那么所有的Activity也就具有了主题切换的功能. 效果如下:
1.首先创建一个主题设置的工具类Theme, 这里为了简单用了数字来标识主题类型, 实际中可以将主题类型用SharedPreferences来保存.
public class Theme {
private int pos;
private Context context;
private static Theme mTheme;
static Theme getInstance(Context context) {
if (mTheme == null) {
synchronized (Theme.class) {
if (mTheme == null)
mTheme = new Theme(context);
}
}
return mTheme;
}
private Theme(Context context) {
this.context = context;
this.pos = 1;
}
public void updateTheme(int pos) {
this.pos = pos;
}
private int getColor(@ColorRes int color) {
return ContextCompat.getColor(context, color);
}
public int getBackgroundColor(){
switch (pos){
case 2:
return getColor(R.color.md_black_1000);
case 1:
default:
return getColor(R.color.md_light_1000);
}
}
public int getTextColor() {
switch (pos){
case 2:
return getColor(R.color.md_light_1000);
case 1:
default:
return getColor(R.color.md_grey_800);
}
}
}
2.创建接口Themeable, 来实现所有自定义View具备换主题的功能, 接口很简单, 只有一个方法
public interface Themeable {
void resetTheme(Theme theme);
}
3.建立一个ThemeActivity, 让所有的Activity继承它, 这样所有的Activity就有了设置主题的功能.
ThemeActivity的代码如下:
public class ThemedActivity extends AppCompatActivity {
private Theme mTheme;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mTheme = Theme.getInstance(getApplicationContext());
}
@Override
protected void onResume() {
super.onResume();
updateUiElements(); //在onRsume中调用updateUiElements这样保证继承自
// ThemeActivity的activity都能自动切换主题
}
//更新主题类型
public void setBaseTheme(int pos) {
mTheme.updateTheme(pos);
}
public void updateUiElements() {
List<View> viewList = getAllChildren(findViewById(android.R.id.content));
for (View view : viewList) {
if (view instanceof Themeable) //这里确保所有继承自Themeable的View能自动切换主题
((Themeable) view).resetTheme(mTheme);
}
}
private List<View> getAllChildren(View target) {
if (!(target instanceof ViewGroup))
return Collections.singletonList(target);
ArrayList<View> allChildren = new ArrayList<>();
ViewGroup viewGroup = (ViewGroup) target;
for (int i = 0; i < viewGroup.getChildCount(); i++) {
View child = viewGroup.getChildAt(i);
ArrayList<View> targetsChildren = new ArrayList<>();
targetsChildren.add(target);
targetsChildren.addAll(getAllChildren(child));
allChildren.addAll(targetsChildren);
}
return allChildren;
}
public int getTextColor() {
return mTheme.getTextColor();
}
public int getBackgroundColor() {
return mTheme.getBackgroundColor();
}
}
4.这里为了测试提供一个MainActivity与SettingActivity, 都继承与ThemeActivity, 实现updateUiElements接口, 不同Activity根据主题的类型在此接口中进行设置, 这样就实现不同的Activity实现主题切换, 代码如下:
public class MainActivity extends ThemedActivity {
private RelativeLayout mRelativeLayout;
private TextView mTextView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRelativeLayout = (RelativeLayout) findViewById(R.id.activity_main);
mTextView = (TextView) findViewById(R.id.main_text_view);
mTextView.setText(
"人生若只如初见,何事秋风悲画扇。\n" +
"等闲变却故人心,却道故人心易变。\n" +
"骊山语罢清宵半,泪雨零铃终不怨。\n" +
"何如薄幸锦衣郎,比翼连枝当日愿。");
Button btnTheme = (Button) findViewById(R.id.btn_setting_theme);
btnTheme.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, SettingActivity.class);
startActivity(intent);
}
});
}
@Override
public void updateUiElements() {
super.updateUiElements();
//根据主题类型设置背景与字体颜色
mRelativeLayout.setBackgroundColor(getBackgroundColor());
mTextView.setTextColor(getTextColor());
}
}
public class SettingActivity extends ThemedActivity {
private RelativeLayout mRelativeLayout;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_setting);
mRelativeLayout = (RelativeLayout) findViewById(R.id.activity_setting);
SettingView view1 = (SettingView) findViewById(R.id.theme_setting_view_1);
view1.setTitleViewText("主题1");
view1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//切换到主题1
setBaseTheme(1);
updateUiElements();
}
});
SettingView view2 = (SettingView) findViewById(R.id.theme_setting_view_2);
view2.setTitleViewText("主题2");
view2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//切换到主题2
setBaseTheme(2);
updateUiElements();
}
});
}
@Override
public void updateUiElements() {
super.updateUiElements();
//根据主题类型设置背景
mRelativeLayout.setBackgroundColor(getBackgroundColor());
}
}
在SettingActivity中为了展示Themeable, 添加自定义SettingView来设置主题样式. SettingView实现Themeable接口, 代码如下:
public class SettingView extends FrameLayout implements Themeable {
public SettingView(Context context) {
this(context, null);
}
public SettingView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public SettingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
LayoutInflater inflater = LayoutInflater.from(getContext());
inflater.inflate(R.layout.setting_view, this);
}
public void setTitleViewText(CharSequence text) {
((TextView)findViewById(R.id.title)).setText(text);
}
@Override
public void resetTheme(Theme theme) {
//根据主题类型设置自定义view的样式
findViewById(R.id.ll_setting_view).setBackgroundColor(theme.getBackgroundColor());
((TextView)findViewById(R.id.title)).setTextColor(theme.getTextColor());
((TextView)findViewById(R.id.caption)).setTextColor(theme.getTextColor());
}
}
在ThemeActivity的updateUiElements接口中会去遍历所有继承自Themeable的view, 调用其resetTheme接口, 这样SettingView就能根据选择的主题类型进行切换.
示例代码可以从这里获得切换主题示例Demo