Gewusst wie: Definieren Sie ein Designelement (Stilelement) für ein benutzerdefiniertes Widget

86

Ich habe ein benutzerdefiniertes Widget für ein Steuerelement geschrieben, das wir in unserer gesamten Anwendung häufig verwenden. Die Widget-Klasse leitet sich von ImageButtonihr ab und erweitert sie auf einige einfache Arten. Ich habe einen Stil definiert, den ich auf das verwendete Widget anwenden kann, aber ich würde es vorziehen, dies über ein Thema einzurichten. In R.styleablesehe ich Widget-Stilattribute wie imageButtonStyleund textViewStyle. Gibt es eine Möglichkeit, so etwas für das von mir geschriebene benutzerdefinierte Widget zu erstellen?

jsmith
quelle

Antworten:

208

Ja, es gibt einen Weg:

Angenommen, Sie haben eine Deklaration von Attributen für Ihr Widget (in attrs.xml):

<declare-styleable name="CustomImageButton">
    <attr name="customAttr" format="string"/>
</declare-styleable>

Deklarieren Sie ein Attribut, das Sie für eine Stilreferenz verwenden (in attrs.xml):

<declare-styleable name="CustomTheme">
    <attr name="customImageButtonStyle" format="reference"/>
</declare-styleable>

Deklarieren Sie eine Reihe von Standardattributwerten für das Widget (in styles.xml):

<style name="Widget.ImageButton.Custom" parent="android:style/Widget.ImageButton">
    <item name="customAttr">some value</item>
</style>

Deklarieren Sie ein benutzerdefiniertes Thema (in themes.xml):

<style name="Theme.Custom" parent="@android:style/Theme">
    <item name="customImageButtonStyle">@style/Widget.ImageButton.Custom</item>
</style>

Verwenden Sie dieses Attribut als drittes Argument im Konstruktor Ihres Widgets (in CustomImageButton.java):

public class CustomImageButton extends ImageButton {
    private String customAttr;

    public CustomImageButton( Context context ) {
        this( context, null );
    }

    public CustomImageButton( Context context, AttributeSet attrs ) {
        this( context, attrs, R.attr.customImageButtonStyle );
    }

    public CustomImageButton( Context context, AttributeSet attrs,
            int defStyle ) {
        super( context, attrs, defStyle );

        final TypedArray array = context.obtainStyledAttributes( attrs,
            R.styleable.CustomImageButton, defStyle,
            R.style.Widget_ImageButton_Custom ); // see below
        this.customAttr =
            array.getString( R.styleable.CustomImageButton_customAttr, "" );
        array.recycle();
    }
}

Jetzt müssen Sie sich Theme.Customauf alle Aktivitäten anwenden , die CustomImageButton(in AndroidManifest.xml) verwendet werden:

<activity android:name=".MyActivity" android:theme="@style/Theme.Custom"/>

Das ist alles. Jetzt CustomImageButtonversucht, Standardattributwerte aus zu laden customImageButtonStyleAttribute des aktuellen Themas. Wenn im Thema kein solches Attribut gefunden wird oder der Wert des Attributs ist, wird @nulldas letzte zu verwendende Argument obtainStyledAttributesverwendet: Widget.ImageButton.Customin diesem Fall.

Sie können die Namen aller Instanzen und aller Dateien (außer AndroidManifest.xml) ändern , es ist jedoch besser, die Android-Namenskonvention zu verwenden.

Michael
quelle
4
Gibt es eine Möglichkeit, genau dasselbe zu tun, ohne ein Thema zu verwenden? Ich habe einige benutzerdefinierte Widgets, aber ich möchte, dass Benutzer diese Widgets mit jedem gewünschten Thema verwenden können. Wenn Sie es so machen, wie Sie es gepostet haben, müssen Benutzer mein Thema verwenden.
TheDazzler
3
@theDazzler: Wenn Sie eine Bibliothek meinen, die Entwickler verwenden können, können sie ihre eigenen Themen erstellen und den Stil für das Widget mithilfe eines Stilattributs im Thema angeben ( customImageButtonStylein dieser Antwort). So wird die Aktionsleiste auf Android angepasst. Und wenn Sie ein Thema zur Laufzeit ändern möchten, ist dies mit diesem Ansatz nicht möglich.
Michael
@ Michael, ja, ich meinte eine Bibliothek, die Entwickler verwenden können. Ihr Kommentar hat mein Problem gelöst. Vielen Dank!
TheDazzler
30
Das Erstaunlichste an all dem ist, dass jemand bei Google dies für in Ordnung hält.
Glenn Maynard
1
Dient name="CustomTheme"der declare-styleableAttribut-Wrapper einem Zweck? Ist das nur für die Organisation, weil ich sehe, dass es nirgendwo verwendet wird. Kann ich einfach alle meine Stilattribute für verschiedene Widgets in einem Wrapper zusammenfassen?
Tenfour04
4

Ein weiterer Aspekt neben Michaels hervorragender Antwort ist das Überschreiben von benutzerdefinierten Attributen in Themen. Angenommen, Sie haben eine Reihe von benutzerdefinierten Ansichten, die sich alle auf das benutzerdefinierte Attribut "custom_background" beziehen.

<declare-styleable name="MyCustomStylables">
    <attr name="custom_background" format="color"/>
</declare-styleable>

In einem Thema definieren Sie den Wert

<style name="MyColorfulTheme" parent="AppTheme">
    <item name="custom_background">#ff0000</item>
</style>

oder

<style name="MyBoringTheme" parent="AppTheme">
    <item name="custom_background">#ffffff</item>
</style>

Sie können in einem Stil auf das Attribut verweisen

<style name="MyDefaultLabelStyle" parent="AppTheme">
    <item name="android:background">?background_label</item>
</style>

Beachten Sie das Fragezeichen, das auch als Referenz für das Android-Attribut verwendet wird, wie in

?android:attr/colorBackground

Wie die meisten von Ihnen bemerkt haben, können und sollten Sie @color-Referenzen anstelle von hartcodierten Farben verwenden.

Warum also nicht einfach tun?

<item name="android:background">@color/my_background_color</item>

Sie können die Definition von "my_background_color" zur Laufzeit nicht ändern, während Sie die Themen einfach wechseln können.

mir
quelle