Jak mogę dynamicznie ustawić pozycję widoku w systemie Android?

102

Jak mogę zmienić pozycję widoku za pomocą kodu? Jak zmiana jego pozycji X, Y. Czy to możliwe?

Arun Badole
źródło

Odpowiedzi:

117

W przypadku wszystkiego poniżej Honeycomb (poziom API 11) będziesz musiał użyć setLayoutParams(...).

Jeśli można ograniczyć wsparcie Honeycomb i nawet można użyć setX(...), setY(...), setLeft(...), setTop(...), itd.

Nietrwały Dave
źródło
41
z dokumentacji setTop () developer.android.com/reference/android/view/… : sets the top position of this view relative to its parent. This method is meant to be called by the layout system and should not generally be called otherwise, because the property may be changed at any time by the layout.- więc może to nie taki dobry pomysł;)
katzenhut
47

Tak, możesz dynamicznie ustawiać pozycję widoku w systemie Android. Podobnie masz ImageViewin LinearLayoutw pliku xml, więc możesz ustawić jego pozycję za pomocą LayoutParams.Ale upewnij się, że robisz to LayoutParamszgodnie z układem wykonanym w pliku xml. Są różne w LayoutParamszależności od przyjętego układu.

Oto kod do ustawienia:

    LayoutParams layoutParams=new LayoutParams(int width, int height);
    layoutParams.setMargins(int left, int top, int right, int bottom);
    imageView.setLayoutParams(layoutParams);

Mam nadzieję, że ustawienie pozycji będzie pomocne.

AkashG
źródło
15
pamiętaj również, że musisz zaimportować android.widget.LinearLayout.LayoutParams; ponieważ wartość domyślna to LayoutParams ViewGroup
David T.
Wydaje się, że to nie działa na Androidzie 8+. czy miałeś podobny problem?
MyFlashLab
Jakikolwiek pomysł, dlaczego projektanci Andriod zadają sobie tyle trudu, aby powstrzymać progamerów przed użyciem X, Y do pozycjonowania rzeczy. Ostatecznie wszystko trzeba narysować w ten sposób. Sugeruje to, że Android jest projektowany przez ludzi, którzy nie potrafią kodować.
Paul McCarthy
34

Istnieją już różne prawidłowe odpowiedzi, ale żadna z nich nie wydaje się właściwie sugerować, której metody należy użyć w danym przypadku, z wyjątkiem odpowiednich ograniczeń na poziomie interfejsu API:

  • Jeśli możesz poczekać na cykl układu, a nadrzędna grupa widoków obsługuje MarginLayoutParams(lub podklasę), ustaw marginLeft/ marginTopodpowiednio.

  • Jeśli potrzebujesz natychmiast i trwale zmienić pozycję (np. Dla kotwicy PopupMenu), dodatkowo wywołaj layout(l, t, r, b)z tymi samymi współrzędnymi. To wyklucza to, co system układu potwierdzi później.

  • W przypadku natychmiastowych (tymczasowych) zmian (takich jak animacje) użyj zamiast tego setX()/ setY(). W przypadkach, gdy rozmiar nadrzędny nie zależy od WRAP_CHILDREN, użycie setX()/ setY()wyłączność może być w porządku .

  • Nigdy nie używaj setLeft()/ setRight()/ setBottom()/ setTop(), patrz poniżej.

tło : pola mLeft/ mTop/ mBottom/ mRightsą wypełniane z odpowiednich parametrów LayoutParams w układzie (). Układ jest wywoływany niejawnie i asynchronicznie przez system układu widoku systemu Android. Dlatego ustawienie MarginLayoutParamswydaje się być najbezpieczniejszym i najczystszym sposobem na ustawienie pozycji na stałe. Jednak asynchroniczne opóźnienie układu może stanowić problem w niektórych przypadkach, np. Podczas korzystania z widoku do renderowania kursora, i powinno zostać ponownie umieszczone i służyć jako kotwica PopupMenu w tym samym czasie. W tym przypadku dzwonienie layout()działało dobrze.

Problemy z setLeft()i setTop()to:

  • Samo dzwonienie do nich nie wystarczy - trzeba też zadzwonić setRight()i setBottom()unikać rozciągania lub zawężania widoku.

  • Implementacja tych metod wygląda na stosunkowo złożoną (= wykonanie pewnej pracy w celu uwzględnienia zmian rozmiaru widoku spowodowanych przez każdą z nich)

  • Wydaje się, że powodują dziwne problemy z polami wejściowymi: miękka klawiatura numeryczna EditText czasami nie zezwala na cyfry

setX()i setY()działają poza układem układu, a odpowiednie wartości są traktowane jako dodatkowe odsunięcie do wartości w lewo / w górę / w dół / w prawo określonych przez system układu, odpowiednio przesuwając widok. Wydaje się, że zostały dodane do animacji (gdzie wymagany jest natychmiastowy efekt bez przechodzenia przez cykl układu).

Stefan Haustein
źródło
Cześć Stefan. Czy mógłbyś doradzić kilka książek lub artykułów, w których mogę zrozumieć, jak działa system układu Androida. Rozumiem tę sekwencję wywołań layout () onMeasure () onDraw (), ale chcę poznać więcej szczegółów. Z góry dziękuję
Sergey Brazhnik
Niestety nie mogę - najlepszym i najbardziej ostatecznym odniesieniem wydaje się być rzeczywisty kod źródłowy Androida O :)
Stefan Haustein
Używam setY do zmiany widoku, czy to zmienia się w następnym cyklu układu, a nie natychmiast?
sygnał
Jak to ustaliłeś?
Stefan Haustein
Warto zwrócić uwagę, że marginLeftmożna ustawić za pomocą setLayoutParamsa, new FrameLayout.LayoutParams(width, height)na którym ustawionomarginLeft
lucidbrot.
18

Istnieje biblioteka o nazwie NineOldAndroids , która umożliwia korzystanie z biblioteki animacji Honeycomb aż do wersji pierwszej.

Oznacza to, że możesz zdefiniować left, right, translationX / Y z nieco innym interfejsem.

Oto jak to działa:

ViewHelper.setTranslationX(view, 50f);

Wystarczy użyć statycznych metod z klasy ViewHelper, przekazać widok i jakąkolwiek wartość, na jaką chcesz go ustawić.

Ben
źródło
16

Polecam używanie setTranslationXi setTranslationY. Sam dopiero zaczynam, ale wydaje mi się, że to najbezpieczniejszy i preferowany sposób przesuwania widoku. Myślę, że zależy to w dużej mierze od tego, co dokładnie próbujesz zrobić, ale to działa dobrze w przypadku animacji 2D.

brianmearns
źródło
13

Jeśli używasz HoneyComb Sdk (poziom interfejsu API 11), możesz spróbować użyć następujących metod.

view.setX(float x);

Parametr x to wizualna pozycja x tego widoku.

view.setY(float y);

Parametr y jest wizualną pozycją y tego widoku.

Mam nadzieję, że okaże się pomocny. :)

Ryż z kurczaka
źródło
4
Pracuję na 2.2 Froyo i tych metod nie ma w nim.
Arun Badole
7

Aby uzyskać obsługę wszystkich poziomów API, możesz go używać w następujący sposób:

ViewPropertyAnimator.animate(view).translationYBy(-yourY).translationXBy(-yourX).setDuration(0);
Orr
źródło
Bo chce zmienić pozycję i powyższe rozwiązanie jest lepsze i nie trzeba animacja
FireZenk
Ponadto tłumaczenie może spowodować, że widok zniknie (częściowo) poza ekranem
mewa
4

Ustaw lewą pozycję tego widoku względem jego rodzica:

view.setLeft(int leftPosition);

Ustaw właściwą pozycję tego widoku względem jego elementu nadrzędnego:

view.setRight(int rightPosition);

Ustaw najwyższą pozycję tego widoku względem jego elementu nadrzędnego:

view.setTop(int topPosition);

Ustaw dolną pozycję tego widoku względem jego elementu nadrzędnego:

view.setBottom(int bottomPositon);

Powyższe metody służą do ustawiania pozycji widoku związanego z jego rodzicem.

Balaji.K
źródło
Przepraszam Te metody nie są dostępne. Czy możesz wysłać mi kod?
Arun Badole
4
„Ta metoda ma być wywoływana przez system układu i generalnie nie powinna być wywoływana w inny sposób, ponieważ właściwość może zostać zmieniona w dowolnym momencie przez układ”.
GDanger
4

Użyj LayoutParams. Jeśli używasz LinearLayout, musisz zaimportować android.widget.LinearLayout.LayoutParams, w przeciwnym razie zaimportuj odpowiednią wersję LayoutParams dla używanego układu lub spowoduje to wyjątek ClassCast , a następnie:

LayoutParams layoutParams = new LayoutParams(int width, int height);
layoutParams.setMargins(int left, int top, int right, int bottom);
imageView.setLayoutParams(layoutParams);

Uwaga: Zauważ, że możesz użyć również imageView.setLeft (int dim), ALE TO NIE BĘDZIE ustawiało pozycji komponentu, ustawi tylko pozycję lewej krawędzi komponentu, reszta pozostanie w tej samej pozycji .

Alessandro Muzzi
źródło
więc jak możemy przenieść widok do określonej pozycji bez wpływu na oryginalny widok?
James
Nie jestem pewien, czy zrozumiałem Twój problem, ponieważ jeśli chcesz przenieść widok, zmienisz go przez to, że go przenosisz. W każdym razie, jeśli chcesz przenieść widok na inny niezależnie od niego, możesz potrzebować widoku FrameLayout, aby rozświetlić główny układ, a następnie w nim możesz umieścić swoje niezależne widoki (oczywiście jeśli chcesz, aby poruszał się pod głównym widokiem, musisz umieścić FrameLayout przed głównym)
Alessandro Muzzi,
3

Użyj RelativeLayout, umieść w nim swój widok, pobierz RelativeLayout.LayoutParamsobiekt ze swojego widoku i ustaw marginesy według potrzeb. Następnie poproś requestLayout()o swój punkt widzenia. To jedyny sposób, jaki znam.

glodos
źródło
1

Odkryłem, że @Stefan Haustein jest bardzo blisko mojego doświadczenia, ale nie jestem pewien w 100%. Moja sugestia to:

  • setLeft()/ setRight()/ setBottom()/ setTop()czasami nie działa.
  • Jeśli chcesz tymczasowo ustawić pozycję (np. Do wykonania animacji, nie ma to wpływu na hierarchię), kiedy widok został dodany i wyświetlony , po prostu użyj setX()/setY() zamiast. (Możesz chcieć wyszukiwać bardziej w różnicy setLeft()isetX() )
  • Zauważ, że X, Y wydają się być absolutne i było obsługiwane przez AbsoluteLayout, który teraz jest przestarzały. Dlatego uważasz, że X, Y prawdopodobnie nie są już obsługiwane. I tak jest, ale tylko częściowo. Oznacza to, że jeśli Twój widok zostanie dodany, setX (), setY () będą działać idealnie ; w przeciwnym razie, gdy próbujesz dodać widok do układu grupy widoków (np. FrameLayout, LinearLayout, RelativeLayout), musisz ustawić jego LayoutParams za pomocą marginLeft , marginTop (w tym przypadku setX (), setY () czasami nie zadziała).
  • Ustawienie pozycji widoku przez marginLeft i marginTop to proces niezsynchronizowany . Więc potrzebuje trochę czasu, aby aktualizować hierarchii . Jeśli użyjesz widoku od razu po ustawieniu dla niego marginesu , możesz otrzymać niewłaściwą wartość .
Nguyen Tan Dat
źródło
0

Jedną rzeczą, o której należy pamiętać przy pozycjonowaniu, jest to, że każdy widok ma indeks względem swojego widoku nadrzędnego. Więc jeśli masz układ liniowy z trzema podglądami podrzędnymi, każdy z podglądów częściowych będzie miał indeks: 0, 1, 2 w powyższym przypadku.

Pozwala to na dodanie widoku do ostatniej pozycji (lub końca) w widoku nadrzędnym, wykonując coś takiego:

int childCount = parent.getChildCount();
parentView.addView(newView, childCount);

Alternatywnie możesz zastąpić widok, używając czegoś podobnego do następującego:

int childIndex = parentView.indexOfChild(childView);
childView.setVisibility(View.GONE);

parentView.addView(newView, childIndex);
Braden Holt
źródło