【问题标题】:Set theme color dynamically动态设置主题颜色
【发布时间】:2017-08-12 20:20:39
【问题描述】:

我在我的 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 命令获取可用主题并为主题返回正确的颜色。但我想知道是否有更好、更好、更快的方法。

谢谢!

【问题讨论】:

  • 你从哪里动态设置主题?你也可以分享那个代码吗?
  • @azizbekian 我添加了 ThemeUtils 类,但我认为它与我的问题无关。

标签: android android-activity colors themes


【解决方案1】:

我终于用下面的方法完成了:

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());
    }
}

这会根据我的主题返回颜色(我使用了前缀)。

【讨论】:

    【解决方案2】:

    如果我理解正确,您正在寻找一种方法

    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. 如果您想一次解析多个属性,则必须对属性数组进行排序!否则有时会崩溃。 &lt;declare-styleable&gt; 为你生成这样的数组和对应的索引。

    【讨论】:

      【解决方案3】:

      你看过这个MultipleThemeMaterialDesign 演示吗?

      设置活动:

      @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);
                  }
              }
          };
      }
      

      查看完整的演示示例。

      【讨论】:

        【解决方案4】:

        通过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));
        

        【讨论】:

          【解决方案5】:

          鉴于每个资源都是 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

          这种处理对任何安卓资源都有效。您也可以通过这种方式设置主题,而不是使用中间变量:

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

          【讨论】:

            猜你喜欢
            • 2016-11-11
            • 2012-07-31
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-04-08
            • 2012-03-17
            • 1970-01-01
            相关资源
            最近更新 更多