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 beaucoup de personnes semblent le faire comme ceci:

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

Ma première question est la suivante: qu’en est-il du constructeur MyView(Context context, AtsortingbuteSet attrs, int defStyle) ? Je ne suis pas sûr où il est utilisé, mais je le vois dans la super classe. Est-ce que j’en ai besoin et où est-il utilisé?

Il y a une autre partie à cette question .

    Si vous souhaitez append votre View personnalisée à partir de xml comme xml :

       

    vous aurez besoin du constructeur public MyView(Context context, AtsortingbuteSet attrs) , sinon vous obtiendrez une Exception lorsque Android essaiera de gonfler votre View .

    Si vous ajoutez votre View partir de xml et spécifiez également l’atsortingbut android:style comme:

       

    le constructeur 2 sera également appelé et le style par défaut à MyCustomStyle avant d’appliquer des atsortingbuts 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.

    Si vous écrasez les trois constructeurs, s’il vous plait, NE CASCADEZ PAS CET APPEL this(...) . Vous devriez plutôt faire ceci:

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

    La raison en est que la classe parente peut inclure des atsortingbuts par défaut dans ses propres constructeurs que vous pouvez accidentellement remplacer. Par exemple, c’est le constructeur de TextView :

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

    Si vous n’avez pas appelé super(context) , vous n’auriez pas correctement défini R.attr.textViewStyle comme style attr.

    MyView (contexte contextuel)

    Utilisé lors de l’instanciation de vues par programmation.

    MyView (Contexte contextuel, AtsortingbuteSet attrs)

    Utilisé par LayoutInflater pour appliquer les atsortingbuts xml. Si l’un de ces atsortingbuts est nommé style , les atsortingbuts seront recherchés dans le style avant de rechercher des valeurs explicites dans le fichier XML de présentation.

    MyView (Contexte contextuel, AtsortingbuteSet attrs, int defStyleAttr)

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

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

    MyView (Contexte contextuel, AtsortingbuteSet attrs, int defStyleAttr, int defStyleRes)

    Le 3ème constructeur fonctionne bien si vous contrôlez le thème de base des applications. Cela fonctionne pour Google, car ils livrent leurs widgets à côté des thèmes par défaut. Mais supposons que vous écriviez une bibliothèque de widgets et que vous souhaitiez définir un style par défaut sans que vos utilisateurs aient besoin de modifier leur thème. Vous pouvez maintenant le faire en utilisant defStyleRes en le définissant sur 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, AtsortingbuteSet attrs) { super(context, attrs, 0, R.style.MyViewStyle); init(); } 

    En tout

    Si vous implémentez vos propres vues, seuls les 2 premiers constructeurs doivent être requirejs et peuvent être appelés par le framework.

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

    Je ne vois pas de véritable 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 souhaitez que vos utilisateurs puissent le faire. Ne devrait pas arriver autant.

    Kotlin semble enlever beaucoup de cette douleur:

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

    @JvmOverloads va générer tous les constructeurs requirejs (voir la documentation de cette annotation), chacun d’entre eux appelant probablement super (). Ensuite, remplacez simplement votre méthode d’initialisation par un bloc Kotlin init {}. Code de la chaudière disparu!

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

    SwitchCompact package Support-v7 SwitchCompact prend en charge l’ thumbTint et trackTint depuis la version 24, tandis que la version 23 ne les prend pas en charge.

    Nous supposons utiliser une vue personnalisée. SupportedSwitchCompact étend SwitchCompact .

     public SupportedSwitchCompat(Context context) { this(context, null); } public SupportedSwitchCompat(Context context, AtsortingbuteSet attrs) { this(context, attrs, 0); } public SupportedSwitchCompat(Context context, AtsortingbuteSet 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 constaterez que getThumbDrawable() renvoie toujours null à quel point il est étrange, car la méthode getThumbDrawable() est la méthode de la super classe SwitchCompact .

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

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

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

      

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

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