Declarar un elemento personalizado de la interfaz de usuario de Android mediante XML
¿Cómo declaro un elemento de la interfaz de usuario de Android usando XML?
La Guía para desarrolladores de Android tiene una sección llamada Creación de componentes personalizados . Desafortunadamente, la discusión sobre los atributos XML solo cubre la declaración del control dentro del archivo de diseño y no el manejo de los valores dentro de la inicialización de la clase. Los pasos son los siguientes:
1. Declarar atributos envalues\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>
Observe el uso de un nombre no calificado en la declare-styleable
etiqueta. Los atributos de Android no estándar, como los que extraInformation
deben declararse, deben tener su tipo. Las etiquetas declaradas en la superclase estarán disponibles en las subclases sin tener que volver a declararlas.
2. Crear constructores
Dado que hay dos constructores que utilizan an AttributeSet
para la inicialización, es conveniente crear un método de inicialización independiente para que lo llamen los constructores.
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
es un int[]
recurso generado automáticamente donde cada elemento es el ID de un atributo. Los atributos se generan para cada propiedad en el XML agregando el nombre del atributo al nombre del elemento. Por ejemplo, R.styleable.MyCustomView_android_text
contiene el android_text
atributo de MyCustomView
. Luego, los atributos se pueden recuperar utilizando TypedArray
varias get
funciones. Si el atributo no está definido en el XML, null
se devuelve. Excepto, por supuesto, si el tipo de retorno es primitivo, en cuyo caso se devuelve el segundo argumento.
Si no desea recuperar todos los atributos, es posible crear esta matriz manualmente. El ID de los atributos estándar de Android se incluye en android.R.attr
, mientras que los atributos de este proyecto se encuentran en R.attr
.
int attrsWanted[]=new int[]{android.R.attr.text, R.attr.textColor};
Tenga en cuenta que no debe utilizar nada en android.R.styleable
, según este hilo , puede cambiar en el futuro. Todavía está en la documentación, ya que es útil ver todas estas constantes en un solo lugar.
3. Úselo en archivos de diseño comolayout\main.xml
Incluya la declaración del espacio de nombres xmlns:app="http://schemas.android.com/apk/res-auto"
en el elemento xml de nivel superior. Los espacios de nombres proporcionan un método para evitar los conflictos que a veces ocurren cuando diferentes esquemas usan los mismos nombres de elementos (consulte este artículo para obtener más información). La URL es simplemente una forma de identificar esquemas de forma única: en realidad no es necesario alojar nada en esa URL . Si esto no parece estar haciendo nada, es porque en realidad no necesita agregar el prefijo del espacio de nombres a menos que necesite resolver un conflicto.
<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"
/>
Haga referencia a la vista personalizada utilizando el nombre completo.
Ejemplo de LabelView de Android
Si desea un ejemplo completo, consulte el ejemplo de vista de etiquetas de Android.
LabelView.java
TypedArray a=context.obtainStyledAttributes(attrs, R.styleable.LabelView);
CharSequences=a.getString(R.styleable.LabelView_text);
atributos.xml
<declare-styleable name="LabelView">
<attr name="text"format="string"/>
<attr name="textColor"format="color"/>
<attr name="textSize"format="dimension"/>
</declare-styleable>
vista_personalizada_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"/>
Esto está contenido en un LinearLayout
atributo de espacio de nombres:xmlns:app="http://schemas.android.com/apk/res-auto"
Enlaces
- Hilo StackOverflow: recuperación de un atributo XML para control personalizado
- ¿Cómo uso getStyledAttributes con temas internos de Android?
- Definición de atributos personalizados + lista de formatos de atributos admitidos
Gran referencia. ¡Gracias! Una adición a esto:
Si tiene incluido un proyecto de biblioteca que ha declarado atributos personalizados para una vista personalizada, debe declarar el espacio de nombres de su proyecto, no el de la biblioteca. P.ej:
Dado que la biblioteca tiene el paquete "com.example.library.customview" y el proyecto de trabajo tiene el paquete "com.example.customview", entonces:
No funcionará (muestra el error "error: No se encontró ningún identificador de recurso para el atributo 'newAttr' en el paquete '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" />
Trabajará:
<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" />
Adición a la respuesta más votada.
obtener atributos de estilo()
Quiero agregar algunas palabras sobre el uso de getStyledAttributes(), cuando creamos una vista personalizada usando los atributos predefinidos de android:xxx. Especialmente cuando usamos TextAppearance.
Como se mencionó en "2. Creación de constructores", la vista personalizada obtiene AttributeSet en su creación. El uso principal lo podemos ver en el código fuente de 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();
¿Qué podemos ver aquí?
obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
El conjunto de atributos se procesa por tema según la documentación. Los valores de los atributos se compilan paso a paso. Los primeros atributos se completan a partir del tema, luego los valores se reemplazan por valores del estilo y, finalmente, los valores exactos del XML para instancias de vista especiales reemplazan a otros.
Matriz de atributos solicitados: com.android.internal.R.styleable.TextView
es una matriz ordinaria de constantes. Si solicitamos atributos estándar, podemos construir esta matriz manualmente.
Lo que no se menciona en la documentación: orden de los elementos de TypedArray resultantes.
Cuando se declara una vista personalizada en attrs.xml, se generan constantes especiales para los índices de atributos. Y podemos extraer valores de esta manera: a.getString(R.styleable.MyCustomView_android_text)
. Pero para manual int[]
no hay constantes. Supongo que getXXXValue(arrayIndex) funcionará bien.
Y otra pregunta es: "¿Cómo podemos reemplazar las constantes internas y solicitar atributos estándar?" Podemos usar valores android.R.attr.*.
Entonces, si queremos usar el atributo TextAppearance estándar en la vista personalizada y leer sus valores en el constructor, podemos modificar el código desde TextView de esta manera:
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();
}
Donde se define 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>
Tal vez me equivoque de alguna manera, pero la documentación de Android sobre getStyledAttributes() es muy pobre.
Ampliación del componente de interfaz de usuario estándar
Al mismo tiempo, podemos ampliar el componente de interfaz de usuario estándar, utilizando todos sus atributos declarados. Este enfoque no es tan bueno porque TextView, por ejemplo, declara muchas propiedades. Y será imposible implementar la funcionalidad completa en onMeasure() y onDraw() anulados.
Pero podemos sacrificar la reutilización teórica amplia del componente personalizado. Diga "Sé exactamente qué funciones usaré" y no comparta el código con nadie.
Entonces podemos implementar el constructor CustomComponent(Context, AttributeSet, defStyle)
. Después de llamar, super(...)
tendremos todos los atributos analizados y disponibles a través de métodos getter.