2015-02-15 17 views
10

Sto provando a tema la mia app in Android. Tuttavia, ogni widget è un dolore esasperante di per sé: devo cercare di definire quel particolare widget e quindi creare uno stile che si spera possa derivare dallo stesso stile utilizzato dal widget.Come esplorare lo stile in Android

Ovviamente, le risposte sul tema di un particolare widget non sempre contengono informazioni sullo stile di base, solo i colori particolari.

Quindi, invece di accettare il pesce da mangiare, puoi insegnarmi a pescare invece?

Come si interpretano quelle chiamate ObtainStyledAttributes() nei costruttori di widget e si estrae gli stili da quello? Come faccio a reclamarlo?

In particolare, puoi indicarmi il colore del pulsante AlertDialog? Quale stile definisce il pulsante piatto lecca-lecca + il colore del testo verde acqua? Come posso ottenere quello stile se inizio da AlertDialog source e ObtainStyledAttributes call?

risposta

27

Trovo che lo stile riguardi lo sherlocking attraverso la struttura. Il quello che (quasi sempre) deriva dall'implementazione del widget. Il dove, trovo è dappertutto. Farò del mio meglio per spiegare il processo attraverso il tuo caso d'uso particolare: i pulsanti di AlertDialog.

Partendo:

Hai già capito questo: si parte con il codice sorgente del widget. Stiamo cercando in particolare di trovare - dove i pulsanti AlertDialog ottengono il loro colore del testo. Quindi, iniziamo guardando da dove provengono questi pulsanti. Sono stati creati esplicitamente in fase di runtime? O sono definiti in un layout xml, che viene gonfiato?

Nel codice sorgente, troviamo che mAlert gestisce le opzioni dei pulsanti tra le altre cose:

public void setButton(int whichButton, CharSequence text, Message msg) { 
    mAlert.setButton(whichButton, text, null, msg); 
} 

mAlert è un'istanza di AlertController. Nel suo costruttore, troviamo che l'attributo alertDialogStyle definisce il layout xml:

TypedArray a = context.obtainStyledAttributes(null, 
      com.android.internal.R.styleable.AlertDialog, 
      com.android.internal.R.attr.alertDialogStyle, 0); 

    mAlertDialogLayout = 
      a.getResourceId(
      com.android.internal.R.styleable.AlertDialog_layout, 
      com.android.internal.R.layout.alert_dialog); 

Così, il layout dovremmo guardare è alert_dialog.xml-[sdk_folder]/platforms/android-21/data/res/layout/alert_dialog.xml:

Il layout XML è piuttosto lungo. Questa è la parte rilevante:

<LinearLayout> 

.... 
.... 

<LinearLayout android:id="@+id/buttonPanel" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:minHeight="54dip" 
    android:orientation="vertical" > 
    <LinearLayout 
     style="?android:attr/buttonBarStyle" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:orientation="horizontal" 
     android:paddingTop="4dip" 
     android:paddingStart="2dip" 
     android:paddingEnd="2dip" 
     android:measureWithLargestChild="true"> 
     <LinearLayout android:id="@+id/leftSpacer" 
      android:layout_weight="0.25" 
      android:layout_width="0dip" 
      android:layout_height="wrap_content" 
      android:orientation="horizontal" 
      android:visibility="gone" /> 
     <Button android:id="@+id/button1" 
      android:layout_width="0dip" 
      android:layout_gravity="start" 
      android:layout_weight="1" 
      style="?android:attr/buttonBarButtonStyle" 
      android:maxLines="2" 
      android:layout_height="wrap_content" /> 
     <Button android:id="@+id/button3" 
      android:layout_width="0dip" 
      android:layout_gravity="center_horizontal" 
      android:layout_weight="1" 
      style="?android:attr/buttonBarButtonStyle" 
      android:maxLines="2" 
      android:layout_height="wrap_content" /> 
     <Button android:id="@+id/button2" 
      android:layout_width="0dip" 
      android:layout_gravity="end" 
      android:layout_weight="1" 
      style="?android:attr/buttonBarButtonStyle" 
      android:maxLines="2" 
      android:layout_height="wrap_content" /> 
     <LinearLayout android:id="@+id/rightSpacer" 
      android:layout_width="0dip" 
      android:layout_weight="0.25" 
      android:layout_height="wrap_content" 
      android:orientation="horizontal" 
      android:visibility="gone" /> 
    </LinearLayout> 

Ora sappiamo che i pulsanti ottenere lo stile tenuto da attributo buttonBarButtonStyle.

oltre al capo [sdk_folder]/platforms/android-21/data/res/values/themes.material.xml e cercare buttonBarButtonStyle:

<!-- Defined under `<style name="Theme.Material">` --> 
<item name="buttonBarButtonStyle">@style/Widget.Material.Button.ButtonBar.AlertDialog</item> 

<!-- Defined under `<style name="Theme.Material.Light">` --> 
<item name="buttonBarButtonStyle">@style/Widget.Material.Light.Button.ButtonBar.AlertDialog</item> 

A seconda di quale tema genitore la vostra attività è, buttonBarButtonStyle farà riferimento a uno di questi due stili. Per ora, supponiamo che il tema della tua attività si estenda su Theme.Material.Vedremo @style/Widget.Material.Button.ButtonBar.AlertDialog:

Aprire [sdk_folder]/platforms/android-21/data/res/values/styles_material.xml e cercare Widget.Material.Button.ButtonBar.AlertDialog:

<!-- Alert dialog button bar button --> 
<style name="Widget.Material.Button.ButtonBar.AlertDialog" parent="Widget.Material.Button.Borderless.Colored"> 
    <item name="minWidth">64dp</item> 
    <item name="maxLines">2</item> 
    <item name="minHeight">@dimen/alert_dialog_button_bar_height</item> 
</style> 

Va bene. Ma questi valori non ci aiutano a determinare il colore del testo del pulsante. Dovremmo guardare lo stile principale successivo - Widget.Material.Button.Borderless.Colored:

<!-- Colored borderless ink button --> 
<style name="Widget.Material.Button.Borderless.Colored"> 
    <item name="textColor">?attr/colorAccent</item> 
    <item name="stateListAnimator">@anim/disabled_anim_material</item> 
</style> 

Infine, troviamo il textColor - e la sua forniti da attr/colorAccent inizializzate in Theme.Material:

<item name="colorAccent">@color/accent_material_dark</item> 

Per Theme.Material.Light, colorAccent è definito come:

<item name="colorAccent">@color/accent_material_light</item> 

Vai a [sdk_folder]/platforms/android-21/data/res/values/colors_material.xml e individuare th colori ESE:

<color name="accent_material_dark">@color/material_deep_teal_200</color> 
<color name="accent_material_light">@color/material_deep_teal_500</color> 

<color name="material_deep_teal_200">#ff80cbc4</color> 
<color name="material_deep_teal_500">#ff009688</color> 

Schermata di un AlertDialog e il corrispondente del testo-color:

enter image description here

rapida:

A volte, è più facile da leggere il valore del colore (come in l'immagine sopra) e cercarla usando AndroidXRef. Questo approccio non sarebbe stato utile nel tuo caso dal #80cbc4 avrebbe solo sottolineato che è il colore dell'accento. Dovresti comunque individuare Widget.Material.Button.Borderless.Colored e legarlo con l'attributo buttonBarButtonStyle.

Modifica del pulsante di testo-colore:

Idealmente, dovremmo creare uno stile che si estende Widget.Material.Button.ButtonBar.AlertDialog, ignorare android:textColor al suo interno, e assegnarlo ad attribuire buttonBarButtonStyle. Ma questo non funzionerà - il tuo progetto non verrà compilato. Questo perché Widget.Material.Button.ButtonBar.AlertDialog è uno stile non pubblico e, quindi, non può essere esteso. Puoi confermarlo selezionando Link.

Faremo la cosa migliore: estendere lo stile principale di Widget.Material.Button.ButtonBar.AlertDialog - Widget.Material.Button.Borderless.Colored che è pubblico.

<style name="CusButtonBarButtonStyle" 
     parent="@android:style/Widget.Material.Button.Borderless.Colored"> 
    <!-- Yellow --> 
    <item name="android:textColor">#ffffff00</item> 

    <!-- From Widget.Material.Button.ButtonBar.AlertDialog --> 
    <item name="android:minWidth">64dp</item> 
    <item name="android:maxLines">2</item> 
    <item name="android:minHeight">@dimen/alert_dialog_button_bar_height</item> 
</style> 

Si noti che aggiungiamo altri 3 elementi dopo aver annullato android:textColor. Questi sono di tipo non pubblico Widget.Material.Button.ButtonBar.AlertDialog. Dal momento che non possiamo estenderlo direttamente, dobbiamo includere gli elementi che definisce. Nota: il valore dimen deve essere esaminato e trasferito nei file appropriati res/values(-xxxxx)/dimens.xml nel progetto.

Lo stile CusButtonBarButtonStyle verrà assegnato all'attributo buttonBarButtonStyle. Ma la domanda è: come farà a sapere un AlertDialog?Dal codice sorgente:

protected AlertDialog(Context context) { 
    this(context, resolveDialogTheme(context, 0), true); 
} 

Passando 0 come secondo argomento per resolveDialogTheme(Context, int) finirà nella clausola else:

static int resolveDialogTheme(Context context, int resid) { 
    if (resid == THEME_TRADITIONAL) { 
     .... 
    } else { 
     TypedValue outValue = new TypedValue(); 
     context.getTheme().resolveAttribute(
       com.android.internal.R.attr.alertDialogTheme, 
       outValue, true); 
     return outValue.resourceId; 
    } 
} 

Ora sappiamo che il tema è detenuto da attributo alertDialogTheme. Successivamente, guardiamo a ciò che indica alertDialogTheme. Il valore di questo attributo dipenderà dal tema principale della tua attività. Passare alla cartella sdk e trovare il values/themes_material.xml all'interno di Android-21. Cerca alertDialogTheme. Risultati:

<!-- Defined under `<style name="Theme.Material">` --> 
<item name="alertDialogTheme">@style/Theme.Material.Dialog.Alert</item> 

<!-- Defined under `<style name="Theme.Material.Light">` --> 
<item name="alertDialogTheme">@style/Theme.Material.Light.Dialog.Alert</item> 

<!-- Defined under `<style name="Theme.Material.Settings">` --> 
<item name="alertDialogTheme">@style/Theme.Material.Settings.Dialog.Alert</item> 

Quindi, sulla base di ciò tema di base della vostra attività è, alertDialogTheme terrà uno di questi 3 valori. Per far sapere ad AlertDialog di CusButtonBarButtonStyle, dobbiamo sostituire l'attributo alertDialogTheme nel tema della nostra app. Supponiamo che stiamo usando Theme.Material come tema di base.

<style name="AppTheme" parent="android:Theme.Material"> 
    <item name="android:alertDialogTheme">@style/CusAlertDialogTheme</item> 
</style> 

Dall'alto, sappiamo che alertDialogTheme punti per Theme.Material.Dialog.Alertquando tema di base della tua app è Theme.Material. Così, CusAlertDialogTheme dovrebbe avere Theme.Material.Dialog.Alert come suo genitore:

<style name="CusAlertDialogTheme" 
     parent="android:Theme.Material.Dialog.Alert"> 
    <item name="android:buttonBarButtonStyle">@style/CusButtonBarButtonStyle</item> 
</style> 

Risultato:

enter image description here

Così, invece di accettare pesce da mangiare, mi puoi insegnare a pescare invece?

Per lo meno, spero di aver spiegato dove sono i pesci.

P.S. Mi rendo conto di aver postato un mammut.

+2

WOW, solo WOW. È il casino che ho immaginato che fosse. Google ha ancora bisogno di imparare molto sullo sviluppo dell'API :(Almeno Materiale cerca di risolvere questi problemi Ora, per vedere se riesco a tradurre quello che ho imparato qui sul tema Appcompat che sto usando attualmente. – velis

+1

@velis lol, quindi vero. Fatemi sapere se siete stati in grado di utilizzare questo approccio nella personalizzazione del tema Appcompat.Se colpisci un muro, possiamo discutere ulteriormente. A proposito, ottima domanda, e grazie per la generosità. – Vikram

+0

su android-24 il file dei temi è themes_material.xml – dazza5000

0

Oltre alla risposta eccellente @Vikram, vale la pena notare che Android Studio può semplificare enormemente il tuo lavoro. Hai solo bisogno di passare il mouse sopra il tema, mostrerà qualcosa come seguire.

actionBarStyle = @style/Widget.AppCompat.Light.ActionBar.Solid 
=> @style/Widget.AppCompat.Light.ActionBar.Solid 

È inoltre possibile utilizzare clic del mouse per navigare tra gli stili, come quello che si fa con il normale codice Java.

E si possono trovare res della biblioteca supporto <sdk root>/extras/android/m2repository/com/android/support/<support library name>/<version number>/<support library>.aar/res

Ma la *.aar/res/values/values.xml contiene tutti i valori, e non è di facile lettura. È possibile ottenere il codice e le risorse originali della libreria di supporto in https://android.googlesource.com/platform/frameworks/support/+/master

C'è un pulsante denominato tgz per scaricare l'istantanea corrente.