J'ai essayé de faire des composants personnalisés. J'ai étendu la classe View
et dessiné dans la méthode onDraw
surchargée. Pourquoi dois-je remplacer onMeasure
? Si je ne le faisais pas, tout se passerait bien. Quelqu'un peut-il l'expliquer? Comment dois-je écrire ma méthode onMeasure
? J'ai vu quelques tutoriels, mais chacun est un peu différent de l'autre. Parfois, ils appellent super.onMeasure
à la fin, parfois, ils utilisent setMeasuredDimension
sans l'appeler. Où est la différence?
Après tout, je veux utiliser plusieurs composants identiques. J'ai ajouté ces composants à mon fichier XML
, mais je ne sais pas quelle taille ils devraient avoir. Je souhaite définir sa position et sa taille ultérieurement (pourquoi je dois définir la taille dans onMeasure
si dans onDraw
lorsque je le dessine, fonctionne également) dans la classe de composants personnalisés. Quand exactement dois-je faire cela?
onMeasure()
est l'occasion pour vous de dire à Android à quel point vous souhaitez que votre affichage personnalisé soit dépendant des contraintes de présentation fournies par le parent; c'est également l'occasion pour votre vue personnalisée d'apprendre quelles sont ces contraintes de présentation (si vous voulez vous comporter différemment dans une situation match_parent
que dans une situation wrap_content
). Ces contraintes sont regroupées dans les valeurs MeasureSpec
qui sont transmises à la méthode. Voici une corrélation approximative des valeurs de mode:
layout_width
ou layout_height
a été définie sur une valeur spécifique. Vous devriez probablement faire de votre point de vue cette taille. Cela peut également être déclenché lorsque match_parent
est utilisé, pour définir la taille exactement de la vue parente (ceci dépend de la présentation dans le cadre).layout_width
ou layout_height
a été définie sur match_parent
ou wrap_content
où une taille maximale est requise (cette disposition dépend de la cadre) et la taille de la dimension parent est la valeur. Vous ne devriez pas être plus grand que cette taille.layout_width
ou layout_height
a été définie sur wrap_content
sans aucune restriction. Vous pouvez avoir la taille que vous souhaitez. Certaines présentations utilisent également ce rappel pour déterminer la taille souhaitée avant de déterminer les spécifications à appliquer pour vous transmettre à nouveau dans une deuxième demande de mesure.Le contrat qui existe avec onMeasure()
est que setMeasuredDimension()
DOIT être appelé à la fin avec la taille souhaitée pour la vue. Cette méthode est appelée par toutes les implémentations d'infrastructure, y compris l'implémentation par défaut trouvée dans View
. C'est pourquoi il est prudent d'appeler super
si cela convient à votre cas d'utilisation.
Certes, du fait que le framework applique une implémentation par défaut, il peut ne pas être nécessaire de remplacer cette méthode, mais vous risquez de voir des coupures dans les cas où l'espace d'affichage est plus petit que votre contenu. Si vous ne le faites pas, et vue personnalisée avec wrap_content
dans les deux sens, votre vue risque de ne pas s'afficher du tout car le cadre ne sait pas quelle est sa taille!
Généralement, si vous écrasez View
et non un autre widget existant, il est probablement judicieux de fournir une implémentation, même si elle est aussi simple que ceci:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int desiredWidth = 100;
int desiredHeight = 100;
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
//Measure Width
if (widthMode == MeasureSpec.EXACTLY) {
//Must be this size
width = widthSize;
} else if (widthMode == MeasureSpec.AT_MOST) {
//Can't be bigger than...
width = Math.min(desiredWidth, widthSize);
} else {
//Be whatever you want
width = desiredWidth;
}
//Measure Height
if (heightMode == MeasureSpec.EXACTLY) {
//Must be this size
height = heightSize;
} else if (heightMode == MeasureSpec.AT_MOST) {
//Can't be bigger than...
height = Math.min(desiredHeight, heightSize);
} else {
//Be whatever you want
height = desiredHeight;
}
//MUST CALL THIS
setMeasuredDimension(width, height);
}
J'espère que ça t'as aidé.
en fait, votre réponse n'est pas complète car les valeurs dépendent également du conteneur d'habillage. En cas de dispositions relatives ou linéaires, les valeurs se comportent comme suit:
Dans le cas d'une vue de défilement horizontal, votre code fonctionnera.
Si vous n'avez pas besoin de changer quelque chose surMeasure, vous n'avez absolument pas besoin de l'annuler.
Le code de Devunwired (la réponse sélectionnée et la plus votée ici) est presque identique à ce que l'implémentation du SDK fait déjà pour vous (et j'ai vérifié - c'est ce qu'il fait depuis 2009).
Vous pouvez vérifier la méthode onMeasure here :
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
Remplacer le code SDK par le même code n'a aucun sens.
Ce document officiel du document qui déclare que "la valeur par défaut onMeasure () définira toujours une taille de 100x100" - est incorrect.