Come _really_ modificare a livello di codice il colore primario e d'accento in Android Lollipop?


156

Prima di tutto, questa domanda pone una domanda molto simile. Tuttavia, la mia domanda ha una sottile differenza.

Quello che mi piacerebbe sapere è se è possibile modificare a livello di colorPrimarycodice l' attributo di un tema in un colore arbitrario ?

Quindi, ad esempio, abbiamo:

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

In fase di esecuzione, l'utente decide di voler utilizzare #ccffffcome colore primario. Ovviamente non è possibile creare temi per tutti i colori possibili.

Non mi importa se devo fare cose complicate, come fare affidamento sugli interni privati ​​di Android, purché funzioni usando l'SDK pubblico.

Il mio obiettivo è quello di avere finalmente le ActionBar e tutti i widget come una CheckBoxdi utilizzare questo colore primario.


1
Non hai idea di cosa siano gli "interni privati ​​di Android" per una versione inesistente di Android. Non dare per scontato che gli "interni privati" di L siano uguali a quelli di qualunque cosa L si trasformi in termini di rilascio di una produzione.
Commons War

No, non puoi inserire dati arbitrari in un tema. Detto questo, colorPrimary viene utilizzato solo per lo sfondo della barra delle azioni, il colore della barra e il colore delle notifiche e puoi cambiarli tutti dinamicamente.
alanv,

2
Penso che dovresti chiedere "Come cambiare l'attributo di stile in fase di esecuzione", e da quello che ho visto la risposta è che non puoi. Tuttavia ho un'idea che potrebbe aiutarti. Utilizzare ContextWrapper personalizzato e fornire risorse proprie. Guarda questo: github.com/negusoft/holoaccent/blob/master/HoloAccent/src/com/… Nel complesso questo progetto potrebbe darti un'idea di come farlo.
Mikooos,

1
Basta fare il brainfart qui, ma tutto l'XML viene convertito in file .dex che vengono caricati nella tua applicazione Android come oggetti java giusti. Ciò non significa che dovremmo essere in grado di creare e impostare interi temi dal codice, nonché generare il tema da una fabbrica ancora da scrivere? Sembra un sacco di lavoro.
G_V,

1
@NiekHaarman hai mai trovato un modo?
gbhall,

Risposte:


187

I temi sono immutabili, non puoi.


8
Grazie Chris! Non è la risposta che cercavo, ma immagino che dovrò conviverci :)
nhaarman,

5
Ciao @Chris Banes, ma in che modo l'app di contatto ha cambiato il colore della barra di stato e il colore della barra degli strumenti in base al colore del tema del contatto? Se è possibile, penso che ciò di cui Niek Haarman ha bisogno non sia troppo lontano poiché deve solo memorizzare il codice colore che l'utente desidera. Potresti spiegare di più al riguardo? Voglio anche creare qualcosa di simile ma sono davvero confuso con esso.
Cigno,

39
Il colore della barra di stato può essere modificato dinamicamente tramite Window.setStatusBarColor ().
Chris Banes,

9
È possibile creare un tema a livello di codice?
Andrew Orobator,

3
E mentre cambi dinamicamente il colore della barra di stato puoi anche cambiare il colore della barra di navigazione tramite Window.setNavigationBarColor () - API 21 :)
user802421

65

Ho letto i commenti sull'app dei contatti e su come utilizzare un tema per ciascun contatto.

Probabilmente, l'app dei contatti ha alcuni temi predefiniti (per ogni colore primario del materiale da qui: http://www.google.com/design/spec/style/color.html ).

È possibile applicare un tema prima di un metodo setContentView all'interno del metodo onCreate.

Quindi l'app dei contatti può applicare un tema in modo casuale a ciascun utente.

Questo metodo è:

setTheme(R.style.MyRandomTheme);

Ma questo metodo ha un problema, ad esempio può cambiare il colore della barra degli strumenti, il colore dell'effetto di scorrimento, il colore dell'ondulazione, ecc., Ma non può cambiare il colore della barra di stato e il colore della barra di navigazione (se si desidera cambiarlo anche).

Quindi, per risolvere questo problema, è possibile utilizzare il metodo prima e:

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

Questi due metodi cambiano il colore della barra di stato e di navigazione. Ricorda, se imposti la tua barra di navigazione in modo traslucido, non puoi cambiarne il colore.

Questo dovrebbe essere il codice finale:

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

}

Puoi usare un interruttore e generare un numero casuale per usare temi casuali o, come nell'app dei contatti, probabilmente a ciascun contatto è associato un numero predefinito.

Un esempio di tema:

<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>

Mi scusi per il mio inglese.


1
La ringrazio per la risposta. Sfortunatamente, la mia richiesta era di usare un colore arbitrario . Naturalmente non è possibile creare temi per tutti i colori.
nhaarman,

1
@DanielGomezRico - AFAIK puoi scavalcare il tema preimpostato indipendentemente da dove inizialmente impostato - purché lo fai abbastanza presto. Come dice la risposta "Puoi applicare un tema prima del metodo setContentView all'interno del metodo onCreate".
ToolmakerSteve

50

Puoi usare Theme.applyStyle per modificare il tema in fase di esecuzione applicando un altro stile ad esso.

Supponiamo che tu abbia queste definizioni di stile:

<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>

Ora puoi rattoppare il tuo tema in fase di esecuzione in questo modo:

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

Il metodo applyStyledeve essere chiamato prima che il layout venga gonfiato! Quindi, a meno che non carichi manualmente la vista, devi applicare gli stili al tema prima di chiamare la setContentViewtua attività.

Naturalmente questo non può essere usato per specificare un colore arbitrario, cioè uno su 16 milioni (256 3 ) di colori. Ma se scrivi un piccolo programma che genera le definizioni di stile e il codice Java per te, dovrebbe essere possibile qualcosa come uno su 512 (8 3 ).

Ciò che rende questo interessante è che puoi usare sovrapposizioni di stili diversi per diversi aspetti del tuo tema. colorAccentPer esempio, aggiungi solo alcune definizioni di overlay . Ora puoi combinare valori diversi per il colore primario e il colore accento quasi arbitrariamente.

È necessario assicurarsi che le definizioni del tema di overlay non ereditino accidentalmente un gruppo di definizioni di stile da una definizione di stile padre. Ad esempio uno stile chiamato AppTheme.OverlayRederedita implicitamente tutti gli stili definiti AppThemee tutte queste definizioni verranno applicate anche quando si applica la patch al tema principale. Quindi, evita i punti nei nomi dei temi di sovrapposizione o usa qualcosa di simile Overlay.Rede definisci Overlayuno stile vuoto.


11
Prova a chiamare applyStyleprima di chiamare la setContentViewtua attività e dovrebbe funzionare.
Devconsole,

1
sì okay, allora potrebbe funzionare! sto cercando un modo per cambiare il coloraccent in framment niveau non attività! ecco perché non ha funzionato tristemente per me: <Il mio caso d'uso è che desidero colori diversi per FAB o indicatori di tabulazione quando passo da tabulazioni che avvieranno un frammento diverso!
Dennis Anderson,

Grazie, questo mi ha aiutato molto! Tuttavia, non sono in grado di farlo più volte. (uno per colorPrimary, uno per colorAccent, ecc.). Potresti aiutarmi? Grazie. stackoverflow.com/questions/41779821/…
Thomas Vos

1
Questo è il tipo di risposta per cui voglio usare un secondo account solo per +1 ancora una volta. Grazie.
Benoit Duffez,

Grazie mille, questo era quello che stavo cercando .... spero di poter cambiare il colore Accent del tema attuale usando questo approccio.
Nasib,

32

Ho creato una soluzione per creare temi di qualsiasi colore, forse questo può essere utile per qualcuno. API 9+

1. prima crea " res / valori-v9 / " e inserisci qui questo file: styles.xml e la normale cartella "res / valori" verranno usate con i tuoi stili.

2. inserisci questo codice nel tuo res / valori / 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. in per AndroidManifest:

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

4. crea una nuova classe con il nome "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. Attività principale:

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

Per cambiare colore, basta sostituire Casuale con il tuo RGB, spero che questo ti aiuti.

inserisci qui la descrizione dell'immagine

C'è un esempio completo: ColorTest.zip


puoi condividere un progetto simile?
Erselan Khan,

Crea un nuovo progetto, scarica il file - "styles.xml" e usa il codice sopra. In bocca al lupo.
Funzionalità QI

1
Non riesco ad avvolgere la testa. context.setTheme(context.getResources().getIdentifier("T_" + stringColor, "style", context.getPackageName()));Puoi darmi una spiegazione o un link per seguire questo argomento?
Langusten Gustel,

1
@ IQ.feature Penso che il push del tuo codice nel repository Github sia più adatto per la condivisione del codice
Vadim Kotov

1
Giusto per essere chiari per gli altri perché mi sono innamorato di questo ... c'è un file res-v9 / styles.xml che dichiara un intero elenco di temi con colori diversi e il codice essenzialmente applica uno di questi temi e ricrea l'attività. È quello che anche altre risposte hanno cercato di ottenere ... ovvero è una soluzione alternativa che predispone temi che non creano temi in modo programmatico o dinamico.
frezq

3

Ho usato il codice di Dahnark ma devo anche cambiare lo sfondo della barra degli strumenti:

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

aggiungi questo codice: android: background = "? attr / colorPrimary", nella tua barra degli strumenti (nel file .xml), quindi non devi impostare lo sfondo in java.
JavierSegoviaCordoba,

Ma ho due barre degli strumenti diverse, una per un tema chiaro e un'altra per un tema scuro. Se aggiungo android: background = "? Attr / colorPrimary" Devo usare un qualche tipo di selettore.
lgallard,

Avevo molti temi e uso solo quel codice. Dai un'occhiata a questa app: play.google.com/store/apps/…
JavierSegoviaCordoba

-1

Non puoi cambiare il colore di colorPrimary, ma puoi cambiare il tema della tua applicazione aggiungendo un nuovo stile con un colore diverso Colore primario

<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>

e all'interno del set di attività

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

Vedo che ci sono già risposte precedenti che descrivono gli stili di commutazione. In quale situazione la tua risposta è più appropriata di quelle? E per essere chiari, la domanda dice "In fase di esecuzione, l'utente decide di voler usare #ccffff come colore primario. Naturalmente non è possibile creare temi per tutti i colori possibili". Questo non risolve questo bisogno; tuttavia se nessuno avesse descritto come farlo, sarebbe utile saperlo.
ToolmakerSteve

-2

da un'attività che puoi fare:

getWindow().setStatusBarColor(i color);

2
Divertente che questa risposta abbia -8 voti ma risolva il mio problema: D
Nabin Bhandari il

-4

USA UNA BARRA DEGLI STRUMENTI

È possibile impostare dinamicamente un colore dell'elemento della barra degli strumenti personalizzato creando una classe barra degli strumenti personalizzata:

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



}

quindi fare riferimento ad esso nel file di layout. Ora puoi impostare un colore personalizzato usando

toolbar.setItemColor(Color.Red);

fonti:

Ho trovato le informazioni per farlo qui: Come cambiare dinamicamente il colore delle icone di Android Toolbar

e poi l'ho modificato, migliorato e pubblicato qui: GitHub: AndroidDynamicToolbarItemColor


Questo non risponde alla domanda. Soprattutto la parte "Il mio obiettivo è quello di avere ActionBar e tutti i widget come un CheckBox per utilizzare questo colore primario ".
nhaarman,

Quindi aggiungi solo per includere una casella di controllo. Ad esempio, aggiungi if (v instanceof CheckBox) {themeChexnoxWithColor (toolbarIconsColor); Non vedo come questo non risponda onestamente alla tua domanda
Michael Kern,

@nhaarman puoi impostare dinamicamente il colore della barra delle azioni come questo stackoverflow.com/questions/23708637/change-actionbar-background-color-dynamically Non capisco esattamente la tua domanda
Michael Kern,

Ho un'app in cui l'utente può scegliere il colore della barra delle azioni e i colori degli elementi della barra delle azioni. Non vedo cos'altro ti serve
Michael Kern,

2
Questo mi è stato molto utile.
Firefly,

-6

Questo è quello che PUOI fare:

scrivere un file in una cartella disegnabile, chiamiamolo 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>

quindi imposta il layout (o qualunque sia il caso) android:background="@drawable/background"

impostando il tema questo colore rappresenterebbe lo stesso.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.