很多應用都提供設置主題的功能, 這裏介紹一種設置主題的方法. 這種方法通過設置一個基類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