Używam, GridView
aby wyświetlić kilka widoków, które są zasadniczo LinearLayouts
. Chcę, LinearLayouts
aby wszystkie były kwadratowe, ale chcę również, aby były dynamicznie zmieniane - to znaczy są dwie kolumny i chcę, LinearLayouts
aby rozciągały się w zależności od rozmiaru ekranu, ale pozostały kwadratowe. Czy istnieje sposób, aby to zrobić za pomocą xml
układu, czy też muszę programowo ustawiać wysokości i szerokości?
84
Odpowiedź może być późna, ale mimo to będzie pomocna dla nowych gości.
Dzięki nowemu ConstraintLayout wprowadzonemu w Android Studio 2.3 tworzenie responsywnych układów jest teraz dość łatwe.
W nadrzędnym ConstraintLayout, aby dowolny z jego elementów podrzędnych w widoku / układzie był dynamicznie kwadratowy, dodaj ten atrybut
app:layout_constraintDimensionRatio="w,1:1"
w ma na celu określenie ograniczeń dotyczących szerokości, a stosunek 1: 1 zapewnia kwadratowy układ.
źródło
W kodzie XML nie ma nic, co umożliwiłoby połączenie właściwości width i height. Prawdopodobnie najłatwiejszą rzeczą do zrobienia jest utworzenie podklasy
LinearLayout
i nadpisanieonMeasure
@Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); int size = width > height ? height : width; setMeasuredDimension(size, size); }
Użyłem tego do tworzenia widoków, które zawsze były kwadratowe. Powinien nadal działać dla
LinearLayout
.Więcej informacji, które pomogą to zrobić: http://developer.android.com/guide/topics/ui/custom-components.html http://developer.android.com/reference/android/view/View.MeasureSpec.html
źródło
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
:!super.onMeasure(widthMeasureSpec, widthMeasureSpec);
daje kwadrat, ale zawsze ma on szerokość x szerokość. Jeśli podane wymiary są szersze niż wysokie, będziesz miał problem. Moje rozwiązanie wykorzystuje mniejszy z dwóch wymiarów, dzięki czemu zawsze otrzymujesz największy kwadrat, który zmieści się w podanym prostokącie.Zrobiłem w ten sposób:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int size; if(widthMode == MeasureSpec.EXACTLY && widthSize > 0){ size = widthSize; } else if(heightMode == MeasureSpec.EXACTLY && heightSize > 0){ size = heightSize; } else{ size = widthSize < heightSize ? widthSize : heightSize; } int finalMeasureSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY); super.onMeasure(finalMeasureSpec, finalMeasureSpec); }
W tej implementacji twój układ będzie kwadratowy, zakładając mniejszy rozmiar między szerokością a wysokością. Można go nawet ustawić za pomocą wartości dynamicznych, takich jak użycie wagi wewnątrz LinearLayout.
źródło
Możemy to zrobić w bardzo prosty sposób - wystarczy zadzwonić
super.onMeasure()
dwa razy.protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = getMeasuredWidth(); int height = getMeasuredHeight(); int squareLen = Math.min(width, height); super.onMeasure( MeasureSpec.makeMeasureSpec(squareLen, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(squareLen, MeasureSpec.EXACTLY)); }
super.onMeasure()
Dwukrotne wywołanie jest mniej wydajne z punktu widzenia procesu rysowania, ale jest prostym sposobem rozwiązania problemów z układem, które mogą powodować inne odpowiedzi.źródło
super.onMeasure()
Dwukrotne wywołanie zapewnia, że układ jest wykonany poprawnie, biorąc pod uwagę nowy rozmiar widoku. Bez tego musimy wywołać układ w inny sposób lub poradzić sobie z nieprawidłowymi układami.To jest tak proste, jak:
public class SquareRelativeLayout extends RelativeLayout { public SquareRelativeLayout(Context context) { super(context); } public SquareRelativeLayout(Context context, AttributeSet attrs) { super(context, attrs); } public SquareRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (widthMeasureSpec < heightMeasureSpec) super.onMeasure(widthMeasureSpec, widthMeasureSpec); else super.onMeasure(heightMeasureSpec, heightMeasureSpec); } }
źródło
Oto rozwiązanie, które działa dla wszystkich parametrów układu, które można ustawić jako widok lub grupę widoku:
int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); int widthDesc = MeasureSpec.getMode(widthMeasureSpec); int heightDesc = MeasureSpec.getMode(heightMeasureSpec); int size = 0; if (widthDesc == MeasureSpec.UNSPECIFIED && heightDesc == MeasureSpec.UNSPECIFIED) { size = DP(defaultSize); // Use your own default size, in our case // it's 125dp } else if ((widthDesc == MeasureSpec.UNSPECIFIED || heightDesc == MeasureSpec.UNSPECIFIED) && !(widthDesc == MeasureSpec.UNSPECIFIED && heightDesc == MeasureSpec.UNSPECIFIED)) { //Only one of the dimensions has been specified so we choose the dimension that has a value (in the case of unspecified, the value assigned is 0) size = width > height ? width : height; } else { //In all other cases both dimensions have been specified so we choose the smaller of the two size = width > height ? height : width; } setMeasuredDimension(size, size);
Twoje zdrowie
źródło
Moja sugestia jest taka, aby utworzyć niestandardową klasę układu, która dziedziczy po FrameLayout. Zastąp metodę OnMeasure () i umieść dowolną kontrolkę, którą chcesz, aby była kwadratowa wewnątrz tego SquareFrameLayout.
Oto jak to się robi w Xamarin.Android:
public class SquareFrameLayout : FrameLayout { private const string _tag = "SquareFrameLayout"; public SquareFrameLayout(Android.Content.Context context):base(context) {} public SquareFrameLayout(IntPtr javaReference, Android.Runtime.JniHandleOwnership transfer):base(javaReference, transfer) {} public SquareFrameLayout(Android.Content.Context context, IAttributeSet attrs):base(context, attrs) {} public SquareFrameLayout(Android.Content.Context context, IAttributeSet attrs, int defStyleAttr):base(context, attrs, defStyleAttr) {} public SquareFrameLayout(Android.Content.Context context, IAttributeSet attrs, int defStyleAttr, int defStyleRes):base(context, attrs, defStyleAttr, defStyleRes) {} protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec) { var widthMode = MeasureSpec.GetMode(widthMeasureSpec); int widthSize = MeasureSpec.GetSize(widthMeasureSpec); var heightMode = MeasureSpec.GetMode(heightMeasureSpec); int heightSize = MeasureSpec.GetSize(heightMeasureSpec); int width, height; switch (widthMode) { case MeasureSpecMode.Exactly: width = widthSize; break; case MeasureSpecMode.AtMost: width = Math.Min(widthSize, heightSize); break; default: width = 100; break; } switch (heightMode) { case MeasureSpecMode.Exactly: height = heightSize; break; case MeasureSpecMode.AtMost: height = Math.Min(widthSize, heightSize); break; default: height = 100; break; } Log.Debug(_tag, $"OnMeasure({widthMeasureSpec}, {heightMeasureSpec}) => Width mode: {widthMode}, Width: {widthSize}/{width}, Height mode: {heightMode}, Height: {heightSize}/{height}"); var size = Math.Min(width, height); var newMeasureSpec = MeasureSpec.MakeMeasureSpec(size, MeasureSpecMode.Exactly); base.OnMeasure(newMeasureSpec, newMeasureSpec); } }
Jeśli chcesz, aby Widok (lub jakakolwiek inna kontrolka) był kwadratowy (i wyśrodkowany), po prostu dodaj go do swojego układu w następujący sposób:
<your.namespace.SquareFrameLayout android:id="@+id/squareContainer" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center"> <View android:id="@+id/squareContent" android:layout_width="match_parent" android:layout_height="match_parent" /> </your.namespace.SquareFrameLayout>
źródło
Dodaj następujący wiersz w XML:
app:layout_constraintDimensionRatio="1:1"
źródło
Sprawdź SquareLayout , bibliotekę systemu Android, która zapewnia klasę opakowującą dla różnych układów, renderując je w wymiarach kwadratowych bez utraty podstawowych funkcji.
Te wymiary są obliczane tuż przed Układ jest renderowany , stąd nie ma re-rendering czy coś takiego, jak kiedyś, aby dostosować widok jest uzyskane.
Aby skorzystać z biblioteki, dodaj to do pliku build.gradle:
repositories { maven { url "https://maven.google.com" } } dependencies { compile 'com.github.kaushikthedeveloper:squarelayout:0.0.3' }
Wymagany jest SquareLinearLayout .
źródło
Dla każdego, kto chce rozwiązania Z Kotlinem, oto co zrobiłem
FrameLayout
.package your.package.name import android.content.Context import android.util.AttributeSet import android.widget.FrameLayout class SquareLayout: FrameLayout { constructor(ctx: Context) : super(ctx) constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { if (widthMeasureSpec < heightMeasureSpec) super.onMeasure(widthMeasureSpec, widthMeasureSpec) else super.onMeasure(heightMeasureSpec, heightMeasureSpec) } }
źródło
Wypróbuj ten kod:
public class SquareRelativeLayout extends RelativeLayout { public SquareRelativeLayout(Context context) { super(context); } public SquareRelativeLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int size; if (widthMode == MeasureSpec.EXACTLY && widthSize > 0) { size = widthSize; } else if (heightMode == MeasureSpec.EXACTLY && heightSize > 0) { size = heightSize; } else { size = widthSize < heightSize ? widthSize : heightSize; } int finalMeasureSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY); super.onMeasure(finalMeasureSpec, finalMeasureSpec); } }
źródło