Jak uzyskać bezwzględne współrzędne widoku

390

Próbuję uzyskać bezwzględne współrzędne pikseli ekranu w lewym górnym rogu widoku. Jednak wszystkie metody, które mogę znaleźć, takie jak getLeft()i getRight()nie działają, ponieważ wszystkie wydają się być względne względem rodzica widoku, co daje mi 0. Jak to zrobić w odpowiedni sposób?

Jeśli to pomaga, jest to gra „przywróć porządek”. Chcę, aby użytkownik mógł narysować ramkę, aby wybrać wiele elementów. Zakładam, że najłatwiej to zrobić do getRawX()iz getRawY()tych, MotionEventa następnie porównać te wartości z lewym górnym rogiem układu zawierającego elementy. Znając rozmiar kawałków, mogę ustalić, ile kawałków zostało wybranych. Zdaję sobie sprawę, że mogę używać getX()i getY()na MotionEvent, ale ponieważ to zwraca względną pozycję, która utrudnia ustalenie, które kawałki zostały wybrane. (Wiem, że nie jest to niemożliwe, ale wydaje się to niepotrzebnie skomplikowane).

Edycja: jest to kod, którego użyłem, aby uzyskać rozmiar pojemnika do przechowywania, zgodnie z jednym z pytań. TableLayoutjest stołem, który zawiera wszystkie elementy układanki.

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

Edycja 2: Oto kod, który wypróbowałem, podążając za sugerowanymi odpowiedziami.

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

Ten kod został dodany po zakończeniu całej inicjalizacji. Obraz został podzielony na tablicę ImageViews (tablica komórek []) zawartą w TableLayout. Komórki [0] znajdują się w lewym górnym rogu ImageView, a ja wybrałem komórki [4], ponieważ są one gdzieś pośrodku i zdecydowanie nie powinny mieć współrzędnych (0,0).

Kod pokazany powyżej wciąż daje mi wszystkie zera w logach, czego tak naprawdę nie rozumiem, ponieważ różne elementy układanki są poprawnie wyświetlane. (Próbowałem public int dla tableLayoutCorners i domyślnej widoczności, obie dały ten sam wynik).

Nie wiem, czy to jest znaczące, ale ImageViewpoczątkowo nie podano rozmiaru. Rozmiar ImageViews jest określany podczas inicjalizacji automatycznie przez View, gdy daję mu obraz do wyświetlenia. Czy może to przyczynić się do tego, że ich wartości są równe 0, nawet jeśli kod rejestrowania następuje po tym, jak otrzymali obraz i automatycznie zmienili rozmiar? Aby potencjalnie temu przeciwdziałać, dodałem kod tableLayout.requestLayout()jak pokazano powyżej, ale to nie pomogło.

Steve Haley
źródło

Odpowiedzi:

632

Użyj View.getLocationOnScreen()i / lub getLocationInWindow().

Romain Guy
źródło
3
Teraz tego rodzaju funkcji spodziewałem się znaleźć. Niestety nie mogę używać go poprawnie. Zdaję sobie sprawę, że to znany błąd, że getLocationOnScreen podaje zerowy wskaźnik wyjątku w Androidzie v1.5 (platforma, dla której koduję), ale testy w v2.1 nie działały lepiej. Obie metody dały mi 0 za wszystko. Powyżej podałem fragment kodu.
Steve Haley
76
Możesz go wywołać tylko PO tym, jak doszło do układu. Wywołujesz tę metodę, zanim widoki zostaną umieszczone na ekranie.
Romain Guy,
5
Aha, masz rację, chociaż nie było oczywiste, że to robię. Dzięki! Dla każdego ciekawego więcej szczegółów dodałem odpowiedź, która wyjaśnia, co mam na myśli.
Steve Haley
9
Masz na myśli jak ViewTreeObserver i jego OnGlobalLayoutListener? Zobacz View.getViewTreeObserver (), aby rozpocząć.
Romain Guy,
8
@RainainGuy Tak, coś w tym rodzaju, ale mniej mylące niż konieczność rejestracji (a następnie wyrejestrowania…). Jest to obszar, w którym iOS robi to lepiej niż Android pod względem SDK. Używając obu na co dzień, mogę powiedzieć, że iOS ma wiele irytujących rzeczy, ale obsługa UIView nie jest jedną z nich. :)
Martin Marconcini,
127

Przyjęta odpowiedź tak naprawdę nie powiedziała, jak uzyskać lokalizację, więc tutaj jest trochę więcej szczegółów. Przechodzisz w inttablicy o długości 2, a wartości są zastępowane współrzędnymi widoku (x, y) (górnego, lewego rogu).

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

Notatki

  • Zastąpienie getLocationOnScreen przez getLocationInWindowpowinno dać takie same wyniki w większości przypadków (patrz ta odpowiedź ). Jeśli jednak znajdujesz się w mniejszym oknie, takim jak okno dialogowe lub klawiatura niestandardowa, użyj go, aby wybrać ten, który najlepiej odpowiada Twoim potrzebom.
  • Otrzymasz, (0,0)jeśli wywołasz tę metodę, onCreateponieważ widok nie został jeszcze ułożony. Możesz użyć a, ViewTreeObserveraby nasłuchiwać, kiedy układ jest gotowy, i możesz uzyskać zmierzone współrzędne. (Zobacz tę odpowiedź .)
Suragch
źródło
86

Najpierw musisz uzyskać lokalny prostokąt Widoku widoku

Na przykład:

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

Mam nadzieję, że to pomoże

Naveen Murthy
źródło
3
To także powinno było zadziałać ... Jednak daje mi to również wartość 0. Podałem powyższy fragment kodu, jeśli to pomoże ci dowiedzieć się, co zrobiłem źle. Dzięki jeszcze raz.
Steve Haley
1
Zobacz moje inne komentarze; Przez przypadek dzwoniłem na to, zanim Widoki skończyły się układać. Teraz działa dobrze, dzięki.
Steve Haley,
@Naveen Murthy podaje współrzędne do konkretnego widoku, potrzebuję współrzędnych klikniętego widoku w układzie głównym ..
Aniket
20
zwraca to lokalizację względem rodzica. użyj getGlobalVisibleRect()dla absolutnych coordów.
Jeffrey Blattman
3
@ SteveHaley, mam do czynienia z tym samym problemem co ty, z jakiegoś powodu daje mi 0 w każdej metodzie, którą próbuję. Wygląda na to, że zrozumiałeś, dlaczego daje zero. Próbuję uzyskać współrzędne widoku po załadowaniu układu, ale otrzymuję zero, dlaczego tak jest? Dzięki za pomoc
Pankaj Nimgade,
46

Korzystanie z globalnego detektora układu zawsze działało dla mnie dobrze. Ma tę zaletę, że może ponownie oceniać rzeczy, jeśli zmieni się układ, np. Jeśli coś jest ustawione na View.GONE lub widoki potomne są dodawane / usuwane.

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);
 }
Szymon
źródło
Nie zapomnij usunąć OnGlobalLayoutListener wewnątrz (na końcu) onGlobalLayout () {...}. Również, dla pewności, sprawdź, czy to, co minęło! = 0; nasłuchiwanie można wywołać dwukrotnie, raz o wartości w = 0 / h = 0, drugi raz o rzeczywistych, poprawnych wartościach.
MacD
Ten sposób, aby nadmuchać główny układ, a następnie użyć, setContentView()spowodował problem w specjalnej Activityaplikacji! To była aktywność dialogowa ( windowFrame:@null, windowActionBar:false, windowIsFloating:true, windowNoTitle:true). Jego główny układ (a LinearLayout) nie ma bezpośredniego potomka z określonym layout_width(Wszystkie były match_parentlub wrap_content).
Mir-Ismaili
17

Po komentarzu Romaina Guya, oto jak to naprawiłem. Mam nadzieję, że pomoże to każdemu, kto również miał ten problem.

Rzeczywiście starałem się ustalić pozycje widoków, zanim zostały one rozłożone na ekranie, ale to wcale nie było oczywiste. Te wiersze zostały umieszczone po uruchomieniu kodu inicjującego, więc założyłem, że wszystko jest gotowe. Jednak ten kod był nadal w onCreate (); eksperymentując z Thread.sleep () odkryłem, że układ nie jest tak naprawdę sfinalizowany, dopóki onCreate () nie skończy się do uruchomienia onResume (). W rzeczy samej, kod próbował uruchomić się zanim układ skończył się pozycjonować na ekranie. Dodając kod do OnClickListener (lub innego Listenera) uzyskano prawidłowe wartości, ponieważ można go było uruchomić tylko po zakończeniu układu.


Poniższy wiersz został zaproponowany jako edycja społeczności:

proszę użyć onWindowfocuschanged(boolean hasFocus)

Steve Haley
źródło
Chcesz powiedzieć, że setContentView (my_layout); nie umieści wszystkich widoków. Wszystkie widoki zostaną umieszczone dopiero po zakończeniu onCreate ()?
Ashwin
5
Nie do końca. Wszystkie widoki „istnieją” później setContentView(R.layout.something), ale nie zostały wizualnie załadowane. Nie mają rozmiaru ani pozycji. Nie dzieje się tak, dopóki nie zakończy się pierwsze przejście układu, co dzieje się w pewnym momencie w przyszłości (zazwyczaj po onResume ()).
Steve Haley,
14

Pierwszy sposób:

W Kotlin możemy stworzyć proste rozszerzenie do podglądu:

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

I po prostu uzyskaj współrzędne:

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

Drugi sposób:

Drugi sposób jest prostszy:

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

i po prostu uzyskaj absolutne X przez view.absX() i Y przezview.absY()

Amir Hossein Ghasemi
źródło
6
Krótsza wersja dla Twojej pierwszej próbki:val location = IntArray(2).apply(::getLocationOnScreen)
Tuan Chau
5

Moja funkcja utils pozwala uzyskać lokalizację widoku, zwróci Pointobiekt za pomocą x valueiy value

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

Za pomocą

Point viewALocation = getLocationOnScreen(viewA);
Phan Van Linh
źródło
5

Współrzędne widoku można uzyskać za pomocą getLocationOnScreen()lubgetLocationInWindow()

Później, xi ypowinno być w lewym górnym rogu widoku. Jeśli Twój układ główny jest mniejszy niż ekran (jak w oknie dialogowym), użyjgetLocationInWindow będzie zależało od kontenera, a nie od całego ekranu.

Rozwiązanie Java

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

UWAGA: Jeśli wartość wynosi zawsze 0, prawdopodobnie zmieniasz widok bezpośrednio przed zażądaniem lokalizacji.

Aby upewnić się, że widok miał szansę zaktualizować, uruchom zapytanie o lokalizację po obliczeniu nowego układu Widoku przy użyciu 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];
});

~~

Rozwiązanie Kotlin

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

UWAGA: Jeśli wartość wynosi zawsze 0, prawdopodobnie zmieniasz widok bezpośrednio przed zażądaniem lokalizacji.

Aby upewnić się, że widok miał szansę zaktualizować, uruchom zapytanie o lokalizację po obliczeniu nowego układu Widoku przy użyciu view.post:

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

Polecam utworzenie funkcji rozszerzenia do obsługi tego:

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

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

A jeśli potrzebujesz niezawodności, dodaj także:

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
źródło
0

Użyj tego kodu, aby znaleźć dokładne cordinate X i 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);

Dodałem / odjąłem niektóre wartości, aby dostosować mój widok. Wykonaj te wiersze dopiero po zawyżeniu całego widoku.

Tarun
źródło
„Dodałem / odjąłem niektóre wartości, aby dostosować mój widok”. Dlaczego??
ToolmakerSteve
Mam na myśli, zgodnie z moimi wymaganiami (Rozmieszczenie widoku), dodałem i odjąłem wartości, aby dostosować mój widok.
Tarun
0

Uzyskaj zarówno pozycję wyświetlania, jak i wymiar na ekranie

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
źródło
0

Oprócz powyższych odpowiedzi na pytanie, gdzie i kiedy należy zadzwonić getLocationOnScreen ?

We wszystkich informacjach związanych z dowolnym widokiem, który chcesz uzyskać, będzie on dostępny dopiero po ułożeniu widoku na ekranie. Możesz to łatwo zrobić, umieszczając w tym miejscu kod zależny od tworzenia widoku (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
źródło