Come ottenere le coordinate assolute di una vista


390

Sto cercando di ottenere le coordinate assolute dei pixel dello schermo nell'angolo in alto a sinistra di una vista. Tuttavia, tutti i metodi che riesco a trovare come getLeft()e getRight()non funzionano in quanto sembrano essere relativi al genitore della vista, dandomi così 0. Qual è il modo corretto per farlo?

Se aiuta, questo è per un gioco 'rimetti l'immagine in ordine'. Voglio che l'utente sia in grado di disegnare una scatola per selezionare più pezzi. La mia ipotesi è che il modo più semplice per farlo sia da getRawX()e getRawY()verso MotionEvente quindi confrontare quei valori con l'angolo in alto a sinistra del layout che contiene i pezzi. Conoscendo le dimensioni dei pezzi, posso quindi determinare quanti pezzi sono stati selezionati. Mi rendo conto di poter usare getX()e getY()sul MotionEvent, ma dato che restituisce una posizione relativa che rende più difficile determinare quali pezzi sono stati selezionati. (Non impossibile, lo so, ma sembra inutilmente complicato).

Modifica: questo è il codice che ho usato per cercare di ottenere la dimensione del contenitore di contenimento, come da una delle domande. TableLayoutè il tavolo che contiene tutti i pezzi del puzzle.

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

Modifica 2: Ecco il codice che ho provato, seguendo più delle risposte suggerite.

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]);

Questo codice è stato aggiunto dopo aver completato l'inizializzazione. L'immagine è stata suddivisa in una matrice di ImageViews (la matrice di celle []) contenuta in a TableLayout. Celle [0] è in alto a sinistra ImageView, e ho scelto le celle [4] in quanto è da qualche parte nel mezzo e sicuramente non dovrebbero avere coordinate di (0,0).

Il codice mostrato sopra mi dà ancora tutti gli 0 nei log, che non capisco davvero perché i vari pezzi del puzzle sono visualizzati correttamente. (Ho provato int pubblico per tableLayoutCorners e visibilità predefinita, entrambi dando lo stesso risultato.)

Non so se questo è significativo, ma all'inizio ImageViewnon viene data una dimensione. La dimensione della ImageViews viene determinata automaticamente durante l'inizializzazione dalla vista quando le conferisco un'immagine da visualizzare. Ciò potrebbe contribuire al fatto che i loro valori sono 0, anche se quel codice di registrazione è dopo che gli è stata data un'immagine e si sono ridimensionati automaticamente? Per contrastare potenzialmente quello, ho aggiunto il codice tableLayout.requestLayout()come mostrato sopra, ma questo non ha aiutato.

Risposte:


632

3
Questo è il tipo di funzione che mi aspettavo di trovare. Sfortunatamente, non devo usarlo correttamente. Mi rendo conto che era un bug noto che getLocationOnScreen dà un'eccezione puntatore null in Android v1.5 (la piattaforma per cui sto codificando), ma i test in v2.1 non hanno funzionato meglio. Entrambi i metodi mi hanno dato 0 per tutto. Ho incluso uno snippet di codice sopra.
Steve Haley,

76
Puoi solo invocarlo DOPO che il layout è avvenuto. Stai chiamando il metodo prima che le viste siano posizionate sullo schermo.
Romain Guy,

5
Ah, hai ragione, anche se non era ovvio che lo stavo facendo. Grazie! Per chiunque sia curioso di maggiori dettagli, ho aggiunto una risposta che spiega cosa intendo.
Steve Haley,

9
Intendi come ViewTreeObserver e il suo OnGlobalLayoutListener? Vedi View.getViewTreeObserver () per iniziare.
Romain Guy,

8
@RomainGuy Sì, un po 'così, ma meno confuso che dover registrarti (e quindi annullare la registrazione ...). Questa è un'area in cui iOS lo fa meglio di Android in termini di SDK. Utilizzando entrambi su base giornaliera, posso dire che iOS ha molte cose fastidiose, ma la gestione di UIView non è una di quelle. :)
Martin Marconcini,

127

La risposta accettata in realtà non ha detto come ottenere la posizione, quindi ecco un po 'più di dettaglio. Si passa in una intmatrice di lunghezza 2 e i valori vengono sostituiti con le coordinate (x, y) della vista (nell'angolo in alto a sinistra).

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

Appunti

  • La sostituzione getLocationOnScreencon getLocationInWindowdovrebbe fornire gli stessi risultati nella maggior parte dei casi (vedere questa risposta ). Tuttavia, se ti trovi in ​​una finestra più piccola come una finestra di dialogo o una tastiera personalizzata, allora dovrai scegliere quale si adatta meglio alle tue esigenze.
  • Si otterrà (0,0)se si chiama questo metodo onCreateperché la vista non è stata ancora definita. È possibile utilizzare a ViewTreeObserverper ascoltare al termine del layout e ottenere le coordinate misurate. (Vedi questa risposta .)

86

Per prima cosa devi ottenere il rettangolo localVisible della vista

Ad esempio:

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));

Spero che questo possa aiutare


3
Anche quello avrebbe dovuto funzionare ... Comunque mi sta dando anche valori di 0. Ho incluso uno snippet di codice sopra se questo ti aiuta a capire cosa ho fatto di sbagliato. Grazie ancora.
Steve Haley,

1
Vedi i miei altri commenti; Lo stavo chiamando per caso prima che i Views avessero finito di presentarsi. Questo funziona bene ora grazie.
Steve Haley,

@Naveen Murthy fornisce le coordinate per una vista specifica, ho bisogno delle coordinate della vista cliccata nel layout di radice ..
Aniket

20
questo restituisce la posizione relativa al genitore. usare getGlobalVisibleRect()per coords assoluti.
Jeffrey Blattman,

3
@SteveHaley, ho riscontrato lo stesso problema che hai fatto tu, per qualche motivo mi dà 0 in ogni metodo che provo. Sembra che tu l'abbia capito perché sta dando zero. Sto cercando di ottenere le coordinate della vista dopo che il layout è stato caricato, ma ho zero perché è così? Grazie per il vostro aiuto
Pankaj Nimgade,

46

L'uso di un listener di layout globale ha sempre funzionato bene per me. Ha il vantaggio di essere in grado di rimisurare le cose se il layout viene modificato, ad es. Se qualcosa è impostato su View.GONE o le viste figlio vengono aggiunte / rimosse.

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);
 }

Non dimenticare di rimuovere OnGlobalLayoutListener all'interno di (alla fine di) di onGlobalLayout () {...}. Inoltre, per essere sicuro, controlla se what '\ s passato! = 0; l'ascoltatore può essere chiamato due volte, una volta con un valore di w = 0 / h = 0, la seconda volta con i valori effettivi, corretti.
MacD,

Questo modo per gonfiare il layout principale e quindi utilizzare ha setContentView()causato un problema in uno speciale Activitynella mia app! Quella era un'attività di dialogo ( windowFrame:@null, windowActionBar:false, windowIsFloating:true, windowNoTitle:true). Il layout principale (a LinearLayout) non ha figli diretti con determinati layout_width(Tutti erano match_parento wrap_content).
Mir-Ismaili,

17

Seguendo il commento di Romain Guy, ecco come l'ho risolto. Spero che possa aiutare chiunque abbia avuto questo problema.

Stavo davvero cercando di ottenere le posizioni delle viste prima che fossero disposte sullo schermo, ma non era affatto ovvio che stesse accadendo. Quelle righe erano state posizionate dopo l'esecuzione del codice di inizializzazione, quindi ho pensato che tutto fosse pronto. Tuttavia, questo codice era ancora in onCreate (); sperimentando Thread.sleep () ho scoperto che il layout non è stato effettivamente finalizzato fino a quando onCreate () fino a quando onResume () non ha terminato l'esecuzione. Quindi, in effetti, il codice stava cercando di essere eseguito prima che il layout avesse finito di essere posizionato sullo schermo. Aggiungendo il codice a un OnClickListener (o qualche altro Listener) sono stati ottenuti i valori corretti perché potevano essere attivati ​​solo dopo il completamento del layout.


La riga seguente è stata suggerita come modifica della community:

si prega di utilizzare onWindowfocuschanged(boolean hasFocus)


Quindi vuoi dire che setContentView (my_layout); non posizionerà tutte le viste. Tutte le viste verranno posizionate solo dopo aver terminato onCreate ()?
Ashwin,

5
Non proprio. Tutte le viste "esistono" dopo setContentView(R.layout.something), ma non sono state caricate visivamente. Non hanno una dimensione o una posizione. Ciò non accade fino al termine del primo passaggio di layout, che si verifica ad un certo punto in futuro (generalmente dopo onResume ()).
Steve Haley,

14

Primo modo:

In Kotlin possiamo creare una semplice estensione per la visualizzazione:

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

E ottieni semplicemente le coordinate:

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

Secondo modo:

Il secondo modo è più semplice:

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]
}

e semplicemente ottieni X assoluto di view.absX()e Y diview.absY()


6
Una versione più breve per il tuo primo campione:val location = IntArray(2).apply(::getLocationOnScreen)
Tuan Chau,

5

La mia funzione utils per ottenere la posizione di visualizzazione, restituirà un Pointoggetto con x valueey value

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

utilizzando

Point viewALocation = getLocationOnScreen(viewA);

5

Puoi ottenere le coordinate di una vista usando getLocationOnScreen()ogetLocationInWindow()

Successivamente, xe ydovrebbe essere l'angolo in alto a sinistra della vista. Se il layout di root è più piccolo dello schermo (come in una finestra di dialogo), l'utilizzo getLocationInWindowsarà relativo al suo contenitore, non all'intero schermo.

Soluzione Java

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

NOTA: se il valore è sempre 0, è probabile che si stia modificando la vista immediatamente prima di richiedere la posizione.

Per assicurarsi che la vista abbia avuto la possibilità di aggiornare, eseguire la richiesta di posizione dopo aver calcolato il nuovo layout della vista utilizzando 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];
});

~~

Soluzione di Kotlin

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

NOTA: se il valore è sempre 0, è probabile che si stia modificando la vista immediatamente prima di richiedere la posizione.

Per assicurarsi che la vista abbia avuto la possibilità di aggiornare, eseguire la richiesta di posizione dopo aver calcolato il nuovo layout della vista utilizzando view.post:

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

Consiglio di creare una funzione di estensione per gestire questo:

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

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

E se hai bisogno di affidabilità, aggiungi anche:

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)
    }
}

0

Usa questo codice per trovare coordinate X e Y esatte:

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);

Ho aggiunto / sottratto alcuni valori per adattare la mia vista. Si prega di fare queste righe solo dopo aver gonfiato l'intera vista.


"Ho aggiunto / sottratto alcuni valori per adattare la mia vista." Perché??
ToolmakerSteve

Intendo secondo il mio requisito (Posizionamento della vista), ho aggiunto e sottratto i valori per adattare la mia vista.
Tarun,

0

Visualizza sia posizione che dimensione sullo schermo

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]
                }

            }
        });
    }

0

Solo in aggiunta alle risposte sopra, per la domanda dove e quando dovresti chiamare getLocationOnScreen?

In tutte le informazioni relative a qualsiasi vista che desideri ottenere, sarà disponibile solo dopo che la vista è stata disposta sullo schermo. Puoi farlo facilmente inserendo il tuo codice che dipende dalla creazione della vista al suo interno (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];
            }
        });  
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.