Я использую a GridView
для отображения множества представлений, которые по сути являются LinearLayouts
. Я хочу, LinearLayouts
чтобы все они были квадратными, но я также хочу, чтобы они имели динамический размер - то есть есть два столбца, и я хочу, LinearLayouts
чтобы они растягивались в зависимости от размера экрана, но оставались квадратными. Есть ли способ сделать это с помощью xml
макета или мне нужно программно установить высоту и ширину?
84
Возможно, ответить поздно, но все же это будет полезно новым посетителям.
С новым ConstraintLayout, представленным в Android Studio 2.3, теперь довольно легко создавать адаптивные макеты.
В родительском ConstraintLayout, чтобы сделать любой из его дочерних видов / макетов динамически квадратным, добавьте этот атрибут
app:layout_constraintDimensionRatio="w,1:1"
w определяет ограничения по ширине, а соотношение 1: 1 обеспечивает квадратную компоновку.
источник
В xml нет ничего, что позволило бы связать свойства ширины и высоты. Наверное, проще всего создать подкласс
LinearLayout
и переопределитьonMeasure
@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); }
Я использовал это для создания представлений, которые раньше всегда были квадратными. Он по-прежнему должен работать для файла
LinearLayout
.Дополнительная информация, которая поможет в этом: http://developer.android.com/guide/topics/ui/custom-components.html http://developer.android.com/reference/android/view/View.MeasureSpec.html.
источник
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
:!super.onMeasure(widthMeasureSpec, widthMeasureSpec);
действительно дает вам квадрат, но всегда ширина x ширина. Если указанные размеры шире, чем высота, у вас возникнут проблемы. В моем решении используется меньшее из двух измерений, так что вы всегда получаете наибольший квадрат, который умещается в данном прямоугольнике.Я сделал так:
@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); }
В этой реализации ваш макет будет квадратным с учетом меньшего размера между шириной и высотой. И его можно даже установить с помощью динамических значений, например, используя вес внутри LinearLayout.
источник
Мы можем сделать это очень простым способом - просто позвонить
super.onMeasure()
дважды.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()
менее эффективен с точки зрения процесса рисования, но это простой способ исправить проблемы макета, которые могут вызвать другие ответы.источник
super.onMeasure()
дважды гарантирует, что макет выполнен правильно с учетом нового размера представления. Без этого нам нужно запускать макет другим способом или иметь дело с неправильными макетами.Это очень просто:
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); } }
источник
Добавьте следующую строку в XML:
app:layout_constraintDimensionRatio="1:1"
источник
Вот решение, которое работает для всех параметров макета, которые можно настроить для просмотра или группы просмотра:
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);
Ура
источник
Я предлагаю создать собственный класс макета, унаследованный от FrameLayout. Переопределите метод OnMeasure () и поместите любой элемент управления, который вы хотите квадратным, внутри этого SquareFrameLayout.
Вот как это делается в 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); } }
Если вы хотите, чтобы View (или любой другой элемент управления) был квадратным (и центрированным), просто добавьте его в свой макет следующим образом:
<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>
источник
Ознакомьтесь с SquareLayout , библиотекой Android, которая предоставляет класс-оболочку для различных макетов, отображая их в квадрате без потери основных функций.
Эти размеры рассчитываются непосредственно перед Компоновка оказывается , следовательно , нет никакого повторного рендеринга или что - нибудь , как , например , чтобы настроить , как только View получается.
Чтобы использовать библиотеку, добавьте это в свой build.gradle:
repositories { maven { url "https://maven.google.com" } } dependencies { compile 'com.github.kaushikthedeveloper:squarelayout:0.0.3' }
Вам нужен SquareLinearLayout .
источник
Если кому-то нужно решение с Kotlin, вот что я сделал
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) } }
источник
Попробуйте этот код:
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); } }
источник