web-dev-qa-db-ja.com

テーマのユーザー選択の実装

ユーザーにいくつかの異なるテーマから選択してもらいたいのですが、これが問題のない方法であるかどうか疑問に思いました。私はこの方法で少しテストをして、それはうまくいきましたが、もっと良い方法があるかもしれないし、後でいくつかの問題を引き起こすかもしれないと思うので、尋ねたいと思いました。

テーマごとに異なるレイアウトを作成することを考えていました。onCreateでは、setContentView()メソッドのスイッチを用意するだけでした。保存したSharedPreference値(整数)を最初にロードし、その値に応じて、対応するレイアウトを表示します。明らかに、ユーザーはボタンなどでSharedPreference値を変更できます。

これらのレイアウトは基本的に同じですが色が異なるため、各レイアウトファイルのTextViewsと他のビューに同じIDを使用したいと思います。私の主な質問は、これが問題を引き起こすでしょうか?

コードのないテキストの壁でごめんなさい。この状況でのグッドプラクティスの概要を知りたいと思います。前もって感謝します。

33
Matt Harris

私は実際にアプリケーションにこの機能を持っており、さらに、ユーザーが実行時にテーマを変更できるようにしています。設定から値を読み取るには時間がかかるため、キャッシュされた値を保持するグローバルにアクセス可能な関数を介してテーマIDを取得しています。

すでに指摘したように、 このガイド を使用していくつかのAndroidテーマを作成します。<style>ファイルには少なくとも2つのstyles.xmlアイテムがあります。 例えば:

<style name="Theme.App.Light" parent="@style/Theme.Light">...</style>
<style name="Theme.App.Dark" parent="@style/Theme">...</style>

ここで、これらのスタイルの1つをアクティビティに適用する必要があります。これは、他の呼び出しの前に、activitieのonCreateメソッドで実行しています。

setTheme(MyApplication.getThemeId());

getThemeIdは、キャッシュされたテーマIDを返すメソッドです。

public static int getThemeId()
{
    return themeId;
}

このフィールドは別の方法で更新されています:

public static void reloadTheme()
{
    themeSetting = PreferenceManager.getDefaultSharedPreferences(context).getString("defaultTheme", "0");
    if(themeSetting.equals("0"))
        themeId = R.style.Theme_Light;
    else
        themeId = R.style.Theme_Dark;
}

これは、設定が変更されるたびに(そしてもちろん起動時に)呼び出されます。これらの2つのメソッドは、MyApplicationを拡張するApplicationクラスにあります。プリファレンス変更リスナーは、この投稿の最後に説明されており、メインアクティビティクラスにあります。

最後の非常に重要なこと-アクティビティが開始されるときにテーマが適用されます。テーマは設定画面でのみ変更でき、そこに到達する方法は1つしかないと仮定します。つまり、1つの(メイン)アクティビティからのみ、設定画面を終了してもこのアクティビティは再開されません。古いテーマは引き続き表示されます。中古。これを修正します(メインアクティビティを再開します)。

@Override
protected void onResume() {
    super.onResume();
    if(schduledRestart)
    {
        schduledRestart = false;
        Intent i = getBaseContext().getPackageManager().getLaunchIntentForPackage( getBaseContext().getPackageName() );
        i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        startActivity(i);
    }
}

scheduledRestartはブール変数で、最初はfalseに設定されています。このリスナーによってテーマが変更されると、trueに設定されます。これにより、前述のキャッシュされたテーマIDも更新されます。

private class themeListener implements OnSharedPreferenceChangeListener{

    @Override
    public void onSharedPreferenceChanged(SharedPreferences spref, String key) {
        if(key.equals("defaultTheme") && !spref.getString(key, "0").equals(MyApplication.getThemeSetting()))
        {
            MyApplication.reloadTheme();
            schduledRestart = true;
        }
    }


sp = PreferenceManager.getDefaultSharedPreferences(this);

listener = new themeListener();
sp.registerOnSharedPreferenceChangeListener(listener);

リスナーオブジェクトへの参照を保持することを忘れないでください。そうしないと、ガベージコレットされます(そして機能しなくなります)。

45
user1234567

以下を使用して、テーマを動的に変更することもできます。

ContextThemeWrapper w = new ContextThemeWrapper(this, <newTHEMEId>);
getTheme().setTo(w.getTheme());

各アクティビティのonCreateの前。

3
mspapant

このようにすれば機能し、問題はないと思いますが、面倒なようです(すべてのレイアウトに追加したいすべてのテーマを掛ける必要があります。後でレイアウト内のリソースを変更したい場合は、すべてのテーマでリソースを変更する必要があります。間違いなく1つを忘れる必要があります)

Androidの スタイルとテーマ 機能を使用しないのはなぜですか?

それらはアクティビティ全体に簡単に適用できます。

<activity Android:theme="@style/my_theme">

そのため、使用するSharedPreferences値(設定アクティビティのボタンなど)の変更を検出すると、スタイルを切り替えることができます。または、実行時に(アクティビティの作成時に)プリファレンス値を読み取るようにスタイルを設定し、それに応じて正しいスタイル/テーマを適用することもできます。

2
Guillaume