Ressourcen abhängig vom Theme laden

missspelled

missspelled

App-Anbieter (In-App)
127
Hallo,
da mir schon seit längerem die Frage durch den Kopf ging, wie man möglichst sinnvoll* verschiedene Themes in eine App einbaut, hier ein kurzes How-To.
*sinnvoll = performanceschonend und hinsichtlich der Implementierung "einfach", sowie "leicht wartbarer Code"

Da ich nicht auf jedes winzige Detail eingehen möchte eine Übersicht hinsichtlich der
Voraussetzung um das How-To zu verstehen:
- wissen, wie eine ListView funktioniert
- wissen, wie man SharedPreferences implementiert und darauf zurückgreift
- wissen, wie man dimens anlegt
(- ein grobes Verständnis für den Vererbungsmechanismus in xml ist hilfreich..)

Was soll passieren:
Wir haben eine ListView, die "CustomDivider" (Trennlinien) erhalten soll. Diese sollen sich automatisch, also ohne den Java-Code in irgendeiner Form zu beeinflussen, farblich an das jeweilige Theme anpassen. Warum diese Divider angepasst werden müssen, kann verschiedene optische Gründe haben. Bei mir war der Grund, dass sie gekürzt anzeigen wollte, also so dass eine Trennlinie nicht mehr von der einen Bildschirmseite bis zur anderen durchgeht - sondern ein gewisser Bereich am linken und rechten Rand frei bleibt.

Los gehts:
Da wir zwei verschiedene Themes zur Auswahl stellen, legen wir in der styles.xml folgende Einträge an:

Code:
    <!--    LIGHT   -->
    <style name="AppTheme" parent="AppTheme.Base">
       <item name="theme_dependent_list_divider">@drawable/custom_list_divider</item>

    </style>

    <style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="colorPrimary">@color/primaryColor</item>
        <item name="colorPrimaryDark">@color/primaryColorDark</item>
        <item name="colorAccent">@color/colorAccent</item>

    </style>

 <!--    DARK   -->
    <style name="AppTheme.Dark" parent="AppTheme.Dark.Base">
        <item name="theme_dependent_list_divider">@drawable/custom_list_divider_dark</item>

    </style>

    <style name="AppTheme.Dark.Base" parent="Theme.AppCompat.NoActionBar">
        <item name="colorPrimary">@color/primaryColor_darkTheme</item>
        <item name="colorPrimaryDark">@color/primaryColorDark_darkTheme</item>
        <item name="colorAccent">@color/colorAccent_darkTheme</item>

    </style>
Wichtig ist bei obigem Code der Eintrag "theme_dependent_list_divider"... auf den kommen wir später zurück...
(ergänzende Info: Da die beiden Themes von einem anderen "Eltern-Theme" abgeleitet sind, wird die Activity später je nach Einstellung "light" oder "dark" angezeigt.)

Laden können wir diese Themes aus einer Activity, wenn wir eine Einstellung dafür in den SharedPrefs angelegt haben und nachfolgende Methode
VOR super.onCreate(saveInstanceState) und setContentView(R.layout.activity_main)
aufrufen.

Code:
   public static void applyTheme(Context c) {
        final SharedPreferences sp = c.getSharedPreferences(Pref.CONFIG_FILE_NAME, Context.MODE_PRIVATE);
        if (sp.getString(Pref.PK_LIST_THEME, Pref.DEFAULT_THEME).equals(Pref.LIGHT))
            c.setTheme(R.style.AppTheme);
        else c.setTheme(R.style.AppTheme_Dark);
    }
Nun kommen wir wieder auf den "theme_dependent_divider" zurück und legen nun in values eine Datei namens "attrs" an. Dort sieht der Code wie folgt aus:
(auch wenn es wenig Code ist - hier genau aufpassen, da hier die "eigentliche" Referenzierung erfolgt!)
Code:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="theme_dependent_list_divider" format="reference"/>

</resources>
Nun ein Blick auf die ListView:
Code:
    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:cacheColorHint="@android:color/transparent"
        android:headerDividersEnabled="false"
        android:divider="?attr/theme_dependent_list_divider"
        android:dividerHeight="1dp"
        />
Beachte hier den Eintrag: android:divider="?attr/theme_dependent_list_divider" - das Setzen von dividerHeight ist ebenfalls notwendig.

Im Prinzip wars das jetzt auch schon.. Es fehlen lediglich 2 Drawables, die wir in der eigentlichen Theme-Konfiguration (siehe styles.xml) laden.

hier der Code von drawable/custom_list_divider_dark.xml
Code:
<?xml version="1.0" encoding="UTF-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:left="@dimen/custom_list_divider_margin_left"
        android:right="@dimen/custom_list_divider_margin_right">

        <shape android:shape="rectangle">
            <gradient
                android:startColor="@color/Grey_7"
                android:centerColor="@color/Grey_7"
                android:endColor="@color/Grey_7"
                android:height="1px"
                android:angle="0"/>
        </shape>
    </item>
</layer-list>
hier der Code von drawable/custom_list_divider.xml (also der für das Light-Theme)
Code:
<?xml version="1.0" encoding="UTF-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:left="@dimen/custom_list_divider_margin_left"
        android:right="@dimen/custom_list_divider_margin_right">

        <shape android:shape="rectangle">
            <gradient
                android:startColor="@color/Grey_4"
                android:centerColor="@color/Grey_4"
                android:endColor="@color/Grey_4"
                android:height="1px"
                android:angle="0"/>
        </shape>
    </item>
</layer-list>
Hoffe es ist halbwegs verständlich geworden, wie das ganze funktioniert. Ich habe mir mit der Referenzierung über die "attrs" anfangs etwas schwer getan, aber wenn man ein bisschen drüber nachtdenkt, kommt man doch recht schnell dahinter. :)

Falls was unklar ist, fragt einfach.
 
Zurück
Oben Unten