Jak dopasować rozmiar widoku Androida na podstawie wymiarów jego rodzica

104

Jak mogę zmienić rozmiar widoku na podstawie rozmiaru jego układu nadrzędnego. Na przykład mam element, RelativeLayoutktóry wypełnia cały ekran i chcę, aby widok podrzędny, powiedzmy an ImageView, zajmował całą wysokość i 1/2 szerokości?

Próbowałem nadrzędnymi wszystko na onMeasure, onLayout, onSizeChanged, itp i nie mogę zmusić go do pracy ....

Mitch
źródło
Można to zrobić za pomocą ConstraintLayout, sprawdź to: stackoverflow.com/questions/37318228/…
Ali Nem

Odpowiedzi:

124

Nie wiem, czy ktoś nadal czyta ten wątek, czy nie, ale rozwiązanie Jeffa zaprowadzi cię tylko do połowy (trochę dosłownie). To, co zrobi jego onMeasure, to wyświetlenie połowy obrazu w połowie rodzica. Problem polega na tym, że wywołanie super.onMeasure przed wywołaniem setMeasuredDimensionspowoduje zmierzenie wszystkich elementów podrzędnych w widoku na podstawie oryginalnego rozmiaru, a następnie po prostu przecięcie widoku na pół podczas zmiany setMeasuredDimensionrozmiaru.

Zamiast tego musisz zadzwonić setMeasuredDimension(zgodnie z wymaganiami dla zastąpienia onMeasure) i podać nowy LayoutParamswidok, a następnie zadzwonić super.onMeasure. Pamiętaj, że LayoutParamspochodzą one od typu nadrzędnego widoku, a nie typu widoku.

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
   int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
   int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
   this.setMeasuredDimension(parentWidth/2, parentHeight);
   this.setLayoutParams(new *ParentLayoutType*.LayoutParams(parentWidth/2,parentHeight));
   super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

Uważam, że jedyny przypadek, w którym będziesz mieć problemy z rodzicem, LayoutParamsto sytuacja, w której rodzic jest AbsoluteLayout(co jest przestarzałe, ale czasami jest przydatne).

Vic
źródło
12
Z mojego doświadczenia wynika, że ​​rzadko to działa, zwł. kiedy wywoływane są fill / match_parent lub weights. Lepiej wywołaj metodę MeasureChildren po ustawieniu setMeasuredDimension (np. Przy pomocy MeasureSpec.makeMeasureSpec (newWidth, MeasureSpec.AT_MOST)).
M. Schenk,
1
Czy MeasureSpec.getSize(widthMeasureSpec)naprawdę podaje prawidłową szerokość rodzica? Dla mnie to po prostu zwraca losowe szerokości, które są dość zbliżone do rzeczywistej szerokości, ale nie są dokładne.
Konsumierer
1
@ M.Schenk to ma. Mam VideoView w układzie ważonym. Potrzebuję, aby szerokość była wartością procentową rozmiaru ekranu i muszę zachować proporcje. To rozciąga wideo, więc wrap_contentnie. Odpowiedź Vica nie działa w tym scenariuszu, ale komentarz @ M.Schenk tak. Zapisuje również dodatkowy przebieg układu. Powinieneś również opublikować to jako odpowiedź na widoczność.
dokkaebi,
7
Czy wywołanie nie będzie super.onMeasure()po prostu przesłonić i unieważnić efekty wcześniejszego wywołania this.setMeasuredDimension()?
Spinner
1
Jestem również ciekawy, jak wspomniał @Spinner, dlaczego wezwanie do super.onMeasure()nie zastępuje wcześniejszych obliczeń.
jwir3
39

Możesz rozwiązać ten problem, tworząc niestandardowy widok i nadpisując metodę onMeasure (). Jeśli zawsze używasz „fill_parent” jako layout_width w swoim xml, wówczas parametr widthMeasureSpec, który jest przekazywany do metody onMeasusre (), powinien zawierać szerokość elementu nadrzędnego.

public class MyCustomView extends TextView {

    public MyCustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
        int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
        this.setMeasuredDimension(parentWidth / 2, parentHeight);
    }
}   

Twój XML wyglądałby mniej więcej tak:

<LinearLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <view
        class="com.company.MyCustomView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>
Jeff
źródło
2
@jeff => dlaczego parentWidth / 2 ??
Saeed-rz
6
@Leon_SFS: Pytanie brzmi: „cała wysokość i 1/2 szerokości”
EdgarT
28

Przekonałem się, że najlepiej nie majstrować przy samodzielnym ustawianiu mierzonych wymiarów. Właściwie jest trochę negocjacji, które toczą się między widokami nadrzędnymi i podrzędnymi i nie chcesz ponownie pisać całego tego kodu .

Możesz jednak zmodyfikować parametry MeasureSpec, a następnie wywołać z nimi super. Twój widok nigdy się nie dowie, że otrzymuje zmodyfikowaną wiadomość od swojego rodzica i zajmie się wszystkim za Ciebie:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
    int myWidth = (int) (parentHeight * 0.5);
    super.onMeasure(MeasureSpec.makeMeasureSpec(myWidth, MeasureSpec.EXACTLY), heightMeasureSpec);
}
Phil Kulak
źródło
16

Podczas definiowania układu i widoku w formacie XML można określić szerokość i wysokość układu widoku, aby był zawijaną zawartością lub elementem nadrzędnym wypełnienia. Zajęcie połowy obszaru jest nieco trudniejsze, ale gdybyś miał coś, czego chciałeś na drugiej połowie, możesz zrobić coś takiego.

   <LinearLayout android:layout_width="fill_parent"
                android:layout_height="fill_parent">
        <ImageView android:layout_height="fill_parent"
                 android:layout_width="0dp"
                 android:layout_weight="1"/>
        <ImageView android:layout_height="fill_parent"
                 android:layout_width="0dp"
                 android:layout_weight="1"/>
   </LinearLayout>

Nadanie dwóm rzeczom tej samej wagi oznacza, że ​​rozciągną się, aby zająć tę samą część ekranu. Więcej informacji na temat układów można znaleźć w dokumentacji deweloperskiej.

Cheryl Simon
źródło
Nie polecam tego, ale ... jako hack, jeśli zastosujesz powyższe podejście, możesz uczynić jeden z tych widoków "atrapą" i ustawić android: visibility = "invisible" na wypełnienie połowy obszaru
RickNotFred
Jaki jest cel wykorzystania tylko połowy obszaru? Czy planujesz wyświetlić coś w drugiej połowie? To, co zrobisz z pozostałym obszarem, będzie najlepszym podejściem
RickNotFred
to zdecydowanie powinno być # 1 - jeśli możesz to zrobić w swoim xml, dlaczego robisz to w swojej Javie?
fandang
9

Uważam, że podejście XML firmy Mayras może przyjść całkiem nieźle . Można jednak zwiększyć dokładność, mając tylko jeden widok, ustawiając weightSum. Nie nazwałbym tego już hackiem, ale moim zdaniem najprostszym podejściem:

<LinearLayout android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:weightSum="1">
    <ImageView android:layout_height="fill_parent"
               android:layout_width="0dp"
               android:layout_weight="0.5"/>
</LinearLayout>

W ten sposób możesz użyć dowolnej wagi, na przykład 0,6 (i wyśrodkowanie) to waga, którą lubię używać dla przycisków.

pinkwerther
źródło
3

Spróbuj tego

int parentWidth = ((parentViewType)childView.getParent()).getWidth();
int parentHeight = ((parentViewType)childView.getParent()).getHeight();

wtedy możesz użyć LinearLayout.LayoutParams do ustawienia parametrów chileView

LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(childWidth,childLength);
childView.setLayoutParams(params);
Abhishek Gupta
źródło
2

Możesz teraz używać PercentRelativeLayout. Bum! Problem rozwiązany.

androidguy
źródło
2
PercentRelativeLayout został wycofany na poziomie API 26.0.0-beta1.
dzikovskyy
Co masz na myśli „na poziomie interfejsu API”? Masz na myśli numer wersji biblioteki? PRL znajduje się w bibliotece wsparcia, nie ma poziomu API.
androidguy
Przez poziom API rozumiem wersję Androida. Informacje stąd developer.android.com/reference/android/support/percent/… . Więcej informacji o poziomie API tutaj developer.android.com/guide/topics/manifest/ ...
dzikovskyy
Nadal możesz używać tej klasy, chyba że z jakiegoś powodu potrzebujesz wersji 26.0.0 lub nowszej.
androidguy
1

Roman, jeśli chcesz zrobić swój układ w kodzie Java (potomek ViewGroup), jest to możliwe. Rzecz w tym, że musisz zaimplementować zarówno metody onMeasure, jak i onLayout. OnMeasure jest wywoływany jako pierwszy i musisz tam „zmierzyć” podgląd (efektywnie dostosowując go do żądanej wartości). Musisz ponownie zmienić rozmiar w wywołaniu onLayout. Jeśli nie wykonasz tej sekwencji lub nie wywołasz metody setMeasuredDimension () na końcu kodu onMeasure, nie uzyskasz wyników. Dlaczego jest to zaprojektowane w tak skomplikowany i delikatny sposób, jest poza mną.

Pavel Lahoda
źródło
1

Jeśli druga strona jest pusta. Myślę, że najprostszym sposobem byłoby dodanie pustego widoku (np. Układu liniowego), a następnie ustawienie szerokości obu widoków na fill_parent i obu ich wag na 1.

Działa to w LinearLayout ...

jpm
źródło
0

To jest coś takiego:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.company.myapp.ActivityOrFragment"
    android:id="@+id/activity_or_fragment">

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/linearLayoutFirst"
    android:weightSum="100">   <!-- Overall weights sum of children elements -->

    <Spinner
        android:layout_width="0dp"   <!-- It's 0dp because is determined by android:layout_weight -->
        android:layout_weight="50"
        android:layout_height="wrap_content"
        android:id="@+id/spinner" />

</LinearLayout>


<LinearLayout
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:layout_below="@+id/linearLayoutFirst"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true"
    android:id="@+id/linearLayoutSecond">

    <EditText
        android:layout_height="wrap_content"
        android:layout_width="0dp"
        android:layout_weight="75"
        android:inputType="numberDecimal"
        android:id="@+id/input" />

    <TextView
        android:layout_height="wrap_content"
        android:layout_width="0dp"
        android:layout_weight="25"
        android:id="@+id/result"/>

</LinearLayout>
</RelativeLayout>
Mario
źródło
-1

Od dłuższego czasu zmagam się z tym pytaniem, chcę zmienić szerokość szuflady DrawerLayout, która jest drugim dzieckiem jej rodzica. Niedawno to wymyśliłem, ale nie wiem, czy jest lepszy pomysł.

override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
    super.onLayout(changed, l, t, r, b)
    (getChildAt(1).layoutParams as LayoutParams).width = measuredWidth/2
}
Jana 6
źródło