Comment obtenir les coordonnées absolues d'une vue

390

J'essaie d'obtenir les coordonnées absolues en pixels de l'écran dans le coin supérieur gauche d'une vue. Cependant, toutes les méthodes que je peux trouver telles que getLeft()et getRight()ne fonctionnent pas car elles semblent toutes être relatives au parent de la vue, me donnant ainsi 0. Quelle est la bonne façon de procéder?

Si cela aide, c'est pour un jeu de «remettre l'image en ordre». Je veux que l'utilisateur puisse dessiner une boîte pour sélectionner plusieurs pièces. Mon hypothèse est que la façon la plus simple de le faire est de getRawX()et getRawY()vers le MotionEventpuis de comparer ces valeurs avec le coin supérieur gauche de la mise en page contenant les pièces. Connaissant la taille des pièces, je peux alors déterminer combien de pièces ont été sélectionnées. Je me rends compte que je peux utiliser getX()et getY()sur MotionEvent, mais comme cela renvoie une position relative qui rend plus difficile la détermination des pièces sélectionnées. (Pas impossible, je sais, mais cela semble inutilement compliqué).

Edit: C'est le code que j'ai utilisé pour essayer d'obtenir la taille du conteneur de stockage, selon l'une des questions. TableLayoutest la table qui contient toutes les pièces du puzzle.

TableLayout tableLayout = (TableLayout) findViewById(R.id.tableLayout);
Log.d(LOG_TAG, "Values " + tableLayout.getTop() + tableLayout.getLeft());

Edit 2: Voici le code que j'ai essayé, en suivant plus des réponses suggérées.

public int[] tableLayoutCorners = new int[2];
(...)

TableLayout tableLayout = (TableLayout) findViewById(R.id.tableLayout);
tableLayout.requestLayout();
Rect corners = new Rect();
tableLayout.getLocalVisibleRect(corners);
Log.d(LOG_TAG, "Top left " + corners.top + ", " + corners.left + ", " + corners.right
            + ", " + corners.bottom);

cells[4].getLocationOnScreen(tableLayoutCorners);
Log.d(LOG_TAG, "Values " + tableLayoutCorners[0] + ", " + tableLayoutCorners[1]);

Ce code a été ajouté une fois l'initialisation terminée. L'image a été divisée en un tableau d'ImageViews (le tableau de cellules []) contenu dans a TableLayout. Les cellules [0] sont en haut à gauche ImageView, et j'ai choisi les cellules [4] car elles se trouvent quelque part au milieu et ne devraient certainement pas avoir de coordonnées (0,0).

Le code ci-dessus me donne toujours tous les 0 dans les journaux, ce que je ne comprends vraiment pas car les différentes pièces du puzzle sont correctement affichées. (J'ai essayé public int pour tableLayoutCorners et la visibilité par défaut, les deux donnant le même résultat.)

Je ne sais pas si c'est important, mais à l' ImageVieworigine , les s n'ont pas de taille. La taille du ImageViews est déterminée automatiquement lors de l'initialisation par la vue lorsque je lui donne une image à afficher. Est-ce que cela pourrait contribuer à ce que leurs valeurs soient 0, même si ce code de journalisation se trouve après avoir reçu une image et se redimensionner automatiquement? Pour potentiellement contrer cela, j'ai ajouté le code tableLayout.requestLayout()comme indiqué ci-dessus, mais cela n'a pas aidé.

Steve Haley
la source

Réponses:

632

Utilisez View.getLocationOnScreen()et / ou getLocationInWindow().

Romain Guy
la source
3
Voilà le genre de fonction que je m'attendais à trouver. Malheureusement, je ne dois pas l'utiliser correctement. Je me rends compte que c'était un bogue connu que getLocationOnScreen donne une exception de pointeur nul dans Android v1.5 (la plate-forme pour laquelle je code), mais les tests dans v2.1 ne fonctionnaient pas mieux. Les deux méthodes m'ont donné 0 pour tout. J'ai inclus un extrait de code ci-dessus.
Steve Haley
76
Vous ne pouvez l'invoquer qu'APRÈS la mise en page. Vous appelez la méthode avant que les vues ne soient positionnées à l'écran.
Romain Guy
5
Aha, tu as raison même s'il n'était pas évident que je faisais ça. Merci! Pour toute personne curieuse de plus de détails, j'ai ajouté une réponse qui explique ce que je veux dire.
Steve Haley
9
Vous voulez dire comme le ViewTreeObserver et son OnGlobalLayoutListener? Voir View.getViewTreeObserver () pour commencer.
Romain Guy
8
@RomainGuy Ouais, un peu comme ça, mais moins déroutant que de devoir s'inscrire (puis se désinscrire…). C'est un domaine où iOS le fait mieux qu'Android en termes de SDK. En utilisant les deux au quotidien, je peux dire que iOS a beaucoup de choses ennuyeuses, mais la gestion UIView n'en fait pas partie. :)
Martin Marconcini
127

La réponse acceptée ne disait pas comment obtenir l'emplacement, alors voici un peu plus de détails. Vous passez dans un inttableau de longueur 2 et les valeurs sont remplacées par les coordonnées (x, y) de la vue (du coin supérieur gauche).

int[] location = new int[2];
myView.getLocationOnScreen(location);
int x = location[0];
int y = location[1];

Remarques

  • Le remplacement getLocationOnScreenpar getLocationInWindowdevrait donner les mêmes résultats dans la plupart des cas (voir cette réponse ). Cependant, si vous êtes dans une fenêtre plus petite comme une boîte de dialogue ou un clavier personnalisé, vous devrez alors choisir celui qui convient le mieux à vos besoins.
  • Vous obtiendrez (0,0)si vous appelez cette méthode onCreatecar la vue n'a pas encore été présentée. Vous pouvez utiliser un ViewTreeObserverpour écouter lorsque la mise en page est terminée et vous pouvez obtenir les coordonnées mesurées. (Voir cette réponse .)
Suragch
la source
86

Vous devez d'abord obtenir le rectangle localVisible de la vue

Par exemple:

Rect rectf = new Rect();

//For coordinates location relative to the parent
anyView.getLocalVisibleRect(rectf);

//For coordinates location relative to the screen/display
anyView.getGlobalVisibleRect(rectf);

Log.d("WIDTH        :", String.valueOf(rectf.width()));
Log.d("HEIGHT       :", String.valueOf(rectf.height()));
Log.d("left         :", String.valueOf(rectf.left));
Log.d("right        :", String.valueOf(rectf.right));
Log.d("top          :", String.valueOf(rectf.top));
Log.d("bottom       :", String.valueOf(rectf.bottom));

J'espère que cela vous aidera

Naveen Murthy
la source
3
Cela aurait également dû fonctionner ... Cependant, cela me donne également des valeurs de 0. J'ai inclus un extrait de code ci-dessus si cela vous aide à comprendre ce que j'ai fait de mal. Merci encore.
Steve Haley
1
Voir mes autres commentaires; J'appelais accidentellement cela avant que les Views aient fini de se présenter. Cela fonctionne bien maintenant merci.
Steve Haley
@Naveen Murthy donne des coordonnées à une vue spécifique, j'ai besoin des coordonnées de la vue cliquée dans la disposition racine ..
Aniket
20
cela renvoie l'emplacement par rapport au parent. utiliser getGlobalVisibleRect()pour les coordonnées absolues.
Jeffrey Blattman
3
@SteveHaley, j'ai été confronté au même problème que vous, pour une raison quelconque, cela me donne 0 dans chaque méthode que j'essaie. Il semble que vous ayez compris pourquoi cela donne zéro. J'essaie d'obtenir les coordonnées de la vue une fois la mise en page chargée, j'obtiens zéro, pourquoi? Merci pour votre aide
Pankaj Nimgade
46

L'utilisation d'un écouteur de mise en page globale a toujours bien fonctionné pour moi. Il a l'avantage de pouvoir remesurer les choses si la disposition est modifiée, par exemple si quelque chose est réglé sur View.GONE ou que des vues enfants sont ajoutées / supprimées.

public void onCreate(Bundle savedInstanceState)
{
     super.onCreate(savedInstanceState);

     // inflate your main layout here (use RelativeLayout or whatever your root ViewGroup type is
     LinearLayout mainLayout = (LinearLayout ) this.getLayoutInflater().inflate(R.layout.main, null); 

     // set a global layout listener which will be called when the layout pass is completed and the view is drawn
     mainLayout.getViewTreeObserver().addOnGlobalLayoutListener(
       new ViewTreeObserver.OnGlobalLayoutListener() {
          public void onGlobalLayout() {
               //Remove the listener before proceeding
               if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    mainLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
               } else {
                    mainLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
               }

               // measure your views here
          }
       }
     );

     setContentView(mainLayout);
 }
Simon
la source
N'oubliez pas de supprimer OnGlobalLayoutListener à l'intérieur (à la fin de) onGlobalLayout () {...}. Aussi, juste pour être sûr, vérifiez si ce qui s'est passé! = 0; l'auditeur peut être appelé deux fois, une fois avec une valeur de w = 0 / h = 0, la deuxième fois avec les valeurs réelles et correctes.
MacD
Cette façon de gonfler la mise en page principale puis de l'utiliser a setContentView()causé un problème dans une spéciale Activityde mon application! C'était une activité de dialogue ( windowFrame:@null, windowActionBar:false, windowIsFloating:true, windowNoTitle:true). Sa disposition principale (a LinearLayout) n'a pas d'enfant direct avec déterminé layout_width(tous étaient match_parentou wrap_content).
Mir-Ismaili
17

Suite au commentaire de Romain Guy, voici comment je l'ai corrigé. J'espère que cela aidera toute autre personne qui a également eu ce problème.

J'essayais en effet d'obtenir les positions des vues avant qu'elles ne soient affichées à l'écran mais ce n'était pas du tout évident que cela se produisait. Ces lignes avaient été placées après l'exécution du code d'initialisation, j'ai donc supposé que tout était prêt. Cependant, ce code était toujours dans onCreate (); en expérimentant avec Thread.sleep (), j'ai découvert que la mise en page n'est réellement finalisée qu'après la fin de l'exécution de onCreate () jusqu'à onResume (). Donc, en effet, le code essayait de s'exécuter avant que la disposition n'ait fini d'être positionnée à l'écran. En ajoutant le code à un OnClickListener (ou à un autre écouteur), les valeurs correctes ont été obtenues car il ne pouvait être déclenché qu'une fois la mise en page terminée.


La ligne ci-dessous a été suggérée comme une modification communautaire:

veuillez utiliser onWindowfocuschanged(boolean hasFocus)

Steve Haley
la source
Vous voulez donc dire que setContentView (my_layout); ne placera pas toutes les vues. Toutes les vues ne seront placées qu'après la fin de onCreate ()?
Ashwin
5
Pas assez. Toutes les vues «existent» après setContentView(R.layout.something), mais elles n'ont pas été chargées visuellement. Ils n'ont ni taille, ni position. Cela ne se produit que lorsque la première passe de mise en page est terminée, ce qui se produit à un moment donné dans le futur (généralement après onResume ()).
Steve Haley
14

Première voie:

Dans Kotlin, nous pouvons créer une extension simple pour la vue:

fun View.getLocationOnScreen(): Point
{
    val location = IntArray(2)
    this.getLocationOnScreen(location)
    return Point(location[0],location[1])
}

Et obtenez simplement les coordonnées:

val location = yourView.getLocationOnScreen()
val absX = location.x
val absY = location.y

Deuxième voie:

La deuxième façon est plus simple:

fun View.absX(): Int
{
    val location = IntArray(2)
    this.getLocationOnScreen(location)
    return location[0]
}

fun View.absY(): Int
{
    val location = IntArray(2)
    this.getLocationOnScreen(location)
    return location[1]
}

et obtenir simplement X absolu view.absX()et Y parview.absY()

Amir Hossein Ghasemi
la source
6
Une version plus courte pour votre 1er échantillon:val location = IntArray(2).apply(::getLocationOnScreen)
Tuan Chau
5

Mes utilitaires fonctionnent pour obtenir l'emplacement de la vue, il retournera un Pointobjet avec x valueety value

public static Point getLocationOnScreen(View view){
    int[] location = new int[2];
    view.getLocationOnScreen(location);
    return new Point(location[0], location[1]);
}

En utilisant

Point viewALocation = getLocationOnScreen(viewA);
Phan Van Linh
la source
5

Vous pouvez obtenir les coordonnées d'une vue en utilisant getLocationOnScreen()ougetLocationInWindow()

Ensuite, xet ydevrait être le coin supérieur gauche de la vue. Si votre disposition racine est plus petite que l'écran (comme dans une boîte de dialogue), l'utilisation getLocationInWindowsera relative à son conteneur, pas à tout l'écran.

Solution Java

int[] point = new int[2];
view.getLocationOnScreen(point); // or getLocationInWindow(point)
int x = point[0];
int y = point[1];

REMARQUE: si la valeur est toujours 0, vous modifiez probablement la vue immédiatement avant de demander l'emplacement.

Pour vous assurer que la vue a pu être mise à jour, exécutez votre demande de localisation une fois que la nouvelle disposition de la vue a été calculée en utilisant view.post:

view.post(() -> {
    // Values should no longer be 0
    int[] point = new int[2];
    view.getLocationOnScreen(point); // or getLocationInWindow(point)
    int x = point[0];
    int y = point[1];
});

~~

Kotlin Solution

val point = IntArray(2)
view.getLocationOnScreen(point) // or getLocationInWindow(point)
val (x, y) = point

REMARQUE: si la valeur est toujours 0, vous modifiez probablement la vue immédiatement avant de demander l'emplacement.

Pour vous assurer que la vue a pu être mise à jour, exécutez votre demande de localisation une fois que la nouvelle disposition de la vue a été calculée en utilisant view.post:

view.post {
    // Values should no longer be 0
    val point = IntArray(2)
    view.getLocationOnScreen(point) // or getLocationInWindow(point)
    val (x, y) = point
}

Je recommande de créer une fonction d'extension pour gérer cela:

// To use, call:
val (x, y) = view.screenLocation

val View.screenLocation get(): IntArray {
    val point = IntArray(2)
    getLocationOnScreen(point)
    return point
}

Et si vous avez besoin de fiabilité, ajoutez également:

view.screenLocationSafe { x, y -> Log.d("", "Use $x and $y here") }

fun View.screenLocationSafe(callback: (Int, Int) -> Unit) {
    post {
        val (x, y) = screenLocation
        callback(x, y)
    }
}
Gibolt
la source
0

Utilisez ce code pour trouver les coordonnées exactes X et Y:

int[] array = new int[2];
ViewForWhichLocationIsToBeFound.getLocationOnScreen(array);
if (AppConstants.DEBUG)
    Log.d(AppConstants.TAG, "array  X = " + array[0] + ", Y = " + array[1]);
ViewWhichToBeMovedOnFoundLocation.setTranslationX(array[0] + 21);
ViewWhichToBeMovedOnFoundLocation.setTranslationY(array[1] - 165);

J'ai ajouté / soustrait des valeurs pour ajuster ma vue. Veuillez ne faire ces lignes qu'après avoir gonflé la vue entière.

Tarun
la source
"J'ai ajouté / soustrait certaines valeurs pour ajuster ma vue." Pourquoi??
ToolmakerSteve
Je veux dire selon mon exigence (placement de la vue), j'ai ajouté et soustrait des valeurs pour ajuster ma vue.
Tarun
0

Afficher à la fois la position et la dimension de l'affichage

val viewTreeObserver: ViewTreeObserver = videoView.viewTreeObserver;

    if (viewTreeObserver.isAlive) {
        viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                //Remove Listener
                videoView.viewTreeObserver.removeOnGlobalLayoutListener(this);

                //View Dimentions
                viewWidth = videoView.width;
                viewHeight = videoView.height;

                //View Location
                val point = IntArray(2)
                videoView.post {
                    videoView.getLocationOnScreen(point) // or getLocationInWindow(point)
                    viewPositionX = point[0]
                    viewPositionY = point[1]
                }

            }
        });
    }
Hitesh Sahu
la source
0

En plus des réponses ci-dessus, pour la question où et quand appeler getLocationOnScreen?

Dans toute information relative à la vue que vous souhaitez obtenir, elle ne sera disponible qu'après que la vue aura été affichée à l'écran. Vous pouvez facilement le faire en mettant votre code qui dépend de la création de la vue à l'intérieur de celui-ci (view.post (Runnable)):

view.post(new Runnable() {
            @Override
            public void run() {

               // This code will run view created and rendered on screen

               // So as the answer to this question, you can put
               int[] location = new int[2];
               myView.getLocationOnScreen(location);
               int x = location[0];
               int y = location[1];
            }
        });  
Suraj Vaishnav
la source