155 votes

Ai-je besoin des trois constructeurs pour une vue personnalisée Android ?

Lors de la création d'une vue personnalisée, j'ai remarqué que de nombreuses personnes semblent procéder de la manière suivante :

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.
}

Ma première question est, qu'en est-il du constructeur MyView(Context context, AttributeSet attrs, int defStyle) ? Je ne sais pas où il est utilisé, mais je le vois dans la super classe. En ai-je besoin, et où est-il utilisé ?

Il y a une autre partie à cette question .

154voto

Ovidiu Latcu Points 20783

Si vous voulez ajouter votre propre View de xml aime aussi :

 <com.mypack.MyView
      ...
      />

vous aurez besoin du constructeur public MyView(Context context, AttributeSet attrs) sinon vous obtiendrez un Exception quand Android essaie de gonfler votre View .

Si vous ajoutez votre View de xml et spécifier également le android:style attribut comme :

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

le 2e constructeur sera également appelé et le style par défaut sera le suivant MyCustomStyle avant d'appliquer des attributs XML explicites.

Le troisième constructeur est généralement utilisé lorsque vous souhaitez que toutes les vues de votre application aient le même style.

3 votes

Quand utiliser le premier constructeur ?

0 votes

@OvidiuLatcu pouvez-vous montrer un exemple du troisième CTOR (avec les 3 paramètres) ?

0 votes

Puis-je ajouter des paramètres supplémentaires au constructeur et comment puis-je les utiliser ?

127voto

Jin Points 3964

Si vous surchargez les trois constructeurs, veuillez NE PAS CASCADER this(...) APPELS. Vous devriez plutôt faire ceci :

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 raison en est que la classe parente peut inclure des attributs par défaut dans ses propres constructeurs que vous pourriez accidentellement remplacer. Par exemple, voici le constructeur 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 vous n'avez pas appelé super(context) vous n'auriez pas réglé correctement R.attr.textViewStyle comme attr de style.

57voto

mbonnin Points 273

MyView(Contexte)

Utilisé lors de l'instanciation programmatique des vues.

MyView(Contexte, AttributeSet attrs)

Utilisé par le LayoutInflater pour appliquer des attributs xml. Si l'un de ces attributs est nommé style Dans le cas d'un style, les attributs seront recherchés dans le style avant de rechercher des valeurs explicites dans le fichier xml de présentation.

MyView(Context context, AttributeSet attrs, int defStyleAttr)

Supposons que vous souhaitiez appliquer un style par défaut à tous les widgets sans avoir à spécifier style dans chaque fichier de mise en page. Par exemple, faites en sorte que toutes les cases à cocher soient roses par défaut. Vous pouvez le faire avec defStyleAttr et le framework recherchera le style par défaut dans votre thème.

Notez que defStyleAttr a été incorrectement nommé defStyle il y a quelque temps et la question de savoir si ce constructeur est vraiment nécessaire ou non fait l'objet de discussions. Voir https://code.google.com/p/Android/issues/detail?id=12683

MyView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)

Le troisième constructeur fonctionne bien si vous avez le contrôle sur le thème de base des applications. Cela fonctionne pour google car ils livrent leurs widgets avec les thèmes par défaut. Mais supposons que vous écriviez une bibliothèque de widgets et que vous vouliez qu'un style par défaut soit défini sans que vos utilisateurs aient besoin de modifier leur thème. Vous pouvez maintenant le faire en utilisant defStyleRes en lui donnant la valeur par défaut dans les deux premiers constructeurs :

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();
}

Tout compte fait

Si vous implémentez vos propres vues, seuls les deux premiers constructeurs devraient être nécessaires et peuvent être appelés par le framework.

Si vous voulez que vos vues soient extensibles, vous pouvez implémenter le 4ème constructeur pour les enfants de votre classe afin de pouvoir utiliser un style global.

Je ne vois pas de réel cas d'utilisation pour le 3ème constructeur. Peut-être un raccourci si vous ne fournissez pas un style par défaut pour votre widget mais que vous voulez quand même que vos utilisateurs puissent le faire. Cela ne devrait pas arriver si souvent.

10voto

jules Points 21

Kotlin semble supprimer une grande partie de ces difficultés :

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

@JvmOverloads générera tous les constructeurs nécessaires (voir l'annotation documentation ), chacun d'entre eux appelant vraisemblablement super(). Ensuite, il suffit de remplacer votre méthode d'initialisation par un bloc Kotlin init {}. Fini le code standard !

1voto

CoXier Points 813

Le troisième constructeur est beaucoup plus compliqué, laissez-moi vous donner un exemple.

Support-v7 SwitchCompact Le paquet supporte thumbTint y trackTint Vous voulez maintenant les prendre en charge dans la version 23 et comment allez-vous vous y prendre pour y parvenir ?

Nous supposons utiliser une vue personnalisée SupportedSwitchCompact étend 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();
}

C'est un style de code traditionnel. Notez que nous passons 0 au troisième paramètre ici . Lorsque vous exécutez le code, vous trouverez getThumbDrawable() renvoie toujours la nullité. C'est étrange car la méthode getThumbDrawable() est sa super classe SwitchCompact La méthode de l'entreprise.

Si vous passez R.attr.switchStyle au troisième paramètre, tout se passe bien. Alors pourquoi ?

Le troisième paramètre est un simple attribut. L'attribut pointe vers une ressource de style. Dans le cas ci-dessus, le système trouvera switchStyle dans le thème actuel, heureusement le système le trouve.

Sur frameworks/base/core/res/res/values/themes.xml vous verrez :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X