Android 設置主題的方法

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章