Android: définir le style de vue par programmation

226

Voici XML:

<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    style="@style/LightStyle"
    android:layout_width="fill_parent"
    android:layout_height="55dip"
    android:clickable="true"
    android:orientation="horizontal" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal" />

</RelativeLayout>

Comment définir un styleattribut par programme?

Jim
la source
Voir ici ['réponse'] [1], cela a fonctionné pour moi. [1]: stackoverflow.com/a/5488652/2938493
Artificioo

Réponses:

211

Techniquement, vous pouvez appliquer des styles par programme, avec des vues personnalisées de toute façon:

private MyRelativeLayout extends RelativeLayout {
  public MyRelativeLayout(Context context) {
     super(context, null, R.style.LightStyle);
  }
}

Le constructeur à un argument est celui utilisé lorsque vous instanciez des vues par programme.

Alors enchaînez ce constructeur au super qui prend un paramètre de style.

RelativeLayout someLayout = new MyRelativeLayout(new ContextThemeWrapper(this,R.style.RadioButton));

Ou comme @Dori l'a souligné simplement:

RelativeLayout someLayout = new RelativeLayout(new ContextThemeWrapper(activity,R.style.LightStyle));
Blundell
la source
34
Vous pouvez simplement le faire par programme sans étendre car le constructeur 3 arg est public de toute façon developer.android.com/reference/android/widget/… , android.util.AttributeSet, int)
Dori
1
@Turbo, les deux docs et Eclipse doivent vous dire que: http://developer.android.com/reference/android/widget/LinearLayout.html#LinearLayout(android.content.Context, android.util.AttributeSet, int). Avec LinearLayout, le niveau 11+ est requis.
TheOne
2
@Dori Que feriez-vous passer pour le AttributeSet?
Blundell
9
S'il s'agit d'un TextView, vous devez utiliser setTextAppearance (R.style.small_text) pour les attributs qui affectent le texte (taille, couleur, etc.)
Maragues
2
Je ne sais pas pourquoi l'approche du troisième argument ne fonctionne pas. J'ai postulé Button. L'approche @Benjamin Piette fonctionne bien.
Youngjae
124

Ce qui a fonctionné pour moi:

Button b = new Button(new ContextThemeWrapper(this, R.style.ButtonText), null, 0);
  • Utilisez un ContextThemeWrapper

ET

  • Utilisez le constructeur à 3 arguments (ne fonctionnera pas sans cela)
Benjamin Piette
la source
L'utilisation de votre méthode fonctionne, mais j'obtiens W / ResourceType ﹕ Trop de références d'attribut, arrêté à: 0x01010034. Une solution?
HaloMediaz
Je n'ai jamais vu ce problème. Cela peut être un problème spécifique à l'appareil ou quelque chose lié à une version récente d'Android?
Benjamin Piette
11
IMPORTANT! Cette méthode fait le travail, mais établira le style pour chaque vue enfant et si la création d' une nouvelle mise en page avec le ContextThemeWrapper.
aProperFox
@aProperFox pouvez-vous mieux expliquer? With me fonctionne en ajoutant juste à un bouton. Vous vous référez à une mise en page?
Gilian
1
@Gilian exactement, si vous l'utilisez sur une vue composée qui a un ou plusieurs enfants, ils obtiendront le même style que la vue parent pour laquelle vous avez défini le style. Cela pourrait entraîner trop de remplissage ou de marge dans les vues intérieures et peut vous rendre fou si vous n'en êtes pas conscient :)
aProperFox
88

Mise à jour : au moment de répondre à cette question (mi-2012, niveau 14-15 de l'API), la définition de la vue par programmation n'était pas une option (même s'il y avait des solutions de contournement non triviales) alors que cela a été rendu possible après la dernière API versions. Voir la réponse de @ Blundell pour plus de détails.

VIEILLE réponse:

Vous ne pouvez pas encore définir le style d'une vue par programmation, mais vous pouvez trouver ce fil utile.

Korhan Ozturk
la source
16
Pas vrai. Voir la réponse de @ Blundell.
a.bertucci
31
Pour certaines situations (en utilisant TextView par exemple), vous pouvez utiliser textView.setTextAppearance(context, R.style.mystyle);. Selon le document "Définit la couleur, la taille, le style, la couleur du conseil et la couleur de surbrillance du texte à partir de la ressource TextAppearance spécifiée." developer.android.com/reference/android/widget/… , int)
Rogel Garcia
4
api> 23; textView.setTextAppearance (R.style.mystyle);
alican akyol
1
J'ai posté cette réponse il y a plus de 4 ans @HaydenKai. et l'API a beaucoup changé depuis lors, permettant d'appliquer des styles par programmation.
Korhan Ozturk
3
@KorhanOzturk a accepté, mais sur SO avec des questions comme celle-ci qui ont une activité, la question devrait être rouverte et une nouvelle réponse acceptée
HaydenKai
16

Pour un nouveau Button / TextView:

Button mMyButton = new Button(new ContextThemeWrapper(this, R.style.button_disabled), null, 0);

Pour une instance existante:

mMyButton.setTextAppearance(this, R.style.button_enabled);

Pour l'image ou les mises en page:

Image mMyImage = new ImageView(new ContextThemeWrapper(context, R.style.article_image), null, 0);
Alon Kogan
la source
8

Si vous souhaitez continuer à utiliser XML (ce que la réponse acceptée ne vous laisse pas faire) et définir le style après la création de la vue, vous pourrez peut-être utiliser la bibliothèque de Paris qui prend en charge un sous-ensemble de tous les attributs disponibles.

Puisque vous gonflez votre vue à partir de XML, vous devez spécifier un identifiant dans la mise en page:

<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/my_styleable_relative_layout"
    style="@style/LightStyle"
    ...

Ensuite, lorsque vous devez modifier le style par programme, une fois la mise en page gonflée:

// Any way to get the view instance will do
RelativeLayout myView = findViewById(R.id.my_styleable_relative_layout);

// This will apply all the supported attribute values of the style
Paris.style(myView).apply(R.style.LightStyle);

Pour en savoir plus: la liste des types et attributs de vue pris en charge (comprend l'arrière-plan, le remplissage, la marge, etc. et peut facilement être étendu) et les instructions d'installation avec une documentation supplémentaire .

Avertissement: je suis l'auteur original de ladite bibliothèque.

Nathanael
la source
Est-ce toujours une approche recommandée à utiliser en 2019?
IgorGanapolsky
1
@IgorGanapolsky afaik yes! Je n'en connais pas de meilleur.
Nathanael
Merci @Nathanael d'avoir fourni cette solution. Aussi pour en être le responsable avec le dernier attribut line_height. Continuez votre bon travail! Vraiment utile.
Guillem Roca
7

Vous pouvez appliquer un style à votre activité en faisant:

super.setTheme( R.style.MyAppTheme );

ou Android par défaut:

super.setTheme( android.R.style.Theme );

dans votre activité, avant setContentView().

FOMDeveloper
la source
4
@siemian pouvez-vous s'il vous plaît laisser FOM s'exprimer librement? Il n'a besoin d'aucune retouche!
Mika
6

Aucune des réponses fournies n'est correcte.

Vous POUVEZ définir le style par programme.

La réponse courte est de jeter un œil à http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/content/Context.java#435

Longue réponse. Voici mon extrait de code pour définir un style personnalisé défini par programme à votre vue:

1) Créez un style dans votre fichier styles.xml

 <style name="MyStyle">
    <item name="customTextColor">#39445B</item>
    <item name="customDividerColor">#8D5AA8</item>
</style>

N'oubliez pas de définir vos attributs personnalisés dans le fichier attrs.xml

Mon fichier attrsl.xml:

<declare-styleable name="CustomWidget">
    <attr name="customTextColor" format="color" />
    <attr name="customDividerColor" format="color" />
</declare-styleable>

Notez que vous pouvez utiliser n'importe quel nom pour votre styleable (mon CustomWidget)

Permet maintenant de définir le style du widget par programmation Voici mon widget simple:

public class StyleableWidget extends LinearLayout {

private final StyleLoader styleLoader = new StyleLoader();

private TextView textView;
private View divider;

public StyleableWidget(Context context) {
    super(context);
    init();
}

private void init() {
    inflate(getContext(), R.layout.widget_styleable, this);
    textView = (TextView) findViewById(R.id.text_view);
    divider = findViewById(R.id.divider);
    setOrientation(VERTICAL);
}

protected void apply(StyleLoader.StyleAttrs styleAttrs) {
    textView.setTextColor(styleAttrs.textColor);
    divider.setBackgroundColor(styleAttrs.dividerColor);
}

public void setStyle(@StyleRes int style) {
    apply(styleLoader.load(getContext(), style));
}
}

disposition:

<TextView
    android:id="@+id/text_view"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textSize="22sp"
    android:layout_gravity="center"
    android:text="@string/styleble_title" />

<View
    android:id="@+id/divider"
    android:layout_width="match_parent"
    android:layout_height="1dp"/>

</merge>

Et enfin l'implémentation de la classe StyleLoader

public class StyleLoader {

public StyleLoader() {

}

public static class StyleAttrs {
    public int textColor;
    public int dividerColor;
}

public StyleAttrs load(Context context, @StyleRes int styleResId) {
    final TypedArray styledAttributes = context.obtainStyledAttributes(styleResId, R.styleable.CustomWidget);
    return load(styledAttributes);
}

@NonNull
private StyleAttrs load(TypedArray styledAttributes) {
    StyleAttrs styleAttrs = new StyleAttrs();
    try {
        styleAttrs.textColor = styledAttributes.getColor(R.styleable.CustomWidget_customTextColor, 0);
        styleAttrs.dividerColor = styledAttributes.getColor(R.styleable.CustomWidget_customDividerColor, 0);
    } finally {
        styledAttributes.recycle();
    }
    return styleAttrs;
}
}

Vous pouvez trouver un exemple pleinement fonctionnel sur https://github.com/Defuera/SetStylableProgramematic

Defuera
la source
13
Ce n'est pas un vrai style, c'est juste un faux en appliquant / définissant les paramètres enregistrés (oui, il s'agit en fait d'une sorte de paramètres enregistrés dans votre fichier XML) sur vos vues (ici, vous n'avez que 2 vues fixes - et vous devez sûrement les connaître préalablement). Toute la magie est dans la applyméthode qui ne fait rien d'intéressant. La véritable signification du style est d' appliquer automatiquement un style visuel à toutes les futures vues ajoutées dynamiquement. Ce code ici ne peut bien sûr pas faire cela et il n'y a rien de dynamique ici, nous devons savoir quelles vues et quelles propriétés / champs définir.
King King
1
Cela ne me dérange pas de spécifier à quelles vues appliquer les styles, mais cette solution a les éléments de style codés en dur (par exemple textColor et dividerColor). Il ne trouvera pas dynamiquement tous les éléments définis dans le style et ne les appliquera pas à la vue.
nasch
Le lien que vous avez publié ne s'ouvre pas. Pouvez-vous s'il vous plaît republier?
IgorGanapolsky
4

C'est une question assez ancienne mais la solution qui a fonctionné pour moi maintenant consiste à utiliser le 4ème paramètre du constructeur defStyleRes- si disponible .. à l'écran ... pour définir le style

Oeuvres suivantes à mes fins (kotlin):

val textView = TextView(context, null, 0, R.style.Headline1)
enveloppe convexe
la source
3

Ceci est mon exemple simple, la clé est le ContextThemeWrapperwrapper, sans elle, mon style ne fonctionne pas et utilise le constructeur à trois paramètres de la vue.

ContextThemeWrapper themeContext = new ContextThemeWrapper(this, R.style.DefaultLabelStyle);
TextView tv = new TextView(themeContext, null, 0);
tv.setText("blah blah ...");
layout.addView(tv);
guogangj
la source
2

la manière simple passe par le constructeur

RadioButton radioButton = new RadioButton(this,null,R.style.radiobutton_material_quiz);
Sai Gopi N
la source
Un peu plus d'illustration sur la façon d'utiliser cette solution serait utile.
IgorGanapolsky
1

Je ne propose pas d'utiliser ContextThemeWrapper car il fait ceci:

Le thème spécifié sera appliqué au-dessus du thème du contexte de base.

Ce qui peut entraîner des résultats indésirables dans votre application. Au lieu de cela, je propose une nouvelle bibliothèque "paris" pour cela des ingénieurs d'Airbnb:

https://github.com/airbnb/paris

Définissez et appliquez des styles aux vues Android par programmation.

Mais après un certain temps d'utilisation, j'ai découvert qu'il était en fait assez limité et j'ai cessé de l'utiliser car il ne prend pas en charge beaucoup de propriétés dont j'ai besoin hors de la boîte, donc il faut vérifier et décider comme toujours.

Renetik
la source
Expliquez votre ami downvote ... J'ai perdu un jour et demi parce que j'ai utilisé ContextThemeWrapper, et il s'est appliqué à mon fond blanc de thème de contexte, puis tout à coup le widget Chip de la bibliothèque de matériaux Google s'est écrasé, devinez pourquoi ...
Renetik
La bibliothèque de Paris n'utilise-t-elle pas ContextThemeWrapper en dessous?
IgorGanapolsky
@IgorGanapolsky ce n'est pas le cas. Il lit le style XML et le convertit en appels de méthode correspondants sur la vue.
Nathanael
C'est en fait assez limité, j'ai arrêté de l'utiliser car il ne prend pas en charge beaucoup de propriétés dont j'avais besoin ...
Renetik
-1

J'ai utilisé des vues définies en XML dans mon ViewGroup composite, les ai gonflées ajoutées à Viewgroup. De cette façon, je ne peux pas changer dynamiquement le style mais je peux faire quelques personnalisations de style. Mon composite:

public class CalendarView extends LinearLayout {

private GridView mCalendarGrid;
private LinearLayout mActiveCalendars;

private CalendarAdapter calendarAdapter;

public CalendarView(Context context) {
    super(context);

}

public CalendarView(Context context, AttributeSet attrs) {
    super(context, attrs);

}

@Override
protected void onFinishInflate() {
    super.onFinishInflate();
    init();
}

private void init() {
    mCalendarGrid = (GridView) findViewById(R.id.calendarContents);
    mCalendarGrid.setNumColumns(CalendarAdapter.NUM_COLS);

    calendarAdapter = new CalendarAdapter(getContext());
    mCalendarGrid.setAdapter(calendarAdapter);
    mActiveCalendars = (LinearLayout) findViewById(R.id.calendarFooter);
}

}

et ma vue en xml où je peux assigner des styles:

<com.mfitbs.android.calendar.CalendarView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/calendar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:orientation="vertical"
>

<GridView
    android:id="@+id/calendarContents"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

<LinearLayout
    android:id="@+id/calendarFooter"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    />

user3918502
la source
1
Où définissez-vous dynamiquement le style?
IgorGanapolsky
-3

Vous pouvez créer le xml contenant la mise en page avec le style souhaité, puis modifier la ressource d'arrière-plan de votre vue, comme ceci .

rfsbraz
la source
Vous parlez plutôt d'un "style" comme dans Android, mais de "l'aspect" d'un bouton. c'est très différent.
Christ