Android: establece el estilo de vista mediante programación

226

Aquí está 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>

¿Cómo establecer styleatributo mediante programación?

Jim
fuente
Ver aquí ['respuesta'] [1], esto funcionó para mí. [1]: stackoverflow.com/a/5488652/2938493
Artificioo

Respuestas:

211

Técnicamente, puede aplicar estilos mediante programación, con vistas personalizadas de todos modos:

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

El constructor de un argumento es el que se usa cuando crea instancias de vistas mediante programación.

Así que encadena este constructor al super que toma un parámetro de estilo.

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

O como @Dori señaló simplemente:

RelativeLayout someLayout = new RelativeLayout(new ContextThemeWrapper(activity,R.style.LightStyle));
Blundell
fuente
34
Podrías hacer esto programáticamente sin extender ya que el constructor de 3 argumentos es público de todos modos developer.android.com/reference/android/widget/… , android.util.AttributeSet, int)
Dori
1
@Turbo, tanto los documentos y Eclipse debe decirle que: http://developer.android.com/reference/android/widget/LinearLayout.html#LinearLayout(android.content.Context, android.util.AttributeSet, int). Con LinearLayout se requiere nivel 11+.
TheOne
2
@Dori ¿Qué pasarías por el AttributeSet?
Blundell
99
Si es TextView, debe usar setTextAppearance (R.style.small_text) para aquellos atributos que afectan el texto (tamaño, color, etc.)
Maragues
2
No tengo idea de por qué el enfoque de tercer argumento no funciona. Me apliqué a Button. El enfoque de @Benjamin Piette funciona bien.
Youngjae
124

Lo que funcionó para mí:

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

Y

  • Use el constructor de 3 argumentos (no funcionará sin esto)
Benjamin Piette
fuente
El uso de su método funciona, pero obtengo W / ResourceType ﹕ Demasiadas referencias de atributos, detenido en: 0x01010034. ¿Alguna solución?
HaloMediaz
Nunca he visto ese problema. ¿Puede ser un problema específico del dispositivo o algo relacionado con una versión reciente de Android?
Benjamin Piette
11
¡IMPORTANTE! Este método hace el trabajo, pero fijará el estilo para cada niño, vista así, si la creación de un nuevo diseño con el ContextThemeWrapper.
aProperFox
@aProperFox ¿puedes explicar mejor? Conmigo funciona agregando solo a un botón. ¿Te refieres a un diseño?
Gilian
1
@Gilian exactamente, si usa esto en una vista compuesta que tiene uno o más elementos secundarios, obtendrán el mismo estilo que la vista principal para la que establece el estilo. Esto podría generar demasiado relleno o margen dentro de las vistas internas y puede
volverlo
88

Actualización : en el momento de responder esta pregunta (mediados de 2012, nivel API 14-15), establecer la vista mediante programación no era una opción (a pesar de que había algunas soluciones alternativas no triviales), mientras que esto fue posible después de la API más reciente lanzamientos. Ver la respuesta de @ Blundell para más detalles.

ANTIGUA respuesta:

Todavía no puede establecer el estilo de una vista mediante programación, pero puede encontrar útil este hilo .

Korhan Ozturk
fuente
16
No es verdad. Ver la respuesta de @ Blundell.
a.bertucci el
31
Para algunas situaciones (usando TextView por ejemplo) puede usar textView.setTextAppearance(context, R.style.mystyle);. Según el documento "Establece el color del texto, el tamaño, el estilo, el color de las sugerencias y el color de resaltado del recurso TextAppearance especificado". developer.android.com/reference/android/widget/… , int)
Rogel Garcia
44
api> 23; textView.setTextAppearance (R.style.mystyle);
akyol alican el
1
Publiqué esta respuesta hace más de 4 años @HaydenKai. y la API ha cambiado mucho desde entonces permitiendo la aplicación de estilos mediante programación.
Korhan Ozturk
3
@KorhanOzturk estuvo de acuerdo, pero en SO con preguntas como esta que tienen actividad, la pregunta debería reabrirse y aceptarse una nueva respuesta
HaydenKai
16

Para un nuevo botón / TextView:

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

Para una instancia existente:

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

Para imagen o diseños:

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

Si desea continuar utilizando XML (que la respuesta aceptada no le permite hacer) y establecer el estilo después de que se haya creado la vista, puede utilizar la biblioteca de París que admite un subconjunto de todos los atributos disponibles.

Como está inflando su vista desde XML, necesitará especificar una identificación en el diseño:

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

Luego, cuando necesite cambiar el estilo mediante programación, después de que se haya inflado el diseño:

// 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);

Para más información: la lista de tipos y atributos de vista admitidos (incluye fondo, relleno, margen, etc. y puede ampliarse fácilmente) e instrucciones de instalación con documentación adicional .

Descargo de responsabilidad: soy el autor original de dicha biblioteca.

Natanael
fuente
¿Sigue siendo un enfoque recomendado para usar en 2019?
IgorGanapolsky
1
@IgorGanapolsky afaik sí! No conozco uno mejor.
Nathanael
Gracias @Nathanael por proporcionar esta solución. También por ser el encargado de mantenerlo con el último atributo line_height. ¡Sigan con el buen trabajo! Realmente util.
Guillem Roca
7

Puede aplicar un estilo a su actividad haciendo:

super.setTheme( R.style.MyAppTheme );

o Android predeterminado:

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

en tu actividad, antes setContentView().

FOMDeveloper
fuente
44
@siemian, ¿puedes dejar que FOM se exprese libremente? ¡No necesita edición!
Mika
6

Ninguna de las respuestas proporcionadas son correctas.

PUEDES configurar el estilo mediante programación.

La respuesta corta es echar un vistazo a http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/content/Context.java#435

Respuesta larga. Aquí está mi fragmento para establecer un estilo definido personalizado mediante programación a su vista:

1) Cree un estilo en su archivo styles.xml

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

No olvide definir sus atributos personalizados en el archivo attrs.xml

Mi archivo attrsl.xml:

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

Tenga en cuenta que puede usar cualquier nombre para su estilo (mi CustomWidget)

Ahora establezcamos el estilo del widget Programatic Aquí está mi 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));
}
}

diseño:

<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>

Y finalmente la implementación de la clase 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;
}
}

Puede encontrar un ejemplo completamente funcional en https://github.com/Defuera/SetStylableProgramatic

Defuera
fuente
13
Este no es el estilo verdadero, es simplemente falso al aplicar / configurar las configuraciones guardadas (sí, en realidad es algún tipo de configuración guardada en su archivo XML) en sus Vistas (aquí solo tiene 2 Vistas fijas, y seguramente necesita saberlas antemano). Toda la magia está en el applymétodo que no hace nada interesante. El verdadero significado del estilo es que aplica automáticamente un estilo visual en todas las vistas agregadas dinámicamente en el futuro. Este código aquí, por supuesto, no puede hacer eso y no hay nada dinámico aquí, necesitamos saber qué Vistas y qué propiedades / campos establecer.
Rey Rey
1
No me importa especificar a qué vistas aplicar estilos, pero esta solución tiene los elementos de estilo codificados (por ejemplo, textColor y dividerColor). No encontrará dinámicamente todos los elementos definidos en el estilo y los aplicará a la vista.
nasch
El enlace que publicaste no se abre. ¿Puedes volver a publicar?
IgorGanapolsky
4

Esta es una pregunta bastante antigua, pero la solución que funcionó para mí ahora es usar el cuarto parámetro del constructor defStyleRes, si está disponible ... a la vista ... para establecer el estilo

Los siguientes trabajos para mis propósitos (kotlin):

val textView = TextView(context, null, 0, R.style.Headline1)
convexHull
fuente
3

Este es mi ejemplo simple, la clave es el ContextThemeWrappercontenedor, sin él, mi estilo no funciona, y usando el constructor de tres parámetros de la Vista.

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

la manera simple es pasar a través del constructor

RadioButton radioButton = new RadioButton(this,null,R.style.radiobutton_material_quiz);
Sai Gopi N
fuente
Sería útil un poco más de ilustración sobre cómo usar esta solución.
IgorGanapolsky
1

No propongo usar ContextThemeWrapper ya que hace esto:

El tema especificado se aplicará sobre el tema del contexto base.

Lo que puede generar resultados no deseados en su aplicación. En cambio, propongo una nueva biblioteca "paris" para esto de los ingenieros de Airbnb:

https://github.com/airbnb/paris

Defina y aplique estilos a las vistas de Android mediante programación.

Pero después de un tiempo de usarlo, descubrí que en realidad es bastante limitado y dejé de usarlo porque no admite muchas propiedades que necesito fuera de la caja, por lo que uno debe verificar y decidir como siempre.

Renetik
fuente
Explique su amigo de voto negativo ... Perdí un día y medio porque he usado ContextThemeWrapper, y se aplicó a mi fondo de contexto de tema blanco, luego, de repente, el widget de chip de la biblioteca de material de Google estaba fallando, adivina por qué ...
Renetik
¿La biblioteca de París no usa ContextThemeWrapper debajo?
IgorGanapolsky
@IgorGanapolsky no lo hace. Lee el estilo XML y lo convierte en las llamadas de método correspondientes en la vista.
Nathanael
En realidad, es bastante limitado. Dejé de usarlo porque no admite muchas propiedades que necesitaba ...
Renetik
-1

Usé vistas definidas en XML en mi ViewGroup compuesto, las inflé agregadas a Viewgroup. De esta manera no puedo cambiar dinámicamente el estilo, pero puedo hacer algunas personalizaciones de estilo. Mi compuesto:

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);
}

}

y mi vista en xml donde puedo asignar estilos:

<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"
    />

usuario3918502
fuente
1
¿Dónde está configurando dinámicamente el estilo?
IgorGanapolsky
-3

Puede crear el xml que contiene el diseño con el estilo deseado y luego cambiar el recurso de fondo de su vista, de esta manera .

rfsbraz
fuente
Estás hablando de un "estilo" como en Android, pero del "aspecto" de un botón. Esto es muy diferente.
Cristo