16

Ich versuche AppCompat-Dialoge einzurichten, so dass die Schaltflächen die gleiche Farbe wie die Akzentfarbe der Anwendung verwenden, ohne die Farbe selbst zu wiederholen. Dies funktioniert perfekt mit AppCompat v22 (nur für Lollipop, natürlich) durch diese Stile mit Datei in values-v21:Vererbung von AppCompat 22.1.1 Dialog colorAccent aus App-Design funktioniert nicht

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

Wenn AppCompat v22.1 was released Ich habe versucht, dies für alle Android-Versionen einzurichten, so zog ich diese Stile an die Basis values Ordner und im Grunde ersetzt alle android: und V21-spezifischen Attribute von ihren AppCompat Pendants.

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

Aber es funktioniert nicht - die App Abstürze, wenn es einen Alarm-Dialog zu zeigen versucht. Es gibt einige Warnungen in logcat, die ich stark an das Problem im Zusammenhang vermuten:

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

Und die Ausnahme ist:

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) 

Natürlich den Farbwert Duplizieren (oder eine Farbressource Erstellen und Referenzierung es funktioniert) ... aber aufgrund der Projektstruktur brauche ich es so.

Fazit: ich den Verweis auf ?attr/colorAccent annehmen irgendwie einen Zyklus schafft ... aber ich kann nicht herausfinden, warum oder wie man es beheben.

Irgendwelche Ideen?


Update: Warum benötige ich/wollen accentColor setzen nur in dem Thema?

Wir haben eine Bibliothek, die vielen Anwendungen gemein ist, die "Basis" -Themen definiert, aus denen das eigentliche Anwendungsthema abgeleitet werden muss. So das obige Beispiel, die vereinfacht ist, ist eigentlich eher an etwas wie folgt aus:

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

(plus die entsprechenden Varianten für Licht und Licht mit dunklen Action-Bar).

Danach sollte die Anwendung Projekt nur ein Basisthema aus der bereitgestellten Liste auswählen und dann wie etwas hat:

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

Zumindest ist dies die ideale Situation, die vor (wie @Vikram Punkte gearbeitet in seiner Antwort, anscheinend nur, weil es zwei verschiedene colorAccent Attribute gibt).

Der Vorteil dieses Schemas besteht darin, dass ich nicht gezwungen bin, Standardwerte für colorAccent in den Bibliotheksthemen anzugeben, da diese von den AppCompat-Basis-Einsen geerbt werden.

Sollte ich auch Farbressourcen erstellen müssen, würde ich zwei von ihnen benötigen (für helle und dunkle Varianten), da die Standardfarben nicht die gleichen sind.

Also, im Idealfall suche ich nach einer ähnlichen Lösung (sofern vorhanden).

Antwort

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

Sie wissen bereits, dass dies nicht funktioniert. Hier ist warum:

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

Das Snippet ist selbsterklärend. Sie können haben:

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

und dann:

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

.... ....

Aber Verkettungs Attribute wie diese können nicht mehr als 20 Ebenen tief sein. Android gibt wegen Too many attribute references... auf.

In Ihrem Fall setzen Sie den Wert eines Attributs, indem Sie versuchen, seinen aktuellen Wert zu lesen. Android sucht, findet aber stattdessen eine andere Referenz - endlose Verfolgung.

Warum hat es vorher funktioniert?

<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, Sie setzen colorAccent Attribut des Support-Bibliothek (kein android: Namespace). Unter AlertDialogTheme setzen Sie android:colorAccent auf Unterstützung Bibliothek colorAccent. Aus diesem Grund nimmt eine Endlosschleife keine Form an.

?attr/colorAccent ist innerhalb der Context gelöst, die Sie übergeben, wenn Sie die AlertDialog erstellen. Aus diesem Grund ist es gegen <item name="colorAccent">#FF9800</item> gelöst.

Eine mögliche Abhilfe wäre eine benutzerdefinierte attr in res/values/attrs.xml zu definieren:

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

und verwenden Sie es in AlertDialogTheme:

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

Jetzt haben wir dieses Attribut unter AppTheme initialisieren

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

BU T, das wird immer noch nicht funktionieren.Es entsteht immer noch ein Zyklus - colorAccent ist alertDialogColorAccent & alertDialogColorAccent ist colorAccent. Irgendwo entlang der Kette, ein tatsächlicher Farbwert muss eingestellt werden:

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

Jetzt können Sie colorAccent mit alertDialogColorAccent initialisieren, da es zu einer tatsächlichen Farbwert bezieht:

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

Dies ist _amost_ die Lösung, die ich brauche, aber nicht ganz. Ich habe mit etwas Ähnlichem experimentiert und nach meinen Tests funktioniert es nicht, wenn ich versuche, in '? Attr/colorAccent' zu setzen. Kannst du das verifizieren? Mein Ziel ist es, ein paar benutzerdefinierte Themen in einer Bibliothek zur Verfügung zu stellen, die angepasst werden können, indem nur 'colorAccent' in ihnen gesetzt wird. – matiash

+0

@matiash Ich sehe. Lass uns hier reden: [Link] (http://chat.stackoverflow.com/rooms/info/77523/room-for-question-30132302?tab=general) – Vikram

0

ich ?attr/colorAccent hier glauben, beziehen sich auf die gleichen, die Sie setzen wollen:

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

Sie können die Akzentfarbe auf ein @color Attribut extrahieren, es dann für beide Thema App verwenden und Dialogdesign.

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

Ja, ich verstehe, dass dies eine Möglichkeit ist, aber ich fragte nach anderen Lösungen. Danke trotzdem. – matiash

1

Basierend auf @ Vikram ausgezeichnete Antwort, ich bin damit fertig (ich stelle es für zukünftige Referenz auf, falls jemand an der tatsächlichen Lösung interessiert ist).

Für die Basis Stile-Datei, in values (Anmerkung: verschiedene XML-Dateien für Attribute, Farben sind & c bevorzugt - sie sind alle hier zusammen für Kompaktheit):

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

Die neue dialogColorAccent Attribut ist notwendig, damit es aus den Dialogstilen referenziert werden kann. Leider kann es sich nicht auf colorAccent selbst beziehen (und beides ist nicht umgekehrt möglich), also müssen beide den gleichen Wert haben (in diesem Fall eine Referenz auf eine Farbressource).

Dadurch wird sichergestellt, dass die mit android.support.v7.app.AlertDialog.Builder erstellten AlertDialogs die Akzentfarbe für die positiven/negativen Schaltflächen für alle Android-Versionen verwenden.

jedoch eine zusätzliche Datei ist notwendig für einige Dialoge (wie DatePickerDialog, TimePickerDialog und ProgressDialog), die Materialausführungen in Lollipop haben aber keine AppCompat gleichwertig bisher. Daher geht die folgende Stildatei 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> 

Diese werden diese neuen Dialoge machen auch die Akzentfarbe haben (aber nur in Lollipop, natürlich).


Die Lösung könnte ein wenig verworren scheinen (@myAccentColor vom AlertDialogTheme Stil Referenzierung ist viel einfacher :)), aber es war notwendig, weil mehrere Themen in einer Bibliothek definiert sind, und die Idee war, dass diese Themen können vererbt werden und mit minimalen Änderungen verwendet.

Verwandte Themen