studio programacion para móviles libro edición desarrollo desarrollar curso aprende aplicaciones android android-custom-view

programacion - ¿Necesito los tres constructores para una vista personalizada de Android?



manual de programacion android pdf (6)

Al crear una vista personalizada, he notado que muchas personas parecen hacerlo así:

public MyView(Context context) { super(context); // this constructor used when programmatically creating view doAdditionalConstructorWork(); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); // this constructor used when creating view through XML doAdditionalConstructorWork(); } private void doAdditionalConstructorWork() { // init variables etc. }

Mi primera pregunta es, ¿qué pasa con el constructor MyView(Context context, AttributeSet attrs, int defStyle) ? No estoy seguro de dónde se usa, pero lo veo en la clase superior. ¿Lo necesito y dónde se usa?

Hay otra parte en esta pregunta .


MyView (Contexto contextual)

Se usa al instanciar vistas mediante programación.

MyView (Contexto contextual, AttributeSet attrs)

Utilizado por LayoutInflater para aplicar atributos xml. Si uno de estos atributos se llama style , los atributos se buscarán en el estilo antes de buscar valores explícitos en el archivo xml de diseño.

MyView (Contexto contextual, AttributeSet attrs, int defStyleAttr)

Supongamos que desea aplicar un estilo predeterminado a todos los widgets sin tener que especificar el style en cada archivo de diseño. Por ejemplo, todas las casillas de verificación son rosadas por defecto. Puede hacer esto con defStyleAttr y el marco buscará el estilo predeterminado en su tema.

Tenga en cuenta que defStyleAttr recibió el nombre incorrecto de defStyle algún tiempo y existe cierta discusión acerca de si este constructor es realmente necesario o no. Ver https://code.google.com/p/android/issues/detail?id=12683

MyView (Contexto de contexto, AttributeSet attrs, int defStyleAttr, int defStyleRes)

El tercer constructor funciona bien si tiene control sobre el tema base de las aplicaciones. Eso funciona para google porque envían sus widgets junto con los temas predeterminados. Pero supongamos que está escribiendo una biblioteca de widgets y desea que se establezca un estilo predeterminado sin que los usuarios necesiten modificar su tema. Ahora puede hacer esto usando defStyleRes al configurarlo en el valor predeterminado en los 2 primeros constructores:

public MyView(Context context) { super(context, null, 0, R.style.MyViewStyle); init(); } public MyView(Context context, AttributeSet attrs) { super(context, attrs, 0, R.style.MyViewStyle); init(); }

Considerándolo todo

Si está implementando sus propios puntos de vista, solo los 2 primeros constructores deberían ser necesarios y el marco puede llamarlos.

Si desea que sus vistas sean extensibles, puede implementar el 4to constructor para que los niños de su clase puedan usar el estilo global.

No veo un caso de uso real para el tercer constructor. Tal vez un atajo si no proporciona un estilo predeterminado para su artilugio pero aún desea que sus usuarios puedan hacerlo. No debería pasar tanto.


El tercer constructor es mucho más complicado. Déjame dar un ejemplo.

El paquete Support-v7 SwitchCompact admite los thumbTint y trackTint desde la versión 24, mientras que la versión 23 no los admite. ¿Desea ahora darles soporte en la versión 23 y cómo lo hará para lograrlo?

Suponemos usar una vista personalizada. SupportedSwitchCompact extiende SwitchCompact .

public SupportedSwitchCompat(Context context) { this(context, null); } public SupportedSwitchCompat(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SupportedSwitchCompat(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init(){ mThumbDrawable = getThumbDrawable(); mTrackDrawable = getTrackDrawable(); applyTint(); }

Es un estilo de código tradicional. Tenga en cuenta que pasamos 0 al tercer parámetro aquí . Cuando ejecuta el código, encontrará que getThumbDrawable() siempre devuelve nulo lo extraño que es porque el método getThumbDrawable() es su método getThumbDrawable() SwitchCompact .

Si pasa R.attr.switchStyle al tercer param, todo va bien. Entonces, ¿por qué?

El tercer param es un atributo simple. El atributo apunta a un recurso de estilo. En el caso anterior, el sistema encontrará el atributo switchStyle en el tema actual. Afortunadamente, el sistema lo encuentra.

En frameworks/base/core/res/res/values/themes.xml , verá:

<style name="Theme"> <item name="switchStyle">@style/Widget.CompoundButton.Switch</item> </style>


Kotlin parece quitar mucho de este dolor:

class MyView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : View(context, attrs, defStyle)

@JvmOverloads generará todos los constructores necesarios (ver la documentation la anotación), cada uno de los cuales supuestamente llama a super (). Luego, simplemente reemplace su método de inicialización con un bloque Kotlin init {}. ¡Se acabó el código repetitivo!


Si agrega su View personalizada desde xml también puede:

<com.mypack.MyView ... />

Necesitará el constructor public MyView(Context context, AttributeSet attrs) ; de lo contrario, obtendrá una Exception cuando Android intente inflar su View .

Si agrega su View desde xml y también especifica el atributo android:style como:

<com.mypack.MyView style="@styles/MyCustomStyle" ... />

el segundo constructor también se llamará y el estilo predeterminado MyCustomStyle antes de aplicar atributos XML explícitos.

El tercer constructor se usa generalmente cuando desea que todas las Vistas en su aplicación tengan el mismo estilo.


Si anula los tres constructores, NO CASCADE this(...) LLAMADAS. En su lugar, deberías estar haciendo esto:

public MyView(Context context) { super(context); init(context, null, 0); } public MyView(Context context, AttributeSet attrs) { super(context,attrs); init(context, attrs, 0); } public MyView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context, attrs, defStyle); } private void init(Context context, AttributeSet attrs, int defStyle) { // do additional work }

La razón es que la clase principal puede incluir atributos predeterminados en sus propios constructores que usted podría estar anulando accidentalmente. Por ejemplo, este es el constructor de TextView :

public TextView(Context context) { this(context, null); } public TextView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, com.android.internal.R.attr.textViewStyle); } public TextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); }

Si no llamó a super(context) , no habría configurado correctamente R.attr.textViewStyle como el estilo attr.


Si tiene que incluir tres constructores como el que se está discutiendo ahora, también podría hacerlo.

public MyView(Context context) { this(context,null,0); } public MyView(Context context, AttributeSet attrs) { this(context,attrs,0); } public MyView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); doAdditionalConstructorWork(); }