Гетерогенный GridLayout

UPDATE – мое рабочее решение: https://stackoverflow.com/a/21233824/902172


Я пытаюсь реализовать макет ниже:

Макет цели

Я предполагаю, что GridLayout подходит для моих нужд, но после 2 часов борьбы я не смог создать даже подобный макет. Макет неправильно изменяет размеры , он превышает экран телефона, а также не охватывает указанные строки и столбцы ,

Здесь я выбрал кнопку, чтобы вы могли видеть, как она превышает границы:

Потерпеть неудачу

и вот соответствующий xml-код: https://gist.github.com/2834492

Я достиг такого же макета с вложенными линейными вычислениями, но его невозможно изменить его размер для разных размеров экрана.


UPDATE – примерная реализация LinearLayout:

XML-код: https://gist.github.com/cdoger/2835887. Однако проблема заключается в том, что он не изменяет размеры для себя, а вот некоторые скриншоты с различными конфигурациями экрана:

введите описание изображения здесь

введите описание изображения здесь

введите описание изображения здесь


TLDR: Может ли кто-нибудь показать мне гетерогенную реализацию макета с GridLayout, как на первом снимке?

CD,

Проблема, с которой вы сталкиваетесь, связана с неправильным использованием GridLayout. GridLayout создан, чтобы показать своих детей в сетке, и вы пытаетесь переопределить это, не расширяя GridLayout. Хотя то, что вы хотите, может быть выполнено в коде (с использованием numcolumns и columnsize), оно не будет полезно для нескольких размеров экрана без большого количества кода.

Единственным адекватным решением, которое не потребует тонны взлома, является разумное использование как LinearLayout, так и RelativeLayout. LinearLayout не следует использовать исключительно, поскольку он предназначен для удаления элементов в строке (только по горизонтали или по вертикали). Это становится особенно очевидным, когда вы пытаетесь сделать нижние четыре кнопки. Хотя кнопки выше могут быть выполнены с LinearLayout с минимальными усилиями, RelativeLayout – это то, что вам нужно для нижних четырех кнопок.

Примечание. RelativeLayout может быть немного сложным для тех, у кого мало опыта их использования. Некоторые подводные камни include в себя: перекрытие детей, перемещение детей с экрана, неправильное применение высоты и ширины. Если вам нужен пример, дайте мне знать, и я отредактирую свой ответ.

Заключительное примечание. Я все использую текущие объекты инфраструктуры уникальными способами и искренне предпочитаю предоставлять запрошенное решение. Решение, однако, не является жизнеспособным с учетом ограничений вопроса.

(Редакция) Решение 1

После некоторой осторожной мысли прошлой ночью это может быть достигнуто с помощью чистого LinearLayout. Хотя мне не нравится это решение, оно должно быть совместимо с несколькими экранами и не требует от меня никаких инструментов. Следует соблюдать осторожность при слишком большом количестве LinearLayouts, так как, согласно разработчикам Google, это может привести к медленному загрузке пользовательских интерфейсов из-за свойства layout_weight. Второе решение, использующее RelativeLayout, будет предоставлено, когда я вернусь домой. Теперь Tested Это обеспечивает требуемые параметры макета для всех размеров экрана и ориентации.

                         

Решение 1 Пояснение

Ключ к LinearLayouts – определить ваши императивы как отдельные макеты и вложить в них другие. Когда вы применяете ограничения для большего количества измерений, необходимо добавить LinearLayouts для инкапсуляции других. Для вас было важно иметь еще двух родителей, чтобы сохранить эту пропорцию. Отличным показателем того, когда вы должны добавить другой уровень, является то, когда вам нужно использовать layout_weight, используя что-либо, кроме целочисленного значения. Это просто сложно определить правильно. Оттуда было довольно просто разбить его на столбцы.

Решение 2 (сбой)

В то время как я смог достичь желаемых результатов с использованием RelativeLayout и «struts», я мог сделать это только с макетами, которые были кратными 2 кнопкам в высоту. Такой трюк был бы потрясающим, так как уровни макета значительно сократились, поэтому я буду работать над чистым XML-решением и разместить ответ здесь, если и когда я его получу. В то же время LinearLayout выше должно удовлетворять вашим потребностям.

Надеюсь это поможет,

FuzzicalLogic

Я прочитал эту тему и понял, что мне нужно более плоское решение, чем те, у кого есть линейная компоновка. После некоторых исследований я закончил свой собственный макет. Он вдохновлен GridLayout, но немного отличается.

Обратите внимание, что если вы собираетесь скопировать код, вам нужно будет изменить имена пакетов в некоторых местах.

Этот макет имеет 4 параметра макета, которые дети используют для размещения самих себя. Это layout_left, layout_top, layout_right, layout_bottom . Сам ICGridLayout имеет два атрибута: layout_spacing и столбцы .

Столбцы сообщают, сколько столбцов вы хотите содержать. Затем он вычислит размер ячейки с той же высотой, что и ширина. Которая будет шириной / столбцами макетов.

Интервал – это объем пространства, который вы хотите между каждым ребенком.

Элементы layout_left | top | right | bottom – это координаты для каждой стороны. В макете нет расчетов, чтобы избежать столкновения или чего-либо еще. Он просто ставит детей там, где они хотят быть.

Если вы хотите иметь меньшие квадраты, вам просто нужно увеличить атрибут столбцов.

Имейте в виду, что это быстрый прототип, я продолжу работу над ним, и когда я почувствую, что он готов, я загружу его в Github и поставлю комментарий здесь.

Весь мой код ниже должен привести к следующему результату: Макет при использовании моего предоставленного кода

***** EDIT ***** Добавлен звонок для измерения для детей, забыл, что в первый раз. END EDIT ICGridLayout.java:

 package com.risch.evertsson.iclib.layout; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import com.risch.evertsson.iclib.R; /** * Created by johanrisch on 6/13/13. */ public class ICGridLayout extends ViewGroup { private int mColumns = 4; private float mSpacing; public ICGridLayout(Context context) { super(context); } public ICGridLayout(Context context, AttributeSet attrs) { super(context, attrs); init(attrs); } public ICGridLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(attrs); } private void init(AttributeSet attrs) { TypedArray a = getContext().obtainStyledAttributes( attrs, R.styleable.ICGridLayout_Layout); this.mColumns = a.getInt(R.styleable.ICGridLayout_Layout_columns, 3); this.mSpacing = a.getDimension(R.styleable.ICGridLayout_Layout_layout_spacing, 0); a.recycle(); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (changed) { int width = (int) (r - l); int side = width / mColumns; int children = getChildCount(); View child = null; for (int i = 0; i < children; i++) { child = getChildAt(i); LayoutParams lp = (LayoutParams) child.getLayoutParams(); int left = (int) (lp.left * side + mSpacing / 2); int right = (int) (lp.right * side - mSpacing / 2); int top = (int) (lp.top * side + mSpacing / 2); int bottom = (int) (lp.bottom * side - mSpacing / 2); child.layout(left, top, right, bottom); } } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { measureVertical(widthMeasureSpec, heightMeasureSpec); } private void measureVertical(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int width = 0; int height = 0; if (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.EXACTLY) { width = MeasureSpec.getSize(widthMeasureSpec); } else { throw new RuntimeException("widthMeasureSpec must be AT_MOST or " + "EXACTLY not UNSPECIFIED when orientation == VERTICAL"); } View child = null; int row = 0; int side = width / mColumns; int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { child = getChildAt(i); LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (lp.bottom > row) { row = lp.bottom; } int childHeight = (lp.bottom - lp.top)*side; int childWidth = (lp.right-lp.left)*side; int heightSpec = MeasureSpec.makeMeasureSpec(childHeight, LayoutParams.MATCH_PARENT); int widthSpec = MeasureSpec.makeMeasureSpec(childWidth, LayoutParams.MATCH_PARENT); child.measure(widthSpec, heightSpec); } height = row * side; // TODO: Figure out a good way to use the heightMeasureSpec... setMeasuredDimension(width, height); } @Override public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { return new ICGridLayout.LayoutParams(getContext(), attrs); } @Override protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { return p instanceof ICGridLayout.LayoutParams; } @Override protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { return new ICGridLayout.LayoutParams(p); } @Override protected ViewGroup.LayoutParams generateDefaultLayoutParams() { return new LayoutParams(); } public static class LayoutParams extends ViewGroup.MarginLayoutParams { int right = 1; int bottom = 1; int top = 0; int left = 0; int width = -1; int height = -1; public LayoutParams() { super(MATCH_PARENT, MATCH_PARENT); top = 0; left = 1; } public LayoutParams(int width, int height) { super(width, height); top = 0; left = 1; } public LayoutParams(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.ICGridLayout_Layout); left = a.getInt(R.styleable.ICGridLayout_Layout_layout_left, 0); top = a.getInt(R.styleable.ICGridLayout_Layout_layout_top, 0); right = a.getInt(R.styleable.ICGridLayout_Layout_layout_right, left + 1); bottom = a.getInt(R.styleable.ICGridLayout_Layout_layout_bottom, top + 1); height = a.getInt(R.styleable.ICGridLayout_Layout_layout_row_span, -1); width = a.getInt(R.styleable.ICGridLayout_Layout_layout_col_span, -1); if (height != -1) { bottom = top + height; } if (width != -1) { right = left + width; } a.recycle(); } public LayoutParams(ViewGroup.LayoutParams params) { super(params); } } } 

ICGridLayout.java довольно прямолинейно. Он принимает значения, предоставляемые детьми, и выдает их. attrs.xml:

              

example_layout.xml:

            

– Йохан Риш

PS Это мой первый длинный ответ, я попытался сделать это правильно. Если я провалился, скажите, пожалуйста, без пылающих 🙂 DS

Как это ?

введите описание изображения здесь

                       

Как многие говорили, вложенные линейные макеты, кажется, единственный способ выиграть здесь. Некоторые из решений не использовали параметры компоновки наиболее гибким образом. Приведенный ниже код стремится сделать это, и таким образом, что это сильно зависит от изменения соотношения сторон. Подробности в комментариях.

введите описание изображения здесьПейзаж

                                   

Итак, вот решение, которое я обещал через год =) Он в основном использует ViewTreeObserver для получения размеров родительского макета и создания пользовательских представлений соответственно. Поскольку этот код составляет один год, ViewTreeObserver не может быть лучшим способом динамического измерения размеров.

Полный исходный код можно найти здесь: https://github.com/cdoger/Android_layout

Я разделил экран на 8 равных ширины и 6 равных высот. Вот снимок того, как я изложил взгляды:

 final RelativeLayout mainLayout = (RelativeLayout) findViewById(R.id.main_layout); ViewTreeObserver vto = mainLayout.getViewTreeObserver(); vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { final int oneUnitWidth = mainLayout.getMeasuredWidth() / 8; final int oneUnitHeight = mainLayout.getMeasuredHeight() / 6; /** * 1 ***************************************************************/ final RelativeLayout.LayoutParams otelParams = new RelativeLayout.LayoutParams( oneUnitWidth * 4, oneUnitHeight); otelParams.addRule(RelativeLayout.ALIGN_PARENT_TOP); otelParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT); // otelParams.setMargins(0, 0, 2, 0); View1.setLayoutParams(otelParams); /***************************************************************/ /** * 2 ***************************************************************/ final RelativeLayout.LayoutParams otherParams = new RelativeLayout.LayoutParams( oneUnitWidth * 4, oneUnitHeight); otherParams.addRule(RelativeLayout.ALIGN_PARENT_TOP); otherParams.addRule(RelativeLayout.RIGHT_OF, View1.getId()); otherParams.setMargins(2, 0, 0, 0); View2.setLayoutParams(otherParams); /***************************************************************/ //... goes on like this 

Вот последний скриншот:

введите описание изображения здесь

Я попробовал этот тату

Я заблокирован здесь:

введите описание изображения здесь

             

Невозможно расширить 8-ю кнопку. : s

Я попробовал

  

Но это неэффективно. : s

Вставьте свой GridLayout в LinearLayout, как показано ниже, и попробуйте, чтобы это сработало для меня.

введите описание изображения здесь

               
Давайте будем гением компьютера.