web-dev-qa-db-ja.com

テーマの色を動的に設定する

Androidアプリで、次のようにテーマを(動的に)使用しています:

my_layout.xml(抽出):

<TextView
    Android:id="@+id/myItem"
    style="?my_item_style" />

attrs.xml(抽出):

<attr name="my_item_style" format="reference" />

themes.xml(抽出):

<style name="MainTheme.Blue">
      <item name="my_item_style">@style/my_item_style_blue</item>
</style>

<style name="MainTheme.Green">
      <item name="my_item_style">@style/my_item_style_green<item>
</style>

styles.xml(抽出):

<style name="my_item_style_blue">
      <item name="Android:textColor">@color/my_blue</item>
</style>

<style name="my_item_style_green">
      <item name="Android:textColor">@color/my_blue</item>
</style>

ご覧のとおり、テーマを動的に設定しています。私はこのクラスを使用しています:

public class ThemeUtils {

  private static int sTheme;
  public final static int THEME_BLUE = 1;
  public final static int THEME_GREEN = 2;

  public static void changeToTheme(MainActivity activity, int theme) {
      sTheme = theme;
      activity.startActivity(new Intent(activity, MyActivity.class));
  }

  public static void onActivityCreateSetTheme(Activity activity)
  {
      switch (sTheme)
      {
          default:
          case THEME_DEFAULT:
          case THEME_BLUE:
              activity.setTheme(R.style.MainTheme_Blue);
              break;
          case THEME_GREEN:
              activity.setTheme(R.style.MainTheme_Green);
              break;
      }
  }

}

知りたいのですが、コードでこれを行う方法(テーマの色を変更)はありますか?たとえば、次のコード(抽出)があります。

((TextView) findViewById(R.id.myItem)).setTextColor(R.color.blue);

これは、使用可能なテーマに対してswitchコマンドを使用し、テーマに対して正しい色を返すヘルパーメソッドによって実行できます。しかし、私はいくつかのより良い、より良い、より速い方法があるかどうか知りたいのですが。

ありがとう!

9
Tom11

私はついに次の方法を使用してそれを行いました:

public static int getColor(String colorName) {
    Context ctx = getContext();
    switch (sTheme) {
        default:
        case THEME_DEFAULT:
            return ctx.getResources().getIdentifier("BLUE_" + colorName, "color", ctx.getPackageName());
        case THEME_BLUE:
            return ctx.getResources().getIdentifier("BLUE_" + colorName, "color", ctx.getPackageName());
        case THEME_GREEN:
            return ctx.getResources().getIdentifier("GREEN_" + colorName, "color", ctx.getPackageName());
    }
}

これは私のテーマに応じて色を返します(私は接頭辞を使用しました)。

4
Tom11

私が正確に理解しているなら、あなたはする方法を探しています

  1. テーマからスタイルを抽出し、
  2. 上記のスタイルから値(テキストの色)を抽出します。

それに取り掛かろう。

// Extract ?my_item_style from a context/activity.
final TypedArray a = context.obtainStyledAttributes(new int[] { R.attr.my_item_style });
@StyleRes final int styleResId = a.getResourceId(0, 0);
a.recycle();

// Extract values from ?my_item_style.
final TypedArray b = context.obtainStyledAttributes(styleResId, new int[] { Android.R.attr.textColor });
final ColorStateList textColors = b.getColorStateList(0);
b.recycle();

// Apply extracted values.
if (textColors != null) {
    textView.setTextColor(textColors);
}

いくつかの注意事項:

  1. TypedArrayは、古いAPIレベルのカラー状態リストでサポートベクタードローアブルとテーマ参照の取得をサポートしていません。 AppCompat内部APIを使用する場合は、TintTypedArrayを試してください。
  2. 常にint[]を割り当てるとコストがかかるため、static finalにします。
  3. 複数の属性を一度に解決したい場合は、属性の配列をソートする必要があります!それ以外の場合はクラッシュすることがあります。 <declare-styleable>は、このような配列と対応するインデックスを生成します。
3
Eugen Pechanec

これを確認しました MultipleThemeMaterialDesign デモ?

enter image description here

SettingActivity:

@Override
protected void onCreate(Bundle savedInstanceState) {
    Preferences.applyTheme(this);
    getDelegate().installViewFactory();
    getDelegate().onCreate(savedInstanceState);
    super.onCreate(savedInstanceState);
    setToolbar();
    addPreferencesFromResource(R.xml.preferences);
    Preferences.sync(getPreferenceManager());
    mListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
        @Override
        public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
            Preferences.sync(getPreferenceManager(), key);
            if (key.equals(getString(R.string.pref_theme))) {
                finish();
                final Intent intent = IntentCompat.makeMainActivity(new ComponentName(
                        SettingsActivity.this, MainActivity.class));
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | IntentCompat.FLAG_ACTIVITY_CLEAR_TASK);
                startActivity(intent);
            }
        }
    };
}

デモの完全な例を参照してください。

1
Pratik Butani

すべてのリソースがRクラスのフィールドであるという事実を考えると、リフレクションを使用してそれらを探すことができます。これはコストがかかりますが、int値を取得するため、取得後にそれらを格納してパフォーマンスの低下を回避できます。また、リソースを使用するメソッドは任意のintを使用するため、int変数をプレースホルダーとして使用し、目的の色をその中に入れることができます。

リソースを取得するため:

String awesomeColor = "blue";
int color = getResourceId(R.color, awesomeColor, false);
if(blue>0) ((TextView) findViewById(R.id.myItem)).setTextColor(color);

関数:

public static int getResourceId(Class rClass, String resourceText, boolean showExceptions){

        String key = rClass.getName()+"-"+resourceText;

        if(FailedResourceMap.containsKey(key)) return 0;
        if(ResourceMap.containsKey(key)) return ResourceMap.get(rClass.getName()+"-"+resourceText);

        try {

            String originalText = resourceText;
            if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.Gingerbread){
                resourceText = ValidationFunctions.normalizeText(resourceText);
            }
            resourceText = resourceText.replace("?", "").replace("  ", " ").replace(" ", "_").replace("(", "").replace(")", "");

            int resource = rClass.getDeclaredField(resourceText).getInt(null);
            ResourceMap.put(rClass.getName()+"-"+originalText, resource);

            return resource;
        } catch (IllegalAccessException | NullPointerException e) {
            FailedResourceMap.put(key, 0);
            if(showExceptions) e.printStackTrace();
        } catch (NoSuchFieldException e) {
            FailedResourceMap.put(key, 0);
            if(showExceptions) e.printStackTrace();
        }

        return 0;
    }

ここで作業バージョン: https://github.com/fcopardo/AndroidFunctions/blob/master/src/main/Java/com/grizzly/functions/TextFunctions.Java

この処理は、すべてのAndroidリソースに有効です。中間変数を使用する代わりに、この方法でテーマを設定することもできます。

public static void onActivityCreateSetTheme(Activity activity)
  {
    int theme = getResourceId(R.style, activity.getClass().getSimpleName(), false);
    if(theme > 0) activity.setTheme(theme);
  }
0
Fco P.

Intentを介してテーマIDを渡すのはどうですか?

Intent intent = new Intent(activity, MyActivity.class);
intent.putExtra("theme", R.style.MainTheme_Green);
activity.startActivity(intent);

そして、onCreate

// assuming that MainTheme_Blue is default theme
setTheme(getIntent().getIntExtra("theme", R.style.MainTheme_Blue));
0
Oleksii K.