Procedura: definire l'elemento del tema (stile) per il widget personalizzato


86

Ho scritto un widget personalizzato per un controllo che utilizziamo ampiamente in tutta la nostra applicazione. La classe widget deriva da ImageButtone la estende in un paio di semplici modi. Ho definito uno stile che posso applicare al widget mentre viene utilizzato, ma preferisco impostarlo tramite un tema. In R.styleablevedo attributi di stile widget come imageButtonStylee textViewStyle. C'è un modo per creare qualcosa del genere per il widget personalizzato che ho scritto?

Risposte:


209

Sì, c'è un modo:

Supponi di avere una dichiarazione di attributi per il tuo widget (in attrs.xml):

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

Dichiara un attributo che utilizzerai per un riferimento allo stile (in attrs.xml):

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

Dichiarare un insieme di valori di attributi predefiniti per il widget (in styles.xml):

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

Dichiara un tema personalizzato (in themes.xml):

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

Usa questo attributo come terzo argomento nel costruttore del tuo widget (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();
    }
}

Ora devi applicare Theme.Customa tutte le attività che utilizzano CustomImageButton(in AndroidManifest.xml):

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

È tutto. Ora CustomImageButtonprova a caricare i valori degli attributi predefiniti dall'attributo customImageButtonStyledel tema corrente. Se non viene trovato alcun attributo di questo tipo nel tema o nel valore dell'attributo, verrà utilizzato @nulll'argomento finale di obtainStyledAttributes: Widget.ImageButton.Customin questo caso.

Puoi cambiare i nomi di tutte le istanze e di tutti i file (eccetto AndroidManifest.xml) ma sarebbe meglio usare la convenzione di denominazione Android.


4
C'è un modo per fare esattamente la stessa cosa senza usare un tema? Ho alcuni widget personalizzati, ma voglio che gli utenti siano in grado di utilizzare questi widget con qualsiasi tema desiderino. Farlo nel modo in cui hai pubblicato richiederebbe agli utenti di utilizzare il mio tema.
theDazzler

3
@theDazzler: se intendi una libreria che gli sviluppatori possono utilizzare, saranno in grado di creare i propri temi e specificare lo stile per il widget utilizzando un attributo di stile nel tema ( customImageButtonStylein questa risposta). Ecco come viene personalizzata la barra delle azioni su Android. E se intendi cambiare un tema in fase di esecuzione, non è possibile con questo approccio.
Michael

@ Michael, Sì, intendevo una libreria che gli sviluppatori possono utilizzare. Il tuo commento ha risolto il mio problema. Grazie!
theDazzler

30
La cosa più sorprendente di tutto questo è che qualcuno in Google pensa che vada bene.
Glenn Maynard

1
Fa name="CustomTheme"in declare-styleableattributo involucro servire uno scopo? È solo per l'organizzazione, perché non lo vedo usato da nessuna parte. Posso semplicemente mettere tutti i miei attributi di stile per vari widget in un unico wrapper?
Tenfour04

4

Un altro aspetto oltre all'eccellente risposta di michael è l'override degli attributi personalizzati nei temi. Supponi di avere un numero di visualizzazioni personalizzate che fanno tutte riferimento all'attributo personalizzato "custom_background".

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

In un tema definisci qual è il valore

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

o

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

Puoi fare riferimento all'attributo in uno stile

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

Notare il punto interrogativo, utilizzato anche per l'attributo Android di riferimento come in

?android:attr/colorBackground

Come molti di voi avranno notato, si possono, e probabilmente si dovrebbero, usare riferimenti @color invece di colori codificati.

Allora perché non farlo e basta

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

Non è possibile modificare la definizione di "my_background_color" in fase di esecuzione, mentre è possibile cambiare facilmente i temi.

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.