J'ai une vue composée de TableLayout, TableRow and TextView
. Je veux que ça ressemble à une grille. Je dois obtenir la hauteur et la largeur de cette grille. Les méthodes getHeight()
et getWidth()
renvoient toujours 0. Cela se produit lorsque je formate la grille dynamiquement et lorsque j'utilise une version XML.
Comment récupérer les dimensions pour une vue?
Voici mon programme de test que j'ai utilisé dans Debug pour vérifier les résultats:
import Android.app.Activity;
import Android.os.Bundle;
import Android.widget.TableLayout;
import Android.widget.TextView;
public class appwig extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.maindemo); //<- includes the grid called "board"
int vh = 0;
int vw = 0;
//Test-1 used the xml layout (which is displayed on the screen):
TableLayout tl = (TableLayout) findViewById(R.id.board);
tl = (TableLayout) findViewById(R.id.board);
vh = tl.getHeight(); //<- getHeight returned 0, Why?
vw = tl.getWidth(); //<- getWidth returned 0, Why?
//Test-2 used a simple dynamically generated view:
TextView tv = new TextView(this);
tv.setHeight(20);
tv.setWidth(20);
vh = tv.getHeight(); //<- getHeight returned 0, Why?
vw = tv.getWidth(); //<- getWidth returned 0, Why?
} //eof method
} //eof class
Je pense que le PO est parti depuis longtemps, mais au cas où cette réponse serait en mesure d’aider les futurs chercheurs, je pensais publier une solution que j’avais trouvée. J'ai ajouté ce code dans ma méthode onCreate()
:
EDITED: 07/05/11 pour inclure le code des commentaires:
final TextView tv = (TextView)findViewById(R.id.image_test);
ViewTreeObserver vto = tv.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
LayerDrawable ld = (LayerDrawable)tv.getBackground();
ld.setLayerInset(1, 0, tv.getHeight() / 2, 0, 0);
ViewTreeObserver obs = tv.getViewTreeObserver();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
obs.removeOnGlobalLayoutListener(this);
} else {
obs.removeGlobalOnLayoutListener(this);
}
}
});
J'obtiens d'abord une dernière référence à ma TextView
(à accéder à la méthode onGlobalLayout()
). Ensuite, j'obtiens la ViewTreeObserver
de ma TextView
et j'ajoute une OnGlobalLayoutListener
, surchargeant onGLobalLayout
(il ne semble pas y avoir de méthode de superclasse à invoquer ici ...) et en ajoutant mon code qui nécessite de connaître les mesures de la vue dans cet écouteur. Tout fonctionne comme prévu pour moi, alors j'espère que cela pourra vous aider.
Je vais simplement ajouter une solution alternative, remplacer la méthode onWindowFocusChanged de votre activité et vous pourrez obtenir les valeurs de getHeight (), getWidth () à partir de là.
@Override
public void onWindowFocusChanged (boolean hasFocus) {
// the height will be set at this point
int height = myEverySoTallView.getMeasuredHeight();
}
Vous essayez d’obtenir la largeur et la hauteur d’un élément qui n’a pas encore été dessiné.
Si vous utilisez le débogage et que vous vous arrêtez à un moment donné, vous verrez que l'écran de votre périphérique est toujours vide, c'est parce que vos éléments n'ont pas encore été dessinés. Vous ne pouvez donc pas obtenir la largeur et la hauteur de quelque chose qui ne l'est pas encore. exister.
Et je me trompe peut-être, mais setWidth()
n'est pas toujours respecté, Layout
définit ses enfants et décide comment les mesurer (appelant child.measure()
). Ainsi, si vous définissez setWidth()
, vous n'êtes pas assuré d'obtenir cette largeur après l'élément sera dessiné.
Ce dont vous avez besoin, c’est d’utiliser getMeasuredWidth()
(la mesure la plus récente de votre vue) quelque part après l’affichage de la vue.
Examinez le cycle de vie Activity
pour trouver le meilleur moment.
http://developer.Android.com/reference/Android/app/Activity.html#ActivityLifecycle
Je pense qu'une bonne pratique consiste à utiliser OnGlobalLayoutListener
comme ceci:
yourView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (!mMeasured) {
// Here your view is already layed out and measured for the first time
mMeasured = true; // Some optional flag to mark, that we already got the sizes
}
}
});
Vous pouvez placer ce code directement dans onCreate()
et il sera invoqué lors de la présentation des vues.
Utilisez la méthode de publication de la vue comme ceci
post(new Runnable() {
@Override
public void run() {
Log.d(TAG, "width " + MyView.this.getMeasuredWidth());
}
});
J'ai essayé d'utiliser onGlobalLayout()
pour effectuer le formatage personnalisé d'une TextView
, mais comme @George Bailey l'a remarqué, onGlobalLayout()
est en effet appelé deux fois: une fois sur le chemin de présentation initial et une seconde fois après modification du texte.
View.onSizeChanged()
fonctionne mieux pour moi car si je modifie le texte à cet endroit, la méthode n'est appelée qu'une fois (pendant la passe de présentation). Cette sous-classification requise de TextView
, mais sur l'API de niveau 11+ - View. addOnLayoutChangeListener()
peut être utilisée pour éviter la sous-classification.
Une dernière chose, pour obtenir la largeur correcte de la vue dans View.onSizeChanged()
, le layout_width
doit être défini sur match_parent
et non pas wrap_content
.
Essayez-vous d’obtenir des tailles dans un constructeur, ou toute autre méthode exécutée AVANT d’obtenir l’image réelle?
Vous n'obtiendrez aucune dimension avant que tous les composants soient réellement mesurés (puisque votre xml ne connaît pas la taille de votre écran, la position de vos parents, etc.).
Essayez d’obtenir des valeurs après onSizeChanged () (bien que vous puissiez l’appeler avec zéro), ou tout simplement d’attendre que vous obteniez une image réelle.
Je suppose que c’est ce que vous devez regarder: utilisez onSizeChanged()
de votre point de vue. Voici un extrait de code EXTENDED indiquant comment utiliser onSizeChanged()
pour obtenir la hauteur et la largeur de votre présentation ou de votre vue de manière dynamique http://syedrakibalhasan.blogspot.com/2011/02/how-to-get-width-and-height-dimensions .html
Comme F.X. mentionné, vous pouvez utiliser un OnLayoutChangeListener
à la vue que vous voulez suivre lui-même
view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
// Make changes
}
});
Vous pouvez supprimer l'écouteur dans le rappel si vous souhaitez uniquement la disposition initiale.
ViewTreeObserver
et onWindowFocusChanged()
ne sont pas du tout nécessaires.
Si vous gonflez la TextView
en tant que disposition et/ou y mettez du contenu et définissez LayoutParams
, vous pouvez utiliser getMeasuredHeight()
et getMeasuredWidth()
.
MAISvous devez être prudent avec LinearLayouts
(peut-être aussi un autre ViewGroups
). Le problème est que vous pouvez obtenir la largeur et la hauteur après onWindowFocusChanged()
, mais si vous essayez d'y ajouter des vues, vous ne pourrez pas obtenir ces informations tant que tout n'aura pas été dessiné. J'essayais d'ajouter plusieurs variables TextViews
à LinearLayouts
pour imiter une FlowLayout
(style d'habillage) et je ne pouvais donc pas utiliser Listeners
. Une fois le processus démarré, il devrait continuer de manière synchrone. Dans ce cas, vous voudrez peut-être conserver la largeur d'une variable pour pouvoir l'utiliser ultérieurement, car lors de l'ajout de vues à la présentation, vous en aurez peut-être besoin.
Même si la solution proposée fonctionne, il se peut que ce ne soit pas la meilleure solution pour tous les cas, car elle repose sur la documentation de ViewTreeObserver.OnGlobalLayoutListener
Définition d'interface pour un rappel à invoquer lorsque l'état de la mise en forme globale ou la visibilité des vues dans l'arborescence de vues change.
ce qui signifie qu'il est appelé plusieurs fois et que la vue n'est pas toujours mesurée (sa hauteur et sa largeur sont déterminées)
Une alternative consiste à utiliser ViewTreeObserver.OnPreDrawListener
, qui n'est appelé que lorsque la vue est prête à être dessinée et qu'elle contient toutes ses mesures.
final TextView tv = (TextView)findViewById(R.id.image_test);
ViewTreeObserver vto = tv.getViewTreeObserver();
vto.addOnPreDrawListener(new OnPreDrawListener() {
@Override
public void onPreDraw() {
tv.getViewTreeObserver().removeOnPreDrawListener(this);
// Your view will have valid height and width at this point
tv.getHeight();
tv.getWidth();
}
});
La hauteur et la largeur sont égales à zéro car la vue n'a pas été créée au moment où vous demandez sa hauteur et sa largeur. Une solution la plus simple est
view.post(new Runnable() {
@Override
public void run() {
view.getHeight(); //height is ready
view.getWidth(); //width is ready
}
});
Cette méthode est bonne comparée à d’autres méthodes car elle est courte et nette.
Vous devriez plutôt consulter le cycle de vie de View: http://developer.Android.com/reference/Android/view/View.html En règle générale, vous ne devez pas connaître la largeur et la hauteur tant que votre activité n’a pas atteint l’état onResume.
Vous pouvez utiliser une diffusion appelée dans OnResume ()
Par exemple:
int vh = 0;
int vw = 0;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.maindemo); //<- includes the grid called "board"
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
TableLayout tl = (TableLayout) findViewById(R.id.board);
tl = (TableLayout) findViewById(R.id.board);
vh = tl.getHeight();
vw = tl.getWidth();
}
}, new IntentFilter("Test"));
}
protected void onResume() {
super.onResume();
Intent it = new Intent("Test");
sendBroadcast(it);
}
Vous ne pouvez pas obtenir la hauteur d'une vue dans OnCreate (), onStart () ou même dans onResume () car kcoppock a répondu