Lors de la création d'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 supérieure. Est-ce que j'en ai besoin et où est-il utilisé?
Il y a ne autre partie de cette question .
Si vous voulez ajouter votre View
personnalisé à partir de xml
, aimez aussi:
<com.mypack.MyView
...
/>
vous aurez besoin du constructeur public MyView(Context context, AttributeSet attrs)
, sinon vous obtiendrez un Exception
quand Android essaiera de gonfler votre View
.
Si vous ajoutez votre View
de xml
et spécifiez également l'attribut Android:style
Comme suit:
<com.mypack.MyView
style="@styles/MyCustomStyle"
...
/>
le second constructeur sera également appelé et le style par défaut sera MyCustomStyle
avant d'appliquer les 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.
Si vous remplacez les trois constructeurs, veuillez NE PAS CASCADER this(...)
CALLS. 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 éventuellement remplacer par accident. Par exemple, il s'agit du 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'avez pas appelé super(context)
, vous n'auriez pas correctement défini R.attr.textViewStyle
Comme style attr.
Utilisé lors de l'instanciation de vues par programme.
Utilisé par le LayoutInflater
pour appliquer les attributs XML. Si l'un de ces attributs s'appelle style
, les attributs seront recherchés dans le style avant de rechercher des valeurs explicites dans le fichier XML de mise en page.
Supposons que vous souhaitiez appliquer un style par défaut à tous les widgets sans avoir à spécifier style
dans chaque fichier de présentation. Par exemple, cochez toutes les cases en rose. Vous pouvez le faire avec defStyleAttr et le framework recherchera le style par défaut dans votre thème.
Notez que defStyleAttr
a été nommé de manière incorrecte defStyle
il y a quelque temps et il y a quelques discussions sur le point de savoir si ce constructeur est vraiment nécessaire ou non. Voir https://code.google.com/p/Android/issues/detail?id=1268
Le 3è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 souhaitiez définir un style par défaut sans que vos utilisateurs aient besoin de modifier leur thème. Vous pouvez maintenant faire ceci en utilisant defStyleRes
en lui donnant 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();
}
Si vous implémentez vos propres vues, seuls les deux premiers constructeurs sont nécessaires et peuvent être appelés par le framework.
Si vous souhaitez que vos vues soient extensibles, vous pouvez implémenter le quatrième constructeur pour que les enfants de votre classe puissent utiliser un style global.
Je ne vois pas de cas d'utilisation réel 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 tout de même que vos utilisateurs puissent le faire. Ça ne devrait pas arriver trop souvent.
Kotlin semble enlever beaucoup de cette douleur:
class MyView
@JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0)
: View(context, attrs, defStyle)
@JvmOverloads générera tous les constructeurs requis (voir documentation ) de cette annotation, chacun d'entre eux appelant vraisemblablement super (). Ensuite, remplacez simplement votre méthode d’initialisation par un bloc Kotlin init {}. Le code de la plaque chauffante est parti!
Le troisième constructeur est beaucoup plus compliqué. Laissez-moi vous donner un exemple.
Support-v7 SwitchCompact
package prend en charge l'attribut thumbTint
et trackTint
depuis la version 24 alors que la version 23 ne les prend pas en charge.Maintenant, vous souhaitez les prendre en charge dans la version 23 et comment allez-vous le faire pour y parvenir?
Nous supposons que la 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 que getThumbDrawable()
renverra toujours à null combien il est étrange car la méthode getThumbDrawable()
est sa méthode de 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 attribut simple. L'attribut pointe sur une ressource de style. Dans le cas ci-dessus, le système trouvera l'attribut switchStyle
dans le thème actuel, heureusement, le système le trouvera.
Dans frameworks/base/core/res/res/values/themes.xml
, Vous verrez:
<style name="Theme">
<item name="switchStyle">@style/Widget.CompoundButton.Switch</item>
</style>