GetWidth() et getHeight() de View renvoie 0
je crée dynamiquement tous les éléments de mon projet android. J'essaie d'obtenir la largeur et la hauteur d'un bouton, afin que je puisse faire tourner ce bouton autour de. J'essaie juste d'apprendre à travailler avec le langage android. Cependant, il renvoie 0.
j'ai fait quelques recherches et j'ai vu que cela devait être fait ailleurs que dans la méthode onCreate()
. Si quelqu'un peut me donner un exemple de comment le faire, ce serait génial.
Voici mon code actuel:
package com.animation;
import android.app.Activity;
import android.os.Bundle;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.Button;
import android.widget.LinearLayout;
public class AnimateScreen extends Activity {
//Called when the activity is first created.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout ll = new LinearLayout(this);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
layoutParams.setMargins(30, 20, 30, 0);
Button bt = new Button(this);
bt.setText(String.valueOf(bt.getWidth()));
RotateAnimation ra = new RotateAnimation(0,360,bt.getWidth() / 2,bt.getHeight() / 2);
ra.setDuration(3000L);
ra.setRepeatMode(Animation.RESTART);
ra.setRepeatCount(Animation.INFINITE);
ra.setInterpolator(new LinearInterpolator());
bt.startAnimation(ra);
ll.addView(bt,layoutParams);
setContentView(ll);
}
Toute aide est appréciée.
10 réponses
vous appelez getWidth()
trop tôt. L'interface utilisateur n'a pas encore été dimensionnée et affichée à l'écran.
je doute que vous vouliez faire ce que vous faites, de toute façon -- les widgets étant animés ne changent pas leurs zones cliquables, et donc le bouton répondra toujours aux clics dans l'orientation d'origine indépendamment de la façon dont il a tourné.
cela dit, Vous pouvez utiliser un dimension resource pour définir la taille du bouton, puis référencez cette ressource de dimension à partir de votre fichier de mise en page et de votre code source, pour éviter ce problème.
le problème de base est, que vous devez attendre la phase de dessin pour les mesures réelles (en particulier avec des valeurs dynamiques comme wrap_content
ou match_parent
), mais habituellement cette phase n'a pas été terminée jusqu'à onResume()
. Donc vous avez besoin d'une solution pour attendre cette phase. Il y a différentes solutions possibles à cela:
1. Écouter Dessin/Mise en page des Événements: ViewTreeObserver
un ViewTreeObserver est mis à feu pour différents événements de dessin. Généralement le OnGlobalLayoutListener
est ce que vous voulez pour obtenir la mesure, de sorte que le code dans l'auditeur sera appelé après la phase de mise en page, de sorte que les mesures sont prêtes:
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
view.getHeight(); //height is ready
}
});
Note: l'écouteur sera immédiatement supprimé car sinon il se déclenchera sur chaque événement de mise en page. Si vous devez prendre en charge les applications SDK Lvl < 16 utilisez ceci pour désinscrire l'auditeur:
public void removeGlobalOnLayoutListener (ViewTreeObserver.OnGlobalLayoutListener victim)
2. Ajoutez une exécutable à la file d'attente layout: View.post ()
pas très connu et ma solution préférée. Fondamentalement, il suffit d'utiliser la méthode POST de la vue avec votre propre runnable. Cela fait la file d'attente de votre code après la mesure de la vue, la disposition, etc. comme indiqué par Romain Guy :
la file D'attente D'événements UI traitera les événements dans l'ordre. Après setContentView () est invoqué, la file d'attente event contiendra un message demande d'un relayout, pour que tout ce que vous postez à la file d'attente arrivera après la mise en page
exemple:
final View view=//smth;
...
view.post(new Runnable() {
@Override
public void run() {
view.getHeight(); //height is ready
}
});
L'avantage ViewTreeObserver
:
- votre code n'est exécuté qu'une fois et vous n'avez pas à désactiver l'Observateur après l'exécution qui peut être embêtant
- syntaxe moins verbeuse
, les Références:
3. Écraser les Vues du onLayout Méthode
ce n'est pratique que dans certaines situations où la logique peut être encapsulée dans la vue elle-même, sinon c'est une syntaxe très verbeuse et lourde.
view = new View(this) {
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
view.getHeight(); //height is ready
}
};
aussi l'esprit, que l'onLayout sera appelé plusieurs fois, ainsi soit considérer ce que vous faites dans la méthode, ou désactiver votre code après la première fois
4. Vérifier si a été par le biais de la disposition de la phase
si vous avez un code qui est exécuté plusieurs fois tout en créant l'ui, vous pouvez utiliser la méthode de soutien v4 lib suivante:
View viewYouNeedHeightFrom = ...
...
if(ViewCompat.isLaidOut(viewYouNeedHeightFrom)) {
viewYouNeedHeightFrom.getHeight();
}
retourne true si la vue a été à travers au moins un layout depuis qu'elle a été dernière attaché ou détaché d'une fenêtre.
supplémentaire: obtenir statique mesures définies
S'il suffit d'obtenir la hauteur/largeur définie statiquement, vous pouvez simplement le faire avec:
mais attention, cela pourrait être différent de la largeur/hauteur réelle après dessin. Le javadoc décrit parfaitement la différence:
la taille d'une vue est exprimée avec une largeur et une hauteur. Vue effectivement posséder deux paires de valeurs de largeur et hauteur.
la première paire est appelée largeur mesurée et hauteur mesurée. Ils les dimensions définissent la taille qu'une vue veut avoir à l'intérieur de son parent (voir Mise en page pour plus de détails.) Les dimensions mesurées peuvent être obtenues par appel getMeasuredWidth () et getMeasuredHeight ().
la deuxième paire est connu comme la largeur et la hauteur, ou parfois largeur du dessin et hauteur du dessin. Ces dimensions définissent le taille de la vue à l'écran, au moment du dessin et après la mise en page. Ils les valeurs peuvent, mais ne doivent pas, être différente de la largeur mesurée et la hauteur. La largeur et la hauteur peuvent être obtenues en appelant getWidth() et getHeight().
nous pouvons utiliser
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
//Here you can get the size!
}
j'ai utilisé cette solution, qui je pense est meilleure que onWindowFocusChanged(). Si vous ouvrez un fragment de dialogue, puis tournez le téléphone, onWindowFocusChanged sera appelé seulement lorsque l'utilisateur ferme la boîte de dialogue):
yourView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
// Ensure you call it only once :
yourView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
// Here you can get the size :)
}
});
Edit : comme removeGlobalOnLayoutListener est obsolète, vous devez faire maintenant :
@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
@Override
public void onGlobalLayout() {
// Ensure you call it only once :
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
yourView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
else {
yourView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
// Here you can get the size :)
}
comme Ian l'indique dans ce fil de Développeurs Android :
quoi qu'il en soit, le marché est que la disposition de la le contenu de la fenêtre arrive après tous les éléments sont construits et ajoutés à leur parent vue. Il doit être de cette façon, parce que jusqu'à ce que vous sachiez quels composants une vue contient, et ce qu'ils contiennent, et donc, il n'y a aucun moyen raisonnable que vous puissiez étalez-la.
en bref, si vous appelez getWidth() etc. dans un constructeur, il reviendra zéro. La procédure est de créer tout vos éléments de vue dans le constructeur, alors attendez que votre vue méthode onSizeChanged () à appeler -- c'est alors que vous devez d'abord trouver votre taille réelle, de sorte que lorsque vous configurez les tailles de vos éléments GUI.
sachez aussi que onSizeChanged () est parfois appelé avec des paramètres de zéro -- Vérifiez cette affaire, et retourner immédiatement (pour ne pas avoir diviser par zéro lors du calcul de votre mise en page, etc.). Quelques temps plus tard, il sera appelé avec les valeurs réelles.
si vous avez besoin d'obtenir la largeur d'un widget avant qu'il ne soit affiché à l'écran, vous pouvez utiliser getMeasuredWidth() ou getMeasuredHeight().
myImage.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
int width = myImage.getMeasuredWidth();
int height = myImage.getMeasuredHeight();
je préférerais utiliser OnPreDrawListener()
au lieu de addOnGlobalLayoutListener()
, car il est appelé plus tôt.
view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener()
{
@Override
public boolean onPreDraw()
{
if (view.getViewTreeObserver().isAlive())
view.getViewTreeObserver().removeOnPreDrawListener(this);
// put your code here
return false;
}
});
Un liner si vous utilisez RxJava & RxBindings . Approche similaire sans le passe-partout. Cela résout également le hack pour supprimer les avertissements comme dans la réponse par Tim Autin .
RxView.layoutChanges(yourView).take(1)
.subscribe(aVoid -> {
// width and height have been calculated here
});
C'est ici. Pas besoin de vous désinscrire, même si jamais appelé.
Extension pour obtenir la hauteur à Kotlin dynamiquement.
Utilisation:
view.height { Log.i("Info", "Here is your height:" + it) }
mise en œuvre:
fun <T : View> T.height(function: (Int) -> Unit) {
if (height == 0)
viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
viewTreeObserver.removeOnGlobalLayoutListener(this)
function(height)
}
})
else function(height)
}
Gone views retourne 0 comme hauteur si app en arrière-plan. Voici mon code (1oo% travaux)
fun View.postWithTreeObserver(postJob: (View, Int, Int) -> Unit) {
viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
val widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
val heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
measure(widthSpec, heightSpec)
postJob(this@postWithTreeObserver, measuredWidth, measuredHeight)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
@Suppress("DEPRECATION")
viewTreeObserver.removeGlobalOnLayoutListener(this)
} else {
viewTreeObserver.removeOnGlobalLayoutListener(this)
}
}
})
}