Dichiarazione di un elemento dell'interfaccia utente Android personalizzata tramite XML


Risposte:


840

La Guida per gli sviluppatori Android ha una sezione chiamata Creazione di componenti personalizzati . Sfortunatamente, la discussione sugli attributi XML riguarda solo la dichiarazione del controllo all'interno del file di layout e non la gestione dei valori all'interno dell'inizializzazione della classe. I passi sono come segue:

1. Dichiarare gli attributi in values\attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyCustomView">
        <attr name="android:text"/>
        <attr name="android:textColor"/>            
        <attr name="extraInformation" format="string" />
    </declare-styleable>
</resources>

Si noti l'uso di un nome non qualificato nel declare-styleabletag. Gli attributi Android non standard come extraInformationdevono essere dichiarati. I tag dichiarati nella superclasse saranno disponibili nelle sottoclassi senza dover essere nuovamente dichiarati.

2. Creare costruttori

Poiché ci sono due costruttori che usano un'inizializzazione AttributeSet, è conveniente creare un metodo di inizializzazione separato che i costruttori possano chiamare.

private void init(AttributeSet attrs) { 
    TypedArray a=getContext().obtainStyledAttributes(
         attrs,
         R.styleable.MyCustomView);

    //Use a
    Log.i("test",a.getString(
         R.styleable.MyCustomView_android_text));
    Log.i("test",""+a.getColor(
         R.styleable.MyCustomView_android_textColor, Color.BLACK));
    Log.i("test",a.getString(
         R.styleable.MyCustomView_extraInformation));

    //Don't forget this
    a.recycle();
}

R.styleable.MyCustomViewè una int[]risorsa generata automaticamente in cui ogni elemento è l'ID di un attributo. Gli attributi vengono generati per ogni proprietà nell'XML aggiungendo il nome dell'attributo al nome dell'elemento. Ad esempio, R.styleable.MyCustomView_android_textcontiene l' android_textattributo per MyCustomView. Gli attributi possono quindi essere recuperati TypedArrayutilizzando varie getfunzioni. Se l'attributo non è definito nel definito nell'XML, nullviene restituito. Tranne, ovviamente, se il tipo restituito è una primitiva, nel qual caso viene restituito il secondo argomento.

Se non vuoi recuperare tutti gli attributi, è possibile creare questo array manualmente. L'ID per gli attributi Android standard è incluso android.R.attr, mentre gli attributi per questo progetto sono presenti R.attr.

int attrsWanted[]=new int[]{android.R.attr.text, R.attr.textColor};

Si prega di notare che è necessario non usare nulla in android.R.styleable, come da questa discussione potrebbe cambiare in futuro. È ancora nella documentazione come essere utile visualizzare tutte queste costanti in un unico punto.

3. Usalo in un file di layout come layout\main.xml

Includi la dichiarazione dello spazio dei nomi xmlns:app="http://schemas.android.com/apk/res-auto"nell'elemento xml di livello superiore. Gli spazi dei nomi forniscono un metodo per evitare i conflitti che a volte si verificano quando schemi diversi usano gli stessi nomi di elementi (vedi questo articolo per maggiori informazioni). L'URL è semplicemente un modo per identificare in modo univoco gli schemi - in realtà non è necessario ospitare nulla in quell'URL . Se questo non sembra fare nulla, è perché in realtà non è necessario aggiungere il prefisso dello spazio dei nomi a meno che non sia necessario risolvere un conflitto.

<com.mycompany.projectname.MyCustomView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@android:color/transparent"
    android:text="Test text"
    android:textColor="#FFFFFF"
    app:extraInformation="My extra information"
/> 

Fare riferimento alla vista personalizzata utilizzando il nome completo.

Esempio Android LabelView

Se vuoi un esempio completo, guarda l'esempio di visualizzazione dell'etichetta Android.

LabelView.java

 TypedArray a=context.obtainStyledAttributes(attrs, R.styleable.LabelView);
 CharSequences=a.getString(R.styleable.LabelView_text);

attrs.xml

<declare-styleable name="LabelView">
    <attr name="text"format="string"/>
    <attr name="textColor"format="color"/>
    <attr name="textSize"format="dimension"/>
</declare-styleable>

custom_view_1.xml

<com.example.android.apis.view.LabelView
    android:background="@drawable/blue"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    app:text="Blue" app:textSize="20dp"/>

Questo è contenuto in a LinearLayoutcon l'attributo namespace:xmlns:app="http://schemas.android.com/apk/res-auto"

link


14
Vorrei aggiungere che se il tuo elemento radice richiede il tuo spazio dei nomi personalizzato, dovrai aggiungere sia lo spazio dei nomi Android standard che quello personalizzato, altrimenti potresti riscontrare errori di costruzione.
Insegui il

11
Questa risposta è la risorsa più chiara su Internet su parametri XML personalizzati che sono riuscito a trovare. Grazie Casebash.
Artem Russakovskii,

2
per qualche motivo, l'editor visuale si rifiuta di utilizzare il valore del testo scritto per Android: testo, ma il dispositivo lo usa bene. come mai ?
sviluppatore Android

2
@androiddeveloper Sembra che l'editor Eclipse si rifiuti di utilizzare i valori per tutti gli attributi android:. Vorrei sapere se si tratta di una caratteristica o di un bug
deej

4
Qual è lo scopo di xmlns: spazio dei nomi delle app e res-auto?
IgorGanapolsky,

91

Ottimo riferimento Grazie! Un'aggiunta ad esso:

Se ti capita di includere un progetto di biblioteca che ha dichiarato attributi personalizzati per una vista personalizzata, devi dichiarare il tuo spazio dei nomi del progetto, non quello della biblioteca. Per esempio:

Dato che la libreria ha il pacchetto "com.example.library.customview" e il progetto di lavoro ha il pacchetto "com.example.customview", quindi:

Non funzionerà (mostra l'errore "errore: nessun identificatore di risorsa trovato per l'attributo 'newAttr' nel pacchetto 'com.example.library.customview'"):

<com.library.CustomView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res/com.example.library.customview"
        android:id="@+id/myView"
        app:newAttr="value" />

Funzionerà:

<com.library.CustomView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res/com.example.customview"
        android:id="@+id/myView"
        app:newAttr="value" />

47
Questo problema è stato risolto nell'anteprima di ADT 17. Per utilizzare lo spazio dei nomi dell'app dalla libreria dichiarare xmlns:app="http://schemas.android.com/apk/res-auto"Vedi il commento 57 in code.google.com/p/android/issues/detail?id=9656
nmr

2
L'inclusione dello spazio dei nomi personalizzato ora restituisce un erroreSuspicious namespace: Did you mean http://schemas.android.com/apk/res-auto
Ben Wilkinson,

lo spazio dei nomi personalizzato termina in res-auto perché stiamo usando Android Studio e Gradle. Altrimenti (ad esempio alcune versioni di Eclipse) finirebbe normalmente in lib / [nome del pacchetto]
Universo

lo spazio dei nomi personalizzato termina res-autoperché stiamo utilizzando Android Studio e Gradle. Altrimenti (ad esempio alcune versioni di Eclipse) di solito finisce lib/[your package name]. vale a direhttp://schemas.android.com/apk/lib/[your package name]
Universo

27

Aggiunta alla risposta più votata.

obtainStyledAttributes ()

Voglio aggiungere alcune parole sull'utilizzo di getStyledAttributes (), quando creiamo una vista personalizzata utilizzando gli attributi predefiniti android: xxx. Soprattutto quando usiamo TextAppearance.
Come menzionato in "2. Creazione di costruttori", la vista personalizzata ottiene AttributeSet alla sua creazione. Utilizzo principale che possiamo vedere nel codice sorgente di TextView (API 16).

final Resources.Theme theme = context.getTheme();

// TextAppearance is inspected first, but let observe it later

TypedArray a = theme.obtainStyledAttributes(
            attrs, com.android.internal.R.styleable.TextView, defStyle, 0);

int n = a.getIndexCount();
for (int i = 0; i < n; i++) 
{
    int attr = a.getIndex(i);
    // huge switch with pattern value=a.getXXX(attr) <=> a.getXXX(a.getIndex(i))
}
a.recycle();

Cosa possiamo vedere qui?
obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
Il set di attributi viene elaborato per tema in base alla documentazione. I valori degli attributi vengono compilati passo dopo passo. I primi attributi vengono riempiti dal tema, quindi i valori vengono sostituiti dai valori dallo stile e infine i valori esatti dall'XML per l'istanza della vista speciale sostituiscono gli altri.
Matrice di attributi richiesti: com.android.internal.R.styleable.TextView
è una matrice ordinaria di costanti. Se stiamo richiedendo attributi standard, possiamo creare questo array manualmente.

Cosa non è menzionato nella documentazione - ordine del risultato Elementi TypedArray.
Quando la vista personalizzata viene dichiarata in attrs.xml, vengono generate costanti speciali per gli indici degli attributi. E possiamo estrarre i valori in questo modo: a.getString(R.styleable.MyCustomView_android_text). Ma per il manuale int[]non ci sono costanti. Suppongo che getXXXValue (arrayIndex) funzionerà bene.

E l'altra domanda è: "Come possiamo sostituire le costanti interne e richiedere attributi standard?" Possiamo usare i valori android.R.attr. *.

Quindi, se vogliamo usare l'attributo TextAppearance standard nella vista personalizzata e leggerne i valori nel costruttore, possiamo modificare il codice da TextView in questo modo:

ColorStateList textColorApp = null;
int textSize = 15;
int typefaceIndex = -1;
int styleIndex = -1;

Resources.Theme theme = context.getTheme();

TypedArray a = theme.obtainStyledAttributes(attrs, R.styleable.CustomLabel, defStyle, 0);
TypedArray appearance = null;
int apResourceId = a.getResourceId(R.styleable.CustomLabel_android_textAppearance, -1);
a.recycle();
if (apResourceId != -1)
{
    appearance = 
        theme.obtainStyledAttributes(apResourceId, new int[] { android.R.attr.textColor, android.R.attr.textSize, 
            android.R.attr.typeface, android.R.attr.textStyle });
}
if (appearance != null)
{
    textColorApp = appearance.getColorStateList(0);
    textSize = appearance.getDimensionPixelSize(1, textSize);
    typefaceIndex = appearance.getInt(2, -1);
    styleIndex = appearance.getInt(3, -1);

    appearance.recycle();
}

Dove si definisce CustomLabel:

<declare-styleable name="CustomLabel">
    <!-- Label text. -->
    <attr name="android:text" />
    <!-- Label text color. -->
    <attr name="android:textColor" />
    <!-- Combined text appearance properties. -->
    <attr name="android:textAppearance" />
</declare-styleable>

Forse, mi sbaglio in qualche modo, ma la documentazione Android su getStyledAttributes () è molto scarsa.

Estensione del componente UI standard

Allo stesso tempo, possiamo semplicemente estendere il componente UI standard, usando tutti i suoi attributi dichiarati. Questo approccio non è così buono, perché TextView ad esempio dichiara molte proprietà. E sarà impossibile implementare la piena funzionalità in overriden onMeasure () e onDraw ().

Ma possiamo sacrificare l'ampio riutilizzo teorico del componente personalizzato. Dì "So esattamente quali funzionalità userò" e non condivido il codice con nessuno.

Quindi possiamo implementare il costruttore CustomComponent(Context, AttributeSet, defStyle). Dopo aver chiamato super(...)avremo tutti gli attributi analizzati e disponibili attraverso metodi getter.


Android: xxx attributi predefiniti funzionano in eclipse gui designer?
deej

Tali attributi sono riconosciuti dal plug-in ADT Eclipse nell'editor delle proprietà. Vedo le impostazioni predefinite dal mio stile, se alcuni valori non sono definiti. E non dimenticare di aggiungere l'annotazione @RemoteView alla tua classe.
yuriy.weiss,

Non riesco a farlo funzionare. Eclipse continua a caricare null per getText e a lanciare android.content.res.Resources $ NotFoundException per getResourceId, sebbene l'app funzioni bene su un dispositivo.
deej

Scusa, non posso aiutarti. Ho creato solo progetti dimostrativi per testare le possibilità e non ho riscontrato tali errori.
yuriy.weiss

È molto meglio che associare gli attributi personalizzati di una vista personalizzata agli attributi incorporati delle viste integrate contenute all'interno.
samis,

13

Sembra che Google abbia aggiornato la sua pagina degli sviluppatori e vi abbia aggiunto vari corsi di formazione.

Uno di questi riguarda la creazione di viste personalizzate e può essere trovato qui


5

Grazie mille per la prima risposta.

Quanto a me, ho avuto solo un problema. Quando gonfiavo la mia vista, avevo un bug: java.lang.NoSuchMethodException: MyView (Context, Attributes)

L'ho risolto creando un nuovo costruttore:

public MyView(Context context, AttributeSet attrs) {
     super(context, attrs);
     // some code
}

Spero che questo possa aiutare!


0

Puoi includere qualsiasi file di layout in altri file di layout come-

             <RelativeLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="30dp" >

                <include
                    android:id="@+id/frnd_img_file"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    layout="@layout/include_imagefile"/>

                <include
                    android:id="@+id/frnd_video_file"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    layout="@layout/include_video_lay" />

                <ImageView
                    android:id="@+id/downloadbtn"
                    android:layout_width="30dp"
                    android:layout_height="30dp"
                    android:layout_centerInParent="true"
                    android:src="@drawable/plus"/>
            </RelativeLayout>

qui i file di layout nel tag include sono altri file di layout .xml nella stessa cartella res.


Ho provato questo, il problema che ho è che il layout incluso non può essere "adattato", non può creare generici. Ad esempio, quando includo un pulsante in modo simile, se provo a impostare il testo nell'xml, funziona.
cfl
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.