Declarar un elemento personalizado de interfaz de usuario de Android utilizando XML

¿Cómo declaro un elemento de 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 de los atributos XML solo cubre declarar el control dentro del archivo de disposición y no manejar realmente los valores dentro de la inicialización de la clase. Los pasos son los siguientes:

1. Declarar atributos en values\attrs.xml

         

Observe el uso de un nombre no calificado en la etiqueta declare-styleable . Atributos android no estándar como extraInformation deben tener su tipo declarado. Las tags declaradas en la superclase estarán disponibles en subclases sin tener que ser redeclaradas.

2. Crear constructores

Dado que hay dos constructores que usan un AttributeSet para la inicialización, es conveniente crear un método de inicialización separado para que los constructores llamen.

 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 recurso autogenerado int[] donde cada elemento es el ID de un atributo. Los atributos se generan para cada propiedad en el XML al agregar el nombre del atributo al nombre del elemento. Por ejemplo, R.styleable.MyCustomView_android_text contiene el atributo MyCustomView para MyCustomView . Los atributos se pueden recuperar desde TypedArray usando varias funciones get . Si el atributo no está definido en el XML definido, entonces se devuelve null . Excepto, por supuesto, si el tipo de devolución es una primitiva, en cuyo caso se devuelve el segundo argumento.

Si no desea recuperar todos los atributos, es posible crear esta matriz manualmente. La ID para los atributos estándar de Android se incluye en android.R.attr , mientras que los atributos para este proyecto están en R.attr .

 int attrsWanted[]=new int[]{android.R.attr.text, R.attr.textColor}; 

Tenga en cuenta que no debe usar nada en android.R.styleable , de acuerdo con este hilo , puede cambiar en el futuro. Todavía está en la documentación que es útil ver todas estas constantes en un solo lugar.

3. Úselo en un diseño de archivos como layout\main.xml

Incluya la statement del espacio de nombres xmlns:app="http://schemas.android.com/apk/res-auto" en el elemento xml de nivel superior. Los espacios de nombre 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 de manera única los esquemas; en realidad, nada tiene que alojarse en esa URL . Si esto no parece estar haciendo nada, es porque no necesita agregar el prefijo del espacio de nombres a menos que necesite resolver un conflicto.

  

Haga referencia a la vista personalizada con el nombre completo.

Muestra Android LabelView

Si desea un ejemplo completo, mire la muestra de la vista de la etiqueta de Android.

LabelView.java

  TypedArray a=context.obtainStyledAttributes(attrs, R.styleable.LabelView); CharSequences=a.getString(R.styleable.LabelView_text); 

attrs.xml

      

custom_view_1.xml

  

Esto está contenido en un LinearLayout con un atributo de espacio de nombres: xmlns:app="http://schemas.android.com/apk/res-auto"

Campo de golf

  • Subproceso StackOverflow: recuperación de un atributo XML para control personalizado
  • ¿Cómo utilizo 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 un proyecto de biblioteca incluido que ha declarado atributos personalizados para una vista personalizada, debe declarar el espacio de nombres del 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 ha encontrado ningún identificador de recurso para el atributo ‘newAttr’ en el paquete ‘com.example.library.customview'”):

  

Trabajará:

  

Además de la respuesta más votada.

obtainStyledAttributes ()

Quiero agregar algunas palabras sobre el uso de getStyledAttributes (), cuando creamos una vista personalizada usando los atributos de android: xxx prdefined. Especialmente cuando usamos TextAppearance.
Como se mencionó en “2. Creación de constructores”, la vista personalizada obtiene AttributeSet en su creación. Uso principal que 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 comstackn paso a paso. Los primeros atributos se rellenan del tema, luego los valores se reemplazan por los valores del estilo y, finalmente, los valores exactos de XML para la instancia de vista especial reemplazan a los demás.
Matriz de atributos solicitados – com.android.internal.R.styleable.TextView
Es una matriz ordinaria de constantes. Si estamos solicitando atributos estándar, podemos construir esta matriz manualmente.

Lo que no se menciona en la documentación: orden de los elementos del resultado TypedArray.
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 los valores de android.R.attr. *.

Entonces, si queremos usar el atributo estándar de TextAppearance en la vista personalizada y leer sus valores en el constructor, podemos modificar el código de 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:

         

Tal vez, estoy equivocado de alguna manera, pero la documentación de Android en obtainStyledAttributes () es muy pobre.

Ampliando el componente UI estándar

Al mismo tiempo, podemos simplemente extender el componente UI 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 overriden onMeasure () y onDraw ().

Pero podemos sacrificar el rechazo teórico amplio 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 a super(...) tendremos todos los atributos analizados y disponibles a través de métodos getter.

Parece que Google ha actualizado su página de desarrollador y agregado varios entrenamientos allí.

Uno de ellos se ocupa de la creación de vistas personalizadas y se puede encontrar aquí

Muchas gracias por la primera respuesta.

En cuanto a mí, solo tuve un problema con eso. Al inflar mi vista, tuve un error: java.lang.NoSuchMethodException: MyView (Contexto, Atributos)

Lo resolví creando un nuevo constructor:

 public MyView(Context context, AttributeSet attrs) { super(context, attrs); // some code } 

Espero que esto ayude!

Puede incluir cualquier archivo de diseño en otro archivo de diseño como-

       

aquí los archivos de diseño en la etiqueta incluir son otros archivos de diseño .xml en la misma carpeta de res.