Podstawowym problemem jest to, że trzeba czekać na fazę rysowania dla rzeczywistych pomiarów (szczególnie z wartościami dynamicznymi, takimi jak wrap_content
lub match_parent
), ale zazwyczaj ta faza nie została jeszcze ukończona onResume()
. Potrzebujesz obejścia, aby poczekać na tę fazę. Istnieją różne możliwe rozwiązania tego:
1. Słuchaj zdarzeń losowania / układu: ViewTreeObserver
ViewTreeObserver zostaje zwolniony dla różnych zdarzeń rysowania. Zwykle OnGlobalLayoutListener
jest to, co chcesz uzyskać pomiar, więc kod w detektorze zostanie wywołany po fazie układu, więc pomiary są gotowe:
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
view.getHeight(); //height is ready
}
});
Uwaga: Detektor zostanie natychmiast usunięty, ponieważ w przeciwnym razie będzie uruchamiany przy każdym zdarzeniu układu. Jeśli musisz obsługiwać aplikacje SDK Lvl <16, użyj tego do wyrejestrowania nasłuchującego:
public void removeGlobalOnLayoutListener (ViewTreeObserver.OnGlobalLayoutListener victim)
2. Dodaj plik wykonywalny do kolejki układu: View.post ()
Niezbyt dobrze znane i moje ulubione rozwiązanie. Zasadniczo po prostu użyj metody postu View z własnym runnable. To w zasadzie umieszcza kod w kolejce po takcie widoku, układzie itp., Jak stwierdził Romain Guy :
Kolejka zdarzeń interfejsu użytkownika będzie przetwarzać zdarzenia w kolejności. Po wywołaniu metody setContentView () kolejka zdarzeń będzie zawierać komunikat z prośbą o przekazanie, więc wszystko, co opublikujesz w kolejce, nastąpi po przejściu układu
Przykład:
final View view=//smth;
...
view.post(new Runnable() {
@Override
public void run() {
view.getHeight(); //height is ready
}
});
Przewaga nad ViewTreeObserver
:
- Twój kod jest wykonywany tylko raz i nie musisz wyłączać Obserwatora po wykonaniu, co może być kłopotliwe
- mniej pełna składnia
Bibliografia:
3. Nadpisz metodę ViewLayout
Jest to praktyczne tylko w niektórych sytuacjach, gdy logikę można zamknąć w samym widoku, w przeciwnym razie jest to dość pełna i nieporęczna składnia.
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
}
};
Pamiętaj też, że onLayout będzie wywoływany wiele razy, więc zastanów się, co robisz w metodzie, lub wyłącz swój kod po raz pierwszy
4. Sprawdź, czy przeszedł fazę układu
Jeśli masz kod, który wykonuje się wiele razy podczas tworzenia interfejsu użytkownika, możesz użyć następującej metody wsparcia v4 lib:
View viewYouNeedHeightFrom = ...
...
if(ViewCompat.isLaidOut(viewYouNeedHeightFrom)) {
viewYouNeedHeightFrom.getHeight();
}
Zwraca wartość true, jeśli widok przeszedł przez co najmniej jeden układ od czasu ostatniego przyłączenia lub odłączenia od okna.
Dodatkowo: uzyskiwanie statycznie zdefiniowanych pomiarów
Jeśli wystarczy uzyskać statycznie zdefiniowaną wysokość / szerokość, możesz to zrobić za pomocą:
Pamiętaj jednak, że po rysowaniu może się ona różnić od rzeczywistej szerokości / wysokości. Jawadok doskonale opisuje różnicę:
Rozmiar widoku jest wyrażany za pomocą szerokości i wysokości. Widok faktycznie zawiera dwie pary wartości szerokości i wysokości.
Pierwsza para jest znana jako zmierzona szerokość i zmierzona wysokość. Wymiary te określają, jak duży widok ma być w swoim obiekcie nadrzędnym (zobacz Układ, aby uzyskać więcej informacji.) Zmierzone wymiary można uzyskać, wywołując getMeasuredWidth () i getMeasuredHeight ().
Druga para jest po prostu znana jako szerokość i wysokość, lub czasami szerokość i wysokość rysunku. Wymiary te określają rzeczywisty rozmiar widoku na ekranie, w czasie rysowania i po układzie. Wartości te mogą, ale nie muszą, różnić się od zmierzonej szerokości i wysokości. Szerokość i wysokość można uzyskać, wywołując getWidth () i getHeight ().