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

en créant une vue personnalisée, j'ai remarqué que beaucoup de gens semblent le faire comme ceci:

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 classe super. J'ai besoin de lui, et où est-il utilisé?

il y a une autre partie de cette question .

121
demandé sur Community 2012-02-08 18:55:51

6 réponses

si vous voulez ajouter votre coutume View de xml aussi comme:

 <com.mypack.MyView
      ...
      />

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

si vous ajoutez votre View de xml et spécifiez également l'attribut android:style comme:

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

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

le troisième constructeur est habituellement utilisé lorsque vous voulez que toutes les vues dans votre application d'avoir le même style.

133
répondu Ovidiu Latcu 2016-09-01 23:29:47

si vous outrepassez les trois constructeurs, s'il vous plaît NE PAS FAIRE DES APPELS en CASCADE this(...) . 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 est que la classe mère peut inclure des attributs par défaut dans ses propres constructeurs que vous pourriez accidentellement annuler. Par exemple, c'est le constructeur pour 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'aviez pas appelé super(context) , vous n'auriez pas correctement placé R.attr.textViewStyle comme style attr.

103
répondu Jin 2015-12-16 00:00:02

MyView (Context context)

utilisé lors de L'instanciation des vues par programmation.

MyView (Context context, AttributeSet attrs)

utilisé par le LayoutInflater pour appliquer les attributs xml. Si l'un de ces attributs est nommé style , les attributs seront recherchés dans le style avant de rechercher des valeurs explicites dans le fichier XML de mise en page.

MyView (Context context, AttributeSet attrs, int defStyleAttr)

supposons que vous voulez appliquer un style par défaut à tous les widgets sans avoir à spécifier style dans chaque fichier de mise en page. Pour un exemple faire toutes les cases à cocher rose par défaut. Vous pouvez le faire avec defStyleAttr et le framework cherchera le style par défaut dans votre thème.

notez que defStyleAttr a été incorrectement nommé defStyle il y a quelque temps et il y a une discussion sur la question de savoir si ce constructeur est vraiment nécessaire ou non. Voir https://code.google.com/p/android/issues/detail?id=12683

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

Le 3ème constructeur fonctionne bien si vous avez le contrôle sur le thème de base des applications. Cela fonctionne pour google parce qu'ils envoient leurs widgets le long des thèmes par défaut. Mais supposons que vous écrivez une bibliothèque de widget et que vous voulez qu'un style par défaut soit défini sans votre utilisateurs ayant besoin de modifier leur thème. Vous pouvez maintenant le faire en utilisant defStyleRes en le mettant à la valeur par défaut dans les 2 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 en Tous

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

si vous voulez que vos vues soient extensibles, vous pourriez mettre en œuvre le 4ème constructeur pour les enfants de votre classe pour être en mesure d'utiliser mondial de style.

Je ne vois pas de cas d'utilisation réelle 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 que vos utilisateurs puissent le faire. Ne devrait pas se produire tant que ça.

38
répondu mbonnin 2017-05-14 08:40:29

Kotlin semble enlever beaucoup de cette douleur:

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

@JvmOverloads va générer tous les constructeurs requis (voir la "documentation de l'annotation ), dont chacun appelle probablement super (). Ensuite, remplacez simplement votre méthode d'initialisation par un bloc init {} de Kotlin. Code Boilerplate parti!

1
répondu jules 2018-03-18 22:27:10

Le troisième constructeur est beaucoup plus compliqué.Permettez-moi de tenir un exemple.

Support-v7 SwitchCompact package supports thumbTint et trackTint attribut depuis la version 24 tandis que la version 23 ne les supporte pas.Maintenant, vous voulez les soutenir dans 23 version et comment allez-vous faire pour y parvenir?

Nous supposons à utiliser Affichage personnalisé SupportedSwitchCompact s'é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 code traditionnel style. Note nous passons 0 au troisième param ici . Lorsque vous exécutez le code, vous trouverez getThumbDrawable() toujours retourner null comment étrange il est parce que la méthode getThumbDrawable() est sa classe super SwitchCompact méthode de l'.

si vous passez R.attr.switchStyle au troisième param, tout va bien.Alors pourquoi?

le troisième paramètre est un attribut simple. L'attribut renvoie à une ressource style.Dans le cas ci-dessus, le système: trouver switchStyle attribut dans le thème actuel heureusement système trouve.

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

<style name="Theme">
    <item name="switchStyle">@style/Widget.CompoundButton.Switch</item>
</style>
0
répondu CoXier 2017-10-22 05:34:13

Si vous devez inclure trois constructeurs comme celui en discussion maintenant, vous pouvez faire cela aussi.

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

}
-2
répondu arTsmarT 2014-07-08 18:54:25