16

Sto provando a configurare le finestre di AppCompat in modo che i pulsanti utilizzino lo stesso colore del colore dell'accento dell'applicazione, senza ripetere il colore stesso. Questo stava funzionando perfettamente con AppCompat V22 (solo per Lollipop, ovviamente) utilizzando questo stili file in values-v21:Eredità AppCompat 22.1.1 Colore della finestra di dialogo L'accesso dal tema dell'app non funziona

<style name="AppTheme" parent="@style/Theme.AppCompat"> 
    <item name="colorAccent">#FF9800</item> 
    <item name="android:alertDialogTheme">@style/AlertDialogTheme</item> 
</style> 

<style name="AlertDialogTheme" parent="android:Theme.Material.Dialog.Alert"> 
    <item name="android:colorAccent">?attr/colorAccent</item> 
</style> 

Quando AppCompat v22.1 was released ho cercato di impostare questo per tutte le versioni di Android, così mi sono trasferito questi stili alla base Cartella values e in pratica ha sostituito tutti gli attributi android: e v21 dalle rispettive controparti AppCompat.

<style name="AppTheme" parent="Theme.AppCompat"> 
    <item name="colorAccent">#FF9800</item> 
    <item name="alertDialogTheme">@style/AlertDialogTheme</item> 
</style> 

<style name="AlertDialogTheme" parent="Theme.AppCompat.Dialog.Alert"> 
    <item name="colorAccent">?attr/colorAccent</item> 
</style> 

Tuttavia, non funziona - l'applicazione si blocca quando si tenta di mostrare una finestra di avviso. Ci sono alcune avvertenze in logcat che ho il forte sospetto sono legati al problema:

05-08 16:55:44.863 W/ResourceType﹕ Too many attribute references, stopped at: 0x7f01009e 

e l'eccezione è:

05-08 16:55:44.900 21301-21301/com.example.test.testaccentcolor E/AndroidRuntime﹕ FATAL EXCEPTION: main 
    Process: com.example.test.testaccentcolor, PID: 21301 
    android.view.InflateException: Binary XML file line #124: Error inflating class Button 
      at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:763) 
      at android.view.LayoutInflater.rInflate(LayoutInflater.java:806) 
      at android.view.LayoutInflater.rInflate(LayoutInflater.java:809) 
      at android.view.LayoutInflater.inflate(LayoutInflater.java:504) 
      at android.view.LayoutInflater.inflate(LayoutInflater.java:414) 
      at android.view.LayoutInflater.inflate(LayoutInflater.java:365) 
      at android.support.v7.app.AppCompatDelegateImplV7.setContentView(AppCompatDelegateImplV7.java:249) 
      at android.support.v7.app.AppCompatDialog.setContentView(AppCompatDialog.java:75) 
      at android.support.v7.app.AlertController.installContent(AlertController.java:216) 
      at android.support.v7.app.AlertDialog.onCreate(AlertDialog.java:240) 
      at android.app.Dialog.dispatchOnCreate(Dialog.java:373) 
      at android.app.Dialog.show(Dialog.java:274) 
      at android.support.v7.app.AlertDialog$Builder.show(AlertDialog.java:902) 
      at com.example.test.testaccentcolor.MainActivity.onOptionsItemSelected(MainActivity.java:37) 
      at android.app.Activity.onMenuItemSelected(Activity.java:2885) 
      at android.support.v4.app.FragmentActivity.onMenuItemSelected(FragmentActivity.java:353) 
      at android.support.v7.app.AppCompatActivity.onMenuItemSelected(AppCompatActivity.java:144) 
      at android.support.v7.internal.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:99) 
      at android.support.v7.app.AppCompatDelegateImplV7.onMenuItemSelected(AppCompatDelegateImplV7.java:538) 
      at android.support.v7.internal.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:802) 
      at android.support.v7.internal.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:153) 
      at android.support.v7.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:949) 
      at android.support.v7.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:939) 
      at android.support.v7.internal.view.menu.MenuPopupHelper.onItemClick(MenuPopupHelper.java:187) 
      at android.widget.AdapterView.performItemClick(AdapterView.java:305) 
      at android.widget.AbsListView.performItemClick(AbsListView.java:1146) 
      at android.widget.AbsListView$PerformClick.run(AbsListView.java:3053) 
      at android.widget.AbsListView$3.run(AbsListView.java:3860) 
      at android.os.Handler.handleCallback(Handler.java:739) 
      at android.os.Handler.dispatchMessage(Handler.java:95) 
      at android.os.Looper.loop(Looper.java:135) 
      at android.app.ActivityThread.main(ActivityThread.java:5254) 
      at java.lang.reflect.Method.invoke(Native Method) 
      at java.lang.reflect.Method.invoke(Method.java:372) 
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903) 
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698) 
    Caused by: java.lang.RuntimeException: Failed to resolve attribute at index 5 
      at android.content.res.TypedArray.getColorStateList(TypedArray.java:425) 
      at android.widget.TextView.<init>(TextView.java:991) 
      at android.widget.Button.<init>(Button.java:111) 
      at android.widget.Button.<init>(Button.java:107) 
      at android.support.v7.widget.AppCompatButton.<init>(AppCompatButton.java:60) 
      at android.support.v7.widget.AppCompatButton.<init>(AppCompatButton.java:56) 
      at android.support.v7.internal.app.AppCompatViewInflater.createView(AppCompatViewInflater.java:97) 
      at android.support.v7.app.AppCompatDelegateImplV7.createView(AppCompatDelegateImplV7.java:782) 
      at android.support.v7.app.AppCompatDelegateImplV7.onCreateView(AppCompatDelegateImplV7.java:810) 
      at android.support.v4.view.LayoutInflaterCompatHC$FactoryWrapperHC.onCreateView(LayoutInflaterCompatHC.java:44) 
      at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:725) 
            at android.view.LayoutInflater.rInflate(LayoutInflater.java:806) 
            at android.view.LayoutInflater.rInflate(LayoutInflater.java:809) 
            at android.view.LayoutInflater.inflate(LayoutInflater.java:504) 
            at android.view.LayoutInflater.inflate(LayoutInflater.java:414) 
            at android.view.LayoutInflater.inflate(LayoutInflater.java:365) 
            at android.support.v7.app.AppCompatDelegateImplV7.setContentView(AppCompatDelegateImplV7.java:249) 
            at android.support.v7.app.AppCompatDialog.setContentView(AppCompatDialog.java:75) 
            at android.support.v7.app.AlertController.installContent(AlertController.java:216) 
            at android.support.v7.app.AlertDialog.onCreate(AlertDialog.java:240) 
            at android.app.Dialog.dispatchOnCreate(Dialog.java:373) 
            at android.app.Dialog.show(Dialog.java:274) 
            at android.support.v7.app.AlertDialog$Builder.show(AlertDialog.java:902) 
            at com.example.test.testaccentcolor.MainActivity.onOptionsItemSelected(MainActivity.java:37) 
            at android.app.Activity.onMenuItemSelected(Activity.java:2885) 
            at android.support.v4.app.FragmentActivity.onMenuItemSelected(FragmentActivity.java:353) 
            at android.support.v7.app.AppCompatActivity.onMenuItemSelected(AppCompatActivity.java:144) 
            at android.support.v7.internal.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:99) 
            at android.support.v7.app.AppCompatDelegateImplV7.onMenuItemSelected(AppCompatDelegateImplV7.java:538) 
            at android.support.v7.internal.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:802) 
            at android.support.v7.internal.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:153) 
            at android.support.v7.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:949) 
            at android.support.v7.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:939) 
            at android.support.v7.internal.view.menu.MenuPopupHelper.onItemClick(MenuPopupHelper.java:187) 
            at android.widget.AdapterView.performItemClick(AdapterView.java:305) 
            at android.widget.AbsListView.performItemClick(AbsListView.java:1146) 
            at android.widget.AbsListView$PerformClick.run(AbsListView.java:3053) 
            at android.widget.AbsListView$3.run(AbsListView.java:3860) 
            at android.os.Handler.handleCallback(Handler.java:739) 
            at android.os.Handler.dispatchMessage(Handler.java:95) 
            at android.os.Looper.loop(Looper.java:135) 
            at android.app.ActivityThread.main(ActivityThread.java:5254) 
            at java.lang.reflect.Method.invoke(Native Method) 
            at java.lang.reflect.Method.invoke(Method.java:372) 
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903) 
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698) 

Naturalmente, duplicando il valore del colore (o la creazione di una risorsa colore e riferimento it) funziona ... ma a causa della struttura del progetto ho bisogno in questo modo.

Per riassumere: Presumo che il riferimento a ?attr/colorAccent stia creando un ciclo in qualche modo ... ma non riesco a capire perché o come risolverlo.

Qualche idea?


Update: Perché ho bisogno/desidera impostare accentColor solo nel tema?

Abbiamo una libreria comune a molte applicazioni, che definisce temi "base" dai quali deve essere derivato il tema dell'applicazione reale. Così l'esempio precedente, che viene semplificata, è in realtà più simile a qualcosa di simile:

<!-- Styles that are inherited by application-generated styles. --> 
<style name="Theme.Library.Dark" parent="@style/Theme.AppCompat"> 
    <item name="alertDialogTheme">@style/Theme.Library.Dark.AlertDialog</item> 
    <!-- more properties --> 
</style> 

<style name="Theme.Library.Dark.AlertDialog" parent="@style/Theme.AppCompat.Dialog.Alert"> 
    <item name="colorAccent">?attr/colorAccent</item> 
    <!-- more properties --> 
</style> 

(più le corrispondenti varianti per luce e la luce con scuro Barra azioni).

Dopo questo, il progetto di applicazione dovrebbe scegliere solo un tema di base dall'elenco fornito, e quindi avere qualcosa di simile:

<!-- Application theme --> 
<style name="ApplicationTheme" parent="Theme.Library.Dark"> 
    <-- Ohter material colors --> 
    <item name="colorAccent">#FF9800</item> 
</style> 

Almeno questa è la situazione ideale, che ha lavorato prima (come punti @Vikram nella sua risposta, apparentemente solo perché ci sono due diversi attributi colorAccent).

Il vantaggio di questo schema è che non sono obbligato a fornire valori predefiniti per colorAccent nei temi di libreria, poiché questi sono ereditati da quelli di AppCompat base.

Inoltre, dovrei aver bisogno di creare risorse di colore, avrei bisogno di due di esse (per le varianti chiare e scure) poiché i colori predefiniti non sono gli stessi.

Quindi, idealmente, sto cercando una soluzione simile (se ne esiste una).

risposta

13
<item name="colorAccent">?attr/colorAccent</item> 

sai già che questo non funziona. Ecco perché:

ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue, 
    uint32_t* outTypeSpecFlags) const 
{ 
    int cnt = 20; 

    if (outTypeSpecFlags != NULL) *outTypeSpecFlags = 0; 

    do { 
     .... 
     ....   
     if (type == Res_value::TYPE_ATTRIBUTE) { 
      if (cnt > 0) { 
       cnt--; 
       resID = te.value.data; 
       continue; 
      } 
      ALOGW("Too many attribute references, stopped at: 0x%08x\n", resID); 
      return BAD_INDEX; 
     } 
     .... 
     .... 
    } while (true); 

    return BAD_INDEX; 
} 

Lo snippet è autoesplicativo. Si può avere:

<item name="colorZ" >?attr/colorY</item> 

e poi:

<item name="colorY" >?attr/colorX</item> 

.... ....

Ma attributi concatenamento come questo non può essere più che 20 livelli di profondità. Android si arrende a causa di Too many attribute references....

Nel tuo caso, stai impostando il valore di un attributo cercando di leggere il suo valore attuale. Android va alla ricerca, ma trova un altro riferimento - ricerca infinita.

Perché ha funzionato prima?

<style name="AppTheme" parent="@style/Theme.AppCompat"> 
    <item name="colorAccent">#FF9800</item> 
    <item name="android:alertDialogTheme">@style/AlertDialogTheme</item> 
</style> 

<style name="AlertDialogTheme" parent="android:Theme.Material.Dialog.Alert"> 
    <item name="android:colorAccent">?attr/colorAccent</item> 
</style> 

In AppTheme, si sta impostando l'attributo colorAccent di libreria di supporto (senza Android: namespace). Sotto AlertDialogTheme, si imposta android:colorAccent su libreria di supporto colorAccent. Questo è il motivo per cui un ciclo infinito non prende forma.

?attr/colorAccent viene risolto entro Context quando si crea lo AlertDialog. Per questo motivo, è stato risolto con <item name="colorAccent">#FF9800</item>.

Una possibile soluzione potrebbe essere quella di definire un personalizzato attr in res/values/attrs.xml:

<attr name="alertDialogColorAccent" format="reference|color"/> 

Ora, siamo in grado di inizializzare questo attributo sotto AppTheme:

<style name="AppTheme" parent="@style/Theme.AppCompat"> 
    <item name="colorAccent">#FF9800</item> 
    <item name="alertDialogColorAccent" >?attr/colorAccent</item> 
    <item name="android:alertDialogTheme">@style/AlertDialogTheme</item> 
</style> 

e utilizzarlo in AlertDialogTheme:

<style name="AlertDialogTheme" parent="android:Theme.Material.Dialog.Alert"> 
    <item name="colorAccent">?attr/alertDialogColorAccent</item> 
</style> 

BU T, questo non funzionerà ancora.Si crea ancora un ciclo - colorAccent è alertDialogColorAccent & alertDialogColorAccent è colorAccent. Da qualche parte lungo la catena, un valore di colore reale deve essere impostata:

<style name="AppTheme" parent="@style/Theme.AppCompat"> 
    <item name="colorAccent">#FF9800</item> 
    <item name="alertDialogColorAccent" >#FF9800</item> 
    <item name="android:alertDialogTheme">@style/AlertDialogTheme</item> 
</style> 

Ora, è possibile inizializzare colorAccent con alertDialogColorAccent in quanto si riferisce ad un valore di colore reale:

<style name="AlertDialogTheme" parent="android:Theme.Material.Dialog.Alert"> 
    <item name="colorAccent">?attr/alertDialogColorAccent</item> 
</style> 
+0

Questa è _almost_ la soluzione di cui ho bisogno, ma non del tutto. Ho sperimentato qualcosa di simile e, secondo i miei test, non funziona se provo a impostare '? Attr/colorAccent' in 'AppTheme'. Puoi verificare questo? Il mio obiettivo è fornire alcuni temi personalizzati in una libreria, che possono essere personalizzati impostando solo 'colorAccent'. – matiash

+0

@matiash vedo. Parliamo qui: [Link] (http://chat.stackoverflow.com/rooms/info/77523/room-for-question-30132302?tab=general) – Vikram

0

Credo ?attr/colorAccent qui si riferiscono alla stessa quello che si sta tentando di impostare:

<item name="colorAccent">?attr/colorAccent</item> 

È possibile estrarre il colore accento per un attributo @color, quindi utilizzarlo sia per app tema e il tema di dialogo.

colors.xml

<color name="accent">#FF9800</color> 

styles.xml

<style name="AppTheme" parent="Theme.AppCompat"> 
    <item name="colorAccent">@color/accent</item> 
    <item name="alertDialogTheme">@style/AlertDialogTheme</item> 
</style> 

<style name="AlertDialogTheme" parent="Theme.AppCompat.Dialog.Alert"> 
    <item name="colorAccent">@color/accent</item> 
</style> 
+0

Sì, ho capito che questa è una possibilità, ma stavo chiedendo altre soluzioni. Grazie comunque. – matiash

1

Sulla base @ Vikram di eccellente risposta, ho finito con questo (sto mettendo su per riferimento futuro, nel caso in cui qualcuno è interessato alla soluzione reale).

Per la base file di stili, in values (nota: diversi file XML per gli attributi, i colori, & c sono preferibili - sono tutti insieme qui per compattezza):

<?xml version="1.0" encoding="utf-8"?> 
<resources> 
    <style name="BaseAppTheme" parent="Theme.AppCompat.Light.DarkActionBar" /> 

    <attr name="dialogColorAccent" /> 
    <color name="myAccentColor">#FF9800</color> 

    <style name="AppTheme" parent="BaseAppTheme"> 
     <item name="dialogTheme">@style/DialogTheme</item> 
     <item name="alertDialogTheme">@style/AlertDialogTheme</item> 
     <item name="colorAccent">@color/myAccentColor</item> 
     <item name="dialogColorAccent">@color/myAccentColor</item> 
    </style> 

    <style name="DialogTheme" parent="Theme.AppCompat.Light.Dialog"> 
     <item name="colorAccent">?attr/dialogColorAccent</item> 
    </style> 

    <style name="AlertDialogTheme" parent="Theme.AppCompat.Light.Dialog.Alert"> 
     <item name="colorAccent">?attr/dialogColorAccent</item> 
    </style> 
</resources> 

Il nuovo dialogColorAccent l'attributo è necessario in modo che possa essere referenziato dagli stili di dialogo. Sfortunatamente non può fare riferimento allo colorAccent stesso (e nessuno è il contrario possibile), quindi entrambi devono avere lo stesso valore (in questo caso, un riferimento a una risorsa colore).

Ciò assicura che AlertDialogs creato con android.support.v7.app.AlertDialog.Builder utilizzi il colore di accento per i pulsanti positivo/negativo per tutte le versioni di Android.

Tuttavia, un file aggiuntivo è necessario per alcune finestre di dialogo (come ad esempio DatePickerDialog, TimePickerDialog e ProgressDialog) che hanno i tipi di materiale in Lollipop, ma non AppCompat equivalente finora. Quindi, il seguente file di stile va in values-v21:

<?xml version="1.0" encoding="utf-8"?> 
<resources> 
    <style name="BaseAppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> 
     <item name="android:dialogTheme">@style/DialogTheme</item> 
    </style> 
</resources> 

Questo renderà queste nuove finestre di dialogo hanno il colore accento troppo (ma solo in Lollipop, ovviamente).


La soluzione potrebbe sembrare un po 'contorta (riferimento @myAccentColor dallo stile AlertDialogTheme è il modo più semplice :)), ma si è reso necessario in quanto più temi vengono definiti in una libreria, e l'idea era che questi temi possono essere ereditate e usato con modifiche minime.

Problemi correlati