如何在Android Lollipop中以编程方式更改原色和强调色?


156

首先,这个问题提出了一个非常相似的问题。但是,我的问题有细微的差别。

我想知道的是,是否可以通过编程colorPrimary方式将主题的属性更改为任意颜色?

因此,例如,我们有:

<style name="AppTheme" parent="android:Theme.Material.Light">
    <item name="android:colorPrimary">#ff0000</item>
    <item name="android:colorAccent">#ff0000</item>
</style>

在运行时,用户决定要#ccffff用作原色。当然,我无法为所有可能的颜色创建主题。

我不介意我是否必须做一些骇人听闻的事情,例如依靠Android的私有内部组件,只要它可以使用公共SDK就能工作。

我的目标是最终使ActionBar 所有小部件都使用CheckBox来使用这种原色。


1
您不知道什么是不存在的Android版本的“ Android私有内部”。不要假定L的“私有内部”与L在生产版本中变成的东西相同。
CommonsWare 2014年

不,您不能将任意数据放入主题。就是说,colorPrimary仅用于操作栏背景,最近栏颜色和通知颜色,您可以动态更改所有这些。
alanv 2014年

2
我认为您应该问“如何在运行时更改样式属性”,从我看来,答案是您不能。但是,我有一个想法可能会对您有所帮助。使用自定义ContextWrapper并提供自己的资源。看一下这个:github.com/negusoft/holoaccent/blob/master/HoloAccent/src/com/…总体来说,这个项目可能会给你一个思路。
Mikooos 2014年

1
只是在这里感到困惑,但是所有XML都被转换为.dex文件,这些文件作为Java对象正确地加载到您的android应用程序中。这是否意味着我们应该能够从代码创建和设置整个主题,以及从尚待编写的工厂生成主题?听起来很麻烦。
2014年

1
@NiekHaarman你有没有想办法?
gbhall 2015年

Answers:


187

主题是不可变的,您不能。


8
谢谢,克里斯!不是我一直在寻找的答案,但我想我必须忍受它了:)
nhaarman 2014年

5
@Chris Banes,您好,但是联系人应用如何根据联系人的主题颜色更改状态栏颜色和工具栏颜色?如果可能的话,我认为Niek Haarman的需求不是太远,因为他只需要存储用户想要的颜色代码即可。您能再解释一下吗?我也想创建类似这样的东西,但真的很困惑。
天鹅2014年

39
可以通过Window.setStatusBarColor()动态更改状态栏颜色。
克里斯·巴内斯

9
是否可以通过编程创建主题​​?
安德鲁·奥罗巴托

3
在动态更改状态栏颜色的同时,您还可以通过Window.setNavigationBarColor()-API 21 :)
user802421

65

我阅读了有关联系人应用程序的评论,以及它如何为每个联系人使用主题。

通讯录应用可能有一些预定义的主题(来自此处的每种材料原色:http : //www.google.com/design/spec/style/color.html)。

您可以在onCreate方法中的setContentView方法之前应用主题。

然后,联系人应用程序可以将主题随机应用于每个用户。

该方法是:

setTheme(R.style.MyRandomTheme);

但是此方法有一个问题,例如它可以更改工具栏颜色,滚动效果颜色,波纹颜色等,但不能更改状态栏颜色和导航栏颜色(如果您也要更改)。

然后,要解决此问题,可以在和之前使用方法:

if (Build.VERSION.SDK_INT >= 21) {
        getWindow().setNavigationBarColor(getResources().getColor(R.color.md_red_500));
        getWindow().setStatusBarColor(getResources().getColor(R.color.md_red_700));
    }

这两种方法可以更改导航和状态栏的颜色。请记住,如果您将导航栏设置为半透明,则无法更改其颜色。

这应该是最终代码:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setTheme(R.style.MyRandomTheme);
    if (Build.VERSION.SDK_INT >= 21) {
        getWindow().setNavigationBarColor(getResources().getColor(R.color.myrandomcolor1));
        getWindow().setStatusBarColor(getResources().getColor(R.color.myrandomcolor2));
    }
    setContentView(R.layout.activity_main);

}

您可以使用开关并生成随机数以使用随机主题,或者像在联系人应用程序中一样,每个联系人可能都具有关联的预定义号码。

主题示例:

<style name="MyRandomTheme" parent="Theme.AppCompat.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/myrandomcolor1</item>
    <item name="colorPrimaryDark">@color/myrandomcolor2</item>
    <item name="android:navigationBarColor">@color/myrandomcolor1</item>
</style>

对不起我的英语不好。


1
谢谢您的回答。不幸的是,我的要求是使用任意颜色。当然,为所有颜色创建主题都是不可行的。
nhaarman

1
@DanielGomezRico-AFAIK,只要您尽早完成设置,无论初始设置在哪里,都可以覆盖预设主题。如回答所说:“您可以在onCreate方法中的setContentView方法之前应用主题。”
制造商史蒂夫,

50

您可以使用Theme.applyStyle在运行时通过对主题应用另一种样式来对其进行修改。

假设您有以下样式定义:

<style name="DefaultTheme" parent="Theme.AppCompat.Light">
    <item name="colorPrimary">@color/md_lime_500</item>
    <item name="colorPrimaryDark">@color/md_lime_700</item>
    <item name="colorAccent">@color/md_amber_A400</item>
</style>

<style name="OverlayPrimaryColorRed">
    <item name="colorPrimary">@color/md_red_500</item>
    <item name="colorPrimaryDark">@color/md_red_700</item>
</style>

<style name="OverlayPrimaryColorGreen">
    <item name="colorPrimary">@color/md_green_500</item>
    <item name="colorPrimaryDark">@color/md_green_700</item>
</style>

<style name="OverlayPrimaryColorBlue">
    <item name="colorPrimary">@color/md_blue_500</item>
    <item name="colorPrimaryDark">@color/md_blue_700</item>
</style>

现在,您可以像这样在运行时修补主题:

getTheme().applyStyle(R.style.OverlayPrimaryColorGreen, true);

applyStyle必须在布局膨胀之前调用该方法!因此,除非您手动加载视图,否则应在调用setContentView活动之前将样式应用于主题。

当然,这不能用于指定任意颜色,即1600万(256 3)种颜色中的一种。但是,如果您编写了一个小程序来为您生成样式定义和Java代码,则应该是512(8 3)中的一个。

有趣的是,您可以对主题的不同方面使用不同的样式叠加层。例如,仅添加一些覆盖定义colorAccent。现在,您几乎可以任意组合原色和强调色的不同值。

您应该确保叠加主题定义不会意外地从父样式定义继承一堆样式定义。例如,称为AppTheme.OverlayRed样式的样式隐式继承了中定义的所有样式,AppTheme并且在修补主主题时也会应用所有这些定义。因此,要么避免覆盖主题名称中出现圆点,要么使用,Overlay.Red然后将其定义Overlay为空样式。


11
致电applyStyle之前先尝试致电setContentView,它应该可以工作。
devconsole

1
是的,那么它可能会起作用!我正在寻找一种方法来更改niveau片段而不活动中的色度!这就是为什么它对我不起作用的原因:<我的用例是,当我从选项卡切换时,我想为FAB或选项卡指示器使用不同的颜色,这将开始一个不同的片段!
丹尼斯·安德森

谢谢,这帮了很多忙!但是,我无法多次执行。(一个用于colorPrimary,一个用于colorAccent,等等)。你可以帮帮我吗?谢谢。stackoverflow.com/questions/41779821/...
托马斯·沃斯

1
这就是我想使用第二个帐户再次+1的答案。谢谢。
贝努瓦Duffez

非常感谢您,这就是我想要的...。我希望我可以使用这种方法来更改当前主题的口音颜色。
纳西卜

32

我创建了一些解决方案来制作任何颜色的主题,也许这对某些人可能有用。API 9以上

1.首先创建“ res / values-v9 / ”,然后放置以下文件:styles.xml 和常规的“ res / values”文件夹将与您的样式一起使用。

2.将此代码放入您的res / values / styles.xml中:

<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light">
        <item name="colorPrimary">#000</item>
        <item name="colorPrimaryDark">#000</item>
        <item name="colorAccent">#000</item>
        <item name="android:windowAnimationStyle">@style/WindowAnimationTransition</item>
    </style>

    <style name="AppThemeDarkActionBar" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="colorPrimary">#000</item>
        <item name="colorPrimaryDark">#000</item>
        <item name="colorAccent">#000</item>
        <item name="android:windowAnimationStyle">@style/WindowAnimationTransition</item>
    </style>

    <style name="WindowAnimationTransition">
        <item name="android:windowEnterAnimation">@android:anim/fade_in</item>
        <item name="android:windowExitAnimation">@android:anim/fade_out</item>
    </style>
</resources>

3.进入AndroidManifest:

<application android:theme="@style/AppThemeDarkActionBar">

4.创建一个名为“ ThemeColors.java”的新类

public class ThemeColors {

    private static final String NAME = "ThemeColors", KEY = "color";

    @ColorInt
    public int color;

    public ThemeColors(Context context) {
        SharedPreferences sharedPreferences = context.getSharedPreferences(NAME, Context.MODE_PRIVATE);
        String stringColor = sharedPreferences.getString(KEY, "004bff");
        color = Color.parseColor("#" + stringColor);

        if (isLightActionBar()) context.setTheme(R.style.AppTheme);
        context.setTheme(context.getResources().getIdentifier("T_" + stringColor, "style", context.getPackageName()));
    }

    public static void setNewThemeColor(Activity activity, int red, int green, int blue) {
        int colorStep = 15;
        red = Math.round(red / colorStep) * colorStep;
        green = Math.round(green / colorStep) * colorStep;
        blue = Math.round(blue / colorStep) * colorStep;

        String stringColor = Integer.toHexString(Color.rgb(red, green, blue)).substring(2);
        SharedPreferences.Editor editor = activity.getSharedPreferences(NAME, Context.MODE_PRIVATE).edit();
        editor.putString(KEY, stringColor);
        editor.apply();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) activity.recreate();
        else {
            Intent i = activity.getPackageManager().getLaunchIntentForPackage(activity.getPackageName());
            i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            activity.startActivity(i);
        }
    }

    private boolean isLightActionBar() {// Checking if title text color will be black
        int rgb = (Color.red(color) + Color.green(color) + Color.blue(color)) / 3;
        return rgb > 210;
    }
}

5. MainActivity:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        new ThemeColors(this);
        setContentView(R.layout.activity_main);
    }

    public void buttonClick(View view){
        int red= new Random().nextInt(255);
        int green= new Random().nextInt(255);
        int blue= new Random().nextInt(255);
        ThemeColors.setNewThemeColor(MainActivity.this, red, green, blue);
    }
}

要更改颜色,只需将RGB替换为Random,希望对您有所帮助。

在此处输入图片说明

有一个完整的示例:ColorTest.zip


你可以像分享项目吗?
Erselan Khan

创建新项目,下载文件-“ styles.xml”,并使用上面的代码。祝好运。
IQ.feature

1
context.setTheme(context.getResources().getIdentifier("T_" + stringColor, "style", context.getPackageName()));无法解决这个问题,您能给我一个解释或链接来跟进这个话题吗?
Langusten Gustel

1
@ IQ.feature我认为将您的代码推送到Github存储库更适合代码共享
Vadim Kotov

1
只是为了让其他人清楚,因为我很喜欢这个……有一个文件res-v9 / styles.xml,它声明了具有不同颜色的整个主题列表,并且代码本质上应用了这些主题之一并重新创建了活动。这是其他答案也试图实现的目标。.即,通过预定义主题(不是通过编程方式或动态创建主题)可以解决此问题。
frezq

3

我使用了Dahnark的代码,但还需要更改ToolBar背景:

if (dark_ui) {
    this.setTheme(R.style.Theme_Dark);

    if (Build.VERSION.SDK_INT >= 21) {
        getWindow().setNavigationBarColor(getResources().getColor(R.color.Theme_Dark_primary));
        getWindow().setStatusBarColor(getResources().getColor(R.color.Theme_Dark_primary_dark));
    }
} else {
    this.setTheme(R.style.Theme_Light);
}

setContentView(R.layout.activity_main);

toolbar = (Toolbar) findViewById(R.id.app_bar);

if(dark_ui) {
    toolbar.setBackgroundColor(getResources().getColor(R.color.Theme_Dark_primary));
}

在您的工具栏(在.xml文件中)中添加以下代码:android:background =“?attr / colorPrimary”,因此您无需在Java中设置背景。
哈维尔·塞哥维亚·科尔多瓦

但是我有两个不同的工具栏,一个用于浅色主题,另一个用于深色主题。如果添加android:background =“?attr / colorPrimary”,则必须使用某种选择器。
lgallard

我有很多主题,我只使用该代码。看看这个应用程序:play.google.com/store/apps/...
JavierSegoviaCordoba

-1

您不能更改colorPrimary的颜色,但是可以通过添加具有不同colorPrimary颜色的新样式来更改应用程序的主题

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
</style>

<style name="AppTheme.NewTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="colorPrimary">@color/colorOne</item>
    <item name="colorPrimaryDark">@color/colorOneDark</item>
</style>

在活动设置主题内

 setTheme(R.style.AppTheme_NewTheme);
 setContentView(R.layout.activity_main);

我看到已经有较早的答案描述了切换样式。在什么情况下,您的答案比那些答案更合适?明确地说,问题是:“在运行时,用户决定他要使用#ccffff作为原色。当然,我无法为所有可能的颜色创建主题。” 这不能解决这个需求;但是,如果没有人描述如何做到这一点,了解这一点将很有用。
制造商史蒂夫,

-2

通过一项活动,您可以执行以下操作:

getWindow().setStatusBarColor(i color);

2
有趣的是,这个答案有-8票,但可以解决我的问题:D
Nabin Bhandari

-4

使用工具栏

您可以通过创建自定义工具栏类来动态设置自定义工具栏项目的颜色:

package view;

import android.app.Activity;
import android.content.Context;
import android.graphics.ColorFilter;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.support.v7.internal.view.menu.ActionMenuItemView;
import android.support.v7.widget.ActionMenuView;
import android.support.v7.widget.Toolbar;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AutoCompleteTextView;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;

public class CustomToolbar extends Toolbar{

    public CustomToolbar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // TODO Auto-generated constructor stub
    }

    public CustomToolbar(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
    }

    public CustomToolbar(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
        ctxt = context;
    }

    int itemColor;
    Context ctxt;

    @Override 
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        Log.d("LL", "onLayout");
        super.onLayout(changed, l, t, r, b);
        colorizeToolbar(this, itemColor, (Activity) ctxt);
    } 

    public void setItemColor(int color){
        itemColor = color;
        colorizeToolbar(this, itemColor, (Activity) ctxt);
    }



    /**
     * Use this method to colorize toolbar icons to the desired target color
     * @param toolbarView toolbar view being colored
     * @param toolbarIconsColor the target color of toolbar icons
     * @param activity reference to activity needed to register observers
     */
    public static void colorizeToolbar(Toolbar toolbarView, int toolbarIconsColor, Activity activity) {
        final PorterDuffColorFilter colorFilter
                = new PorterDuffColorFilter(toolbarIconsColor, PorterDuff.Mode.SRC_IN);

        for(int i = 0; i < toolbarView.getChildCount(); i++) {
            final View v = toolbarView.getChildAt(i);

            doColorizing(v, colorFilter, toolbarIconsColor);
        }

      //Step 3: Changing the color of title and subtitle.
        toolbarView.setTitleTextColor(toolbarIconsColor);
        toolbarView.setSubtitleTextColor(toolbarIconsColor);
    }

    public static void doColorizing(View v, final ColorFilter colorFilter, int toolbarIconsColor){
        if(v instanceof ImageButton) {
            ((ImageButton)v).getDrawable().setAlpha(255);
            ((ImageButton)v).getDrawable().setColorFilter(colorFilter);
        }

        if(v instanceof ImageView) {
            ((ImageView)v).getDrawable().setAlpha(255);
            ((ImageView)v).getDrawable().setColorFilter(colorFilter);
        }

        if(v instanceof AutoCompleteTextView) {
            ((AutoCompleteTextView)v).setTextColor(toolbarIconsColor);
        }

        if(v instanceof TextView) {
            ((TextView)v).setTextColor(toolbarIconsColor);
        }

        if(v instanceof EditText) {
            ((EditText)v).setTextColor(toolbarIconsColor);
        }

        if (v instanceof ViewGroup){
            for (int lli =0; lli< ((ViewGroup)v).getChildCount(); lli ++){
                doColorizing(((ViewGroup)v).getChildAt(lli), colorFilter, toolbarIconsColor);
            }
        }

        if(v instanceof ActionMenuView) {
            for(int j = 0; j < ((ActionMenuView)v).getChildCount(); j++) {

                //Step 2: Changing the color of any ActionMenuViews - icons that
                //are not back button, nor text, nor overflow menu icon.
                final View innerView = ((ActionMenuView)v).getChildAt(j);

                if(innerView instanceof ActionMenuItemView) {
                    int drawablesCount = ((ActionMenuItemView)innerView).getCompoundDrawables().length;
                    for(int k = 0; k < drawablesCount; k++) {
                        if(((ActionMenuItemView)innerView).getCompoundDrawables()[k] != null) {
                            final int finalK = k;

                            //Important to set the color filter in seperate thread, 
                            //by adding it to the message queue
                            //Won't work otherwise. 
                            //Works fine for my case but needs more testing

                            ((ActionMenuItemView) innerView).getCompoundDrawables()[finalK].setColorFilter(colorFilter);

//                              innerView.post(new Runnable() {
//                                  @Override
//                                  public void run() {
//                                      ((ActionMenuItemView) innerView).getCompoundDrawables()[finalK].setColorFilter(colorFilter);
//                                  }
//                              });
                        }
                    }
                }
            }
        }
    }



}

然后在布局文件中引用它。现在,您可以使用

toolbar.setItemColor(Color.Red);

资料来源:

我在此处找到了执行此操作的信息:如何动态更改Android工具栏图标的颜色

然后我对其进行了编辑,对其进行了改进,并将其发布在此处:GitHub:AndroidDynamicToolbarItemColor


这不能回答问题。尤其是“我的目标是最终使ActionBar 和所有小部件(如CheckBox)都使用此原色 ”。
nhaarman

然后只需添加一个复选框即可。例如,添加if(v instanceof CheckBox){themeChexnoxWithColor(toolbarIconsColor); 我看不出这不回答你的诚实问题
迈克尔·科恩

@nhaarman,您可以动态设置操作条颜色,例如stackoverflow.com/questions/23708637/change-actionbar-background-color-dynamic我只是不完全了解您的问题
Michael Kern

我有一个应用程序,用户可以在其中选择操作栏颜色和操作栏项颜色。我看不到您还需要什么
Michael Kern

2
这对我很有帮助。
萤火虫2015年

-6

您可以执行以下操作:

在drawable文件夹中写一个文件,让它命名为background.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
    <solid android:color="?attr/colorPrimary"/>
</shape>

然后设置您的布局(或情况如何) android:background="@drawable/background"

在设置主题时,该颜色将代表相同的颜色。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.