Declarar un elemento personalizado de la interfaz de usuario de Android mediante XML

Resuelto Casebash asked hace 54 años • 6 respuestas

¿Cómo declaro un elemento de la interfaz de usuario de Android usando XML?

Casebash avatar Jan 01 '70 08:01 Casebash
Aceptado

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-styleableetiqueta. Los atributos de Android no estándar, como los que extraInformationdeben 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 AttributeSetpara 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.MyCustomViewes 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_textcontiene el android_textatributo de MyCustomView. Luego, los atributos se pueden recuperar utilizando TypedArrayvarias getfunciones. Si el atributo no está definido en el XML, nullse 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 LinearLayoutatributo 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
Casebash avatar Apr 23 '2010 01:04 Casebash

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" />
Andy avatar Nov 28 '2011 06:11 Andy

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.

yuriy.weiss avatar Feb 28 '2013 14:02 yuriy.weiss