Casi todos los desarrolladores saben que hay Temas en Android, pero su uso generalmente se limita a copiar piezas de XML desde Stack Overflow u otros recursos. Hay información en Internet sobre temas, pero esto generalmente es solo una receta sobre cómo lograr un determinado resultado. En este artículo traté de dar una visión general introductoria del mecanismo de estilización de Android.
Contenido
IntroduccionAtributos para RectViewEstilo para RectViewEstilo predeterminadoAtributo del tema de estilo RectViewTema de nivel de actividadVer temaCómo aplicar valores de atributoTemas de recursosResumenIntroduccion
Los estilos y temas en Android son mecanismos que le permiten separar los detalles del diseño (por ejemplo, color, tamaño de fuente, etc.) de la estructura de la interfaz de usuario. Para entender cómo funciona esto, un simple ejemplo con vista personalizada nos ayudará.
Necesitaremos una vista personalizada simple que dibuje un rectángulo con el color deseado dentro de los límites de la vista.
class RectView @JvmOverloads constructor( context: Context, attrSet: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrSet, defStyleAttr) { private val paint = Paint().apply { color = Color.BLUE } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint) } }

Atributos para RectView
Ahora me gustaría poder cambiar el color del rectángulo de diseño.
Para hacer esto, necesitamos agregar un nuevo atributo, agregar attrs.xml al archivo
<resources> <attr name="rectColor" format="color"/> </resources>
Ahora en código podemos acceder a la identificación de este atributo a través de R.attr.rectColor y
En el diseño de la pantalla, podemos utilizar la aplicación: atributo rectColor.
<org.berendeev.themes.RectView android:id="@+id/text2" android:layout_width="100dp" app:rectColor="@color/colorPrimary" android:layout_height="100dp"/>
Pero RectView aún no sabe que hay un atributo en el que puede tomar el color para el rectángulo.
Enseñemos a RectView a comprender el atributo rectColor, agregue un grupo de atributos
<resources> <attr name="rectColor" format="color"/> <declare-styleable name="RectView"> <attr name="rectColor"/> </declare-styleable> </resources>
En la clase R, se generaron 2 nuevos campos:
R.styleable.RectView es una matriz de atributos de identificación, actualmente es una matriz de un elemento R.attr.rectColor
R.styleable.RectView_rectColor es el índice de identificación del atributo en la matriz R.styleable.RectView, es decir atributo id que podemos obtener y así R.styleable.RectView [R.styleable.RectView_rectColor]
Y agregue soporte para el atributo rectColor al código.
init { val typedArray = context.theme.obtainStyledAttributes( attrSet, R.styleable.RectView, 0, 0 ) try { paint.color = typedArray.getColor( R.styleable.RectView_rectColor, Color.BLUE ) } finally { typedArray.recycle() } }
Código completo class RectView @JvmOverloads constructor( context: Context, attrSet: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrSet, defStyleAttr) { private val paint = Paint().apply { color = Color.BLUE } init { val typedArray = context.theme.obtainStyledAttributes( attrSet, R.styleable.RectView, 0, 0 ) try { paint.color = typedArray.getColor( R.styleable.RectView_rectColor, Color.BLUE ) } finally { typedArray.recycle() } } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint) } }
Ahora podemos cambiar el color del rectángulo desde el diseño:

Estilo para RectView
Ahora solo tenemos un atributo, pero imagine que hay diez de ellos; sería extremadamente inconveniente para nosotros establecer todos estos atributos en el diseño para elementos similares cada vez.
La decisión de hacer todo con estilo.
<style name="DefaultRectViewStyle"> <item name="rectColor">@color/colorAccent</item> </style>
Ahora podemos crear cualquier tipo de vista con la misma apariencia especificando el estilo.
<org.berendeev.themes.RectView android:id="@+id/text2" android:layout_width="100dp" style="@style/DefaultRectViewStyle" android:layout_height="100dp"/>
Estilo predeterminado
Ahora queremos que todas las vistas tengan algún tipo de estilo por defecto. Para hacer esto, especificaremos el estilo predeterminado para el método getStyledAttributes:
context.theme.obtainStyledAttributes( attrSet, R.styleable.RectView, 0, R.style.DefaultRectViewStyle )
Eso es suficiente Ahora todo el RectView que agreguemos al diseño tendrá el estilo predeterminado DefaultRectViewStyle.
Atributo del tema de estilo RectView
El valor predeterminado es bueno, pero me gustaría controlar el diseño de manera más flexible. Es conveniente establecer el estilo de una vista particular para toda la aplicación o para una Actividad separada.
Para esto necesitamos un nuevo atributo. Estableceremos su valor en el tema, el valor será el estilo que determina la apariencia de RectView.
<attr name="rectViewStyle" format="reference"/>
Y enseñe a nuestro rectView a comprender este atributo del tema. Al pasar el tercer parámetro R.attr.rectViewStyle al método getStyledAttributes.
context.theme.obtainStyledAttributes( attrSet, R.styleable.RectView, R.attr.rectViewStyle, R.style.DefaultRectViewStyle )
Ahora, si se especifica un elemento con el nombre rectViewStyle y un valor de estilo de tipo en el tema, este estilo se aplicará a todos los RectView con este tema.
Tema de nivel de actividad
Establecemos el valor del atributo rectViewStyle en nuestro tema.
<resources> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> <item name="rectViewStyle">@style/LocalRectViewStyle</item> </style> </resources>
Especifique el tema en el manifiesto de la aplicación.
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.berendeev.themes"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest>
Toda la actividad se establecerá en el tema predeterminado de AppTheme. Configuramos el diseño RectView para toda la aplicación.
El tema también se puede establecer para una Actividad específica en el manifiesto.
<activity android:name=".MainActivity" android:theme="@style/MyTheme">
Aquí es importante que el tema que estableceremos a nivel de aplicación o actividad se herede del tema estándar. Por ejemplo, Theme.AppCompat. De lo contrario, nos estrellamos en el tiempo de ejecución.
java.lang.IllegalStateException: debe usar un tema Theme.AppCompat (o descendiente) con esta actividad.
Ver tema
A nivel de vista, no reinstalamos el tema, sino que sobrescribimos los valores de atributo deseados, por lo que no necesitamos heredar del tema estándar. Un padre puede estar vacío o, por ejemplo, podemos heredar de una de las superposiciones ThemeOverlay.AppCompat.
Aquí configuramos el tema MyOverlay para el grupo LinearLayout y todos sus descendientes en la jerarquía.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:theme="@style/MyOverlay"> <org.berendeev.themes.RectView android:layout_width="100dp" android:layout_height="100dp"/> </LinearLayout>
En el tema, podemos prescindir de un padre, porque solo modificamos el tema de la actividad.
<style name="MyOverlay"> <item name="rectViewStyle">@style/LocalRectViewStyle</item> </style>
Cómo aplicar valores de atributo

Si para la vista definimos los valores de los atributos tanto en el diseño como en el estilo y en el tema, entonces el orden de elección del valor será el siguiente. La prioridad más alta es el valor especificado en el diseño, es decir, si el valor se establece en el diseño, se ignorará el estilo y el tema, luego continuará el estilo, luego el tema y, en último lugar, el estilo predeterminado.
Temas de recursos
Los valores de tema se pueden usar en recursos. Por ejemplo, en dibujable podemos establecer un color que dependerá del color establecido en el tema.
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" > <solid android:color="?attr/colorPrimary"/> </shape>
Ahora el color del rectángulo depende del valor del atributo colorPrimary en el tema. El atributo puede ser cualquiera. Por ejemplo, podemos establecer nuestro atributo en recursos y establecer su valor en el tema.
Tal truco se puede usar en todos los recursos, por ejemplo, en el selector o en el dibujo vectorial. Esto hace que el diseño sea más estructurado y flexible. Podemos cambiar rápidamente el tema de todas las actividades.
Resumen
- El tema y el estilo en el nivel de recursos son uno y el mismo, pero se usan de manera diferente.
- Un tema puede contener otros temas, solo significados o estilos para la vista.
- El estilo se indica en el diseño a nivel de vista. LayoutInflater calcula los valores de estilo y los pasa como un conjunto de atributos al constructor de vistas.
- Temas es un mecanismo que le permite definir la apariencia globalmente para toda la actividad.
- Los valores del tema se pueden cambiar para el elemento (y descendientes) en la jerarquía de vistas.
- Los valores del tema se pueden usar no solo en Vista, sino también en recursos.
PD: Si el artículo es interesante, escribiré un artículo más avanzado o un artículo con muchos ejemplos reales.