Как _really_ программно изменить основной и акцентный цвет в Android Lollipop?
Прежде всего, этот вопрос задает очень похожий вопрос. Однако у моего вопроса есть небольшая разница.
Я хотел бы знать, можно ли программно изменить 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
использовать этот основной цвет.
13 ответов
Я читаю комментарии о приложении контактов и о том, как оно использует тему для каждого контакта.
Возможно, в приложении контактов есть несколько предопределенных тем (для каждого основного цвета материала отсюда: http://www.google.com/design/spec/style/color.html).
Вы можете применить тему перед методом setContentView внутри метода onCreate.
Затем приложение "Контакты" может применить тему произвольно для каждого пользователя.
Этот метод:
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>
Извините за мой английский.
Вы можете использовать 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
в вашей деятельности.
Конечно, это нельзя использовать для указания произвольного цвета, то есть одного из 16 миллионов (2563) цветов. Но если вы напишете небольшую программу, которая генерирует определения стилей и код Java для вас, то должно быть возможно что-то вроде одной из 512 (83).
Что делает это интересным, так это то, что вы можете использовать разные стили наложения для разных аспектов вашей темы. Просто добавьте несколько определений наложения для colorAccent
например. Теперь вы можете комбинировать разные значения основного цвета и цвета акцента практически произвольно.
Вы должны убедиться, что определения вашей оверлейной темы случайно не наследуют набор определений стилей из определения родительского стиля. Например, стиль называется AppTheme.OverlayRed
неявно наследует все стили, определенные в AppTheme
и все эти определения также будут применены, когда вы исправите основную тему. Так что либо избегайте точек в именах наложенных тем, либо используйте что-то вроде Overlay.Red
и определить Overlay
как пустой стиль.
Я создал какое-то решение для создания любых цветовых тем, может быть, это кому-нибудь пригодится. 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. и перед вызовом setContentView(R.layout.activity_main) просто добавьте:
new ThemeColors(this);
чтобы изменить цвет, замените Random на RGB:
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
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);
}
});
Библиотека GreenMatter может помочь вам достичь желаемой функциональности:
Я использовал код Dahnark, но мне также нужно изменить фон панели инструментов:
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));
}
Из деятельности вы можете сделать:
getWindow().setStatusBarColor(i color);
Вы можете изменить определение своих собственных тем или настроить существующие темы Android в res> values> themes, найти, где указан основной цвет, и указать его на цвет, определенный в color.xml, который вы хотите
<style name="Theme.HelloWorld" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/my_color</item>
<item name="colorPrimaryVariant">@color/my_color</item>
<item name="colorOnPrimary">@color/white</item>
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(getResources().getColor(R.color.Black));
}
Вы не можете изменить цвет 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);
ИСПОЛЬЗУЙТЕ ИНСТРУМЕНТ
Вы можете динамически установить цвет элемента панели инструментов, создав собственный класс панели инструментов:
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
а затем я отредактировал его, улучшил его и разместил здесь: https://github.com/powerpoint45/AndroidDynamicToolbarItemColor
Это может быть не совсем то, что вы хотите, но этот ответ работает для меня.
Я также хотел бы увидеть метод, где вы устанавливаете один раз для всех ваших действий. Но насколько я знаю, вы должны установить в каждом действии, прежде чем показывать какие-либо взгляды.
Для справки проверьте это:
http://www.anddev.org/applying_a_theme_to_your_application-t817.html
Редактировать (скопировано с этого форума):
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Call setTheme before creation of any(!) View.
setTheme(android.R.style.Theme_Dark);
// ...
setContentView(R.layout.main);
}
Это то, что вы можете сделать:
записать файл в папку 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"
При настройке вашей темы этот цвет будет соответствовать.