Различия в виджетах для Android

Я пытаюсь создать активность, которая представляет некоторые данные пользователю. Данные таковы, что их можно разделить на «слова», каждый из которых является виджетами, а последовательность «слов» будет формировать данные («предложение»?), Виджет ViewGroup, содержащий слова. Поскольку пространство, требуемое для всех «слов» в «предложении», превысит доступное горизонтальное пространство на дисплее, я хотел бы обернуть эти «предложения», как обычный текст.

Следующий код:

public class WrapTest extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); LinearLayout l = new LinearLayout(this); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); LinearLayout.LayoutParams mlp = new LinearLayout.LayoutParams( new ViewGroup.MarginLayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)); mlp.setMargins(0, 0, 2, 0); for (int i = 0; i < 10; i++) { TextView t = new TextView(this); t.setText("Hello"); t.setBackgroundColor(Color.RED); t.setSingleLine(true); l.addView(t, mlp); } setContentView(l, lp); } } 

дает что-то вроде левого изображения, но я бы хотел, чтобы макет представлял те же виджеты, что и в правой.

non-wrapping http://sofru.miximages.com/java/01-have.png wrapping http://sofru.miximages.com/java/01-want.png

Есть ли такой макет или комбинация макетов и параметров, или я должен реализовать свою собственную ViewGroup для этого?

С мая 2016 года появился новый макет под названием FlexboxLayout от Google, который очень настраивается по назначению.

FlexboxLayout находится в репозитории Google GitHub на https://github.com/google/flexbox-layout в настоящий момент.

Вы можете использовать его в своем проекте, добавив зависимость от вашего файла build.gradle :

 dependencies { compile 'com.google.android:flexbox:0.3.2' } 

Подробнее об использовании FlexboxLayout и всех атрибутах, которые вы можете найти в репозитории readme или в статьях Mark Allison здесь:

https://blog.stylingandroid.com/flexboxlayout-part-1/

https://blog.stylingandroid.com/flexboxlayout-part2/

https://blog.stylingandroid.com/flexboxlayout-part-3/

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

Активность:

 package se.fnord.xmms2.predicate; import se.fnord.android.layout.PredicateLayout; import android.app.Activity; import android.graphics.Color; import android.os.Bundle; import android.widget.TextView; public class Predicate extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); PredicateLayout l = new PredicateLayout(this); for (int i = 0; i < 10; i++) { TextView t = new TextView(this); t.setText("Hello"); t.setBackgroundColor(Color.RED); t.setSingleLine(true); l.addView(t, new PredicateLayout.LayoutParams(2, 0)); } setContentView(l); } } 

Или в XML-макете:

  

И макет:

 package se.fnord.android.layout; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; /** * ViewGroup that arranges child views in a similar way to text, with them laid * out one line at a time and "wrapping" to the next line as needed. * * Code licensed under CC-by-SA * * @author Henrik Gustafsson * @see http://stackoverflow.com/questions/549451/line-breaking-widget-layout-for-android * @license http://creativecommons.org/licenses/by-sa/2.5/ * */ public class PredicateLayout extends ViewGroup { private int line_height; public PredicateLayout(Context context) { super(context); } public PredicateLayout(Context context, AttributeSet attrs){ super(context, attrs); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { assert(MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED); final int width = MeasureSpec.getSize(widthMeasureSpec); // The next line is WRONG!!! Doesn't take into account requested MeasureSpec mode! int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom(); final int count = getChildCount(); int line_height = 0; int xpos = getPaddingLeft(); int ypos = getPaddingTop(); for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child.getVisibility() != GONE) { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); child.measure( MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(height, MeasureSpec.UNSPECIFIED)); final int childw = child.getMeasuredWidth(); line_height = Math.max(line_height, child.getMeasuredHeight() + lp.height); if (xpos + childw > width) { xpos = getPaddingLeft(); ypos += line_height; } xpos += childw + lp.width; } } this.line_height = line_height; if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED){ height = ypos + line_height; } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST){ if (ypos + line_height < height){ height = ypos + line_height; } } setMeasuredDimension(width, height); } @Override protected LayoutParams generateDefaultLayoutParams() { return new LayoutParams(1, 1); // default of 1px spacing } @Override protected boolean checkLayoutParams(LayoutParams p) { return (p instanceof LayoutParams); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final int count = getChildCount(); final int width = r - l; int xpos = getPaddingLeft(); int ypos = getPaddingTop(); for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child.getVisibility() != GONE) { final int childw = child.getMeasuredWidth(); final int childh = child.getMeasuredHeight(); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (xpos + childw > width) { xpos = getPaddingLeft(); ypos += line_height; } child.layout(xpos, ypos, xpos + childw, ypos + childh); xpos += childw + lp.width; } } } } 

В результате:

Обернутые виджеты http://fnord.se/android/01-have2.png

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

 package com.asolutions.widget; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import com.asolutions.widget.R; public class RowLayout extends ViewGroup { public static final int DEFAULT_HORIZONTAL_SPACING = 5; public static final int DEFAULT_VERTICAL_SPACING = 5; private final int horizontalSpacing; private final int verticalSpacing; private List currentRows = Collections.emptyList(); public RowLayout(Context context, AttributeSet attrs) { super(context, attrs); TypedArray styledAttributes = context.obtainStyledAttributes(attrs, R.styleable.RowLayout); horizontalSpacing = styledAttributes.getDimensionPixelSize(R.styleable.RowLayout_android_horizontalSpacing, DEFAULT_HORIZONTAL_SPACING); verticalSpacing = styledAttributes.getDimensionPixelSize(R.styleable.RowLayout_android_verticalSpacing, DEFAULT_VERTICAL_SPACING); styledAttributes.recycle(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int widthMode = MeasureSpec.getMode(widthMeasureSpec); final int heightMode = MeasureSpec.getMode(heightMeasureSpec); final int maxInternalWidth = MeasureSpec.getSize(widthMeasureSpec) - getHorizontalPadding(); final int maxInternalHeight = MeasureSpec.getSize(heightMeasureSpec) - getVerticalPadding(); List rows = new ArrayList(); RowMeasurement currentRow = new RowMeasurement(maxInternalWidth, widthMode); rows.add(currentRow); for (View child : getLayoutChildren()) { LayoutParams childLayoutParams = child.getLayoutParams(); int childWidthSpec = createChildMeasureSpec(childLayoutParams.width, maxInternalWidth, widthMode); int childHeightSpec = createChildMeasureSpec(childLayoutParams.height, maxInternalHeight, heightMode); child.measure(childWidthSpec, childHeightSpec); int childWidth = child.getMeasuredWidth(); int childHeight = child.getMeasuredHeight(); if (currentRow.wouldExceedMax(childWidth)) { currentRow = new RowMeasurement(maxInternalWidth, widthMode); rows.add(currentRow); } currentRow.addChildDimensions(childWidth, childHeight); } int longestRowWidth = 0; int totalRowHeight = 0; for (int index = 0; index < rows.size(); index++) { RowMeasurement row = rows.get(index); totalRowHeight += row.getHeight(); if (index < rows.size() - 1) { totalRowHeight += verticalSpacing; } longestRowWidth = Math.max(longestRowWidth, row.getWidth()); } setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? MeasureSpec.getSize(widthMeasureSpec) : longestRowWidth + getHorizontalPadding(), heightMode == MeasureSpec.EXACTLY ? MeasureSpec.getSize(heightMeasureSpec) : totalRowHeight + getVerticalPadding()); currentRows = Collections.unmodifiableList(rows); } private int createChildMeasureSpec(int childLayoutParam, int max, int parentMode) { int spec; if (childLayoutParam == LayoutParams.FILL_PARENT) { spec = MeasureSpec.makeMeasureSpec(max, MeasureSpec.EXACTLY); } else if (childLayoutParam == LayoutParams.WRAP_CONTENT) { spec = MeasureSpec.makeMeasureSpec(max, parentMode == MeasureSpec.UNSPECIFIED ? MeasureSpec.UNSPECIFIED : MeasureSpec.AT_MOST); } else { spec = MeasureSpec.makeMeasureSpec(childLayoutParam, MeasureSpec.EXACTLY); } return spec; } @Override protected void onLayout(boolean changed, int leftPosition, int topPosition, int rightPosition, int bottomPosition) { final int widthOffset = getMeasuredWidth() - getPaddingRight(); int x = getPaddingLeft(); int y = getPaddingTop(); Iterator rowIterator = currentRows.iterator(); RowMeasurement currentRow = rowIterator.next(); for (View child : getLayoutChildren()) { final int childWidth = child.getMeasuredWidth(); final int childHeight = child.getMeasuredHeight(); if (x + childWidth > widthOffset) { x = getPaddingLeft(); y += currentRow.height + verticalSpacing; if (rowIterator.hasNext()) { currentRow = rowIterator.next(); } } child.layout(x, y, x + childWidth, y + childHeight); x += childWidth + horizontalSpacing; } } private List getLayoutChildren() { List children = new ArrayList(); for (int index = 0; index < getChildCount(); index++) { View child = getChildAt(index); if (child.getVisibility() != View.GONE) { children.add(child); } } return children; } protected int getVerticalPadding() { return getPaddingTop() + getPaddingBottom(); } protected int getHorizontalPadding() { return getPaddingLeft() + getPaddingRight(); } private final class RowMeasurement { private final int maxWidth; private final int widthMode; private int width; private int height; public RowMeasurement(int maxWidth, int widthMode) { this.maxWidth = maxWidth; this.widthMode = widthMode; } public int getHeight() { return height; } public int getWidth() { return width; } public boolean wouldExceedMax(int childWidth) { return widthMode == MeasureSpec.UNSPECIFIED ? false : getNewWidth(childWidth) > maxWidth; } public void addChildDimensions(int childWidth, int childHeight) { width = getNewWidth(childWidth); height = Math.max(height, childHeight); } private int getNewWidth(int childWidth) { return width == 0 ? childWidth : width + horizontalSpacing + childWidth; } } } 

Для этого также требуется запись в /res/values/attrs.xml, которую вы можете создать, если ее еще нет.

        

Использование в XML-макете выглядит следующим образом:

            

Проект android-flowlayout с помощью линии поддержки ApmeM тоже ломается.

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

Вот моя упрощенная версия кода:

  package com.superliminal.android.util; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; /** * A view container with layout behavior like that of the Swing FlowLayout. * Originally from http://nishantvnair.wordpress.com/2010/09/28/flowlayout-in-android/ itself derived from * http://stackoverflow.com/questions/549451/line-breaking-widget-layout-for-android * * @author Melinda Green */ public class FlowLayout extends ViewGroup { private final static int PAD_H = 2, PAD_V = 2; // Space between child views. private int mHeight; public FlowLayout(Context context) { super(context); } public FlowLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { assert (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED); final int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight(); int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom(); final int count = getChildCount(); int xpos = getPaddingLeft(); int ypos = getPaddingTop(); int childHeightMeasureSpec; if(MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST); else childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); mHeight = 0; for(int i = 0; i < count; i++) { final View child = getChildAt(i); if(child.getVisibility() != GONE) { child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), childHeightMeasureSpec); final int childw = child.getMeasuredWidth(); mHeight = Math.max(mHeight, child.getMeasuredHeight() + PAD_V); if(xpos + childw > width) { xpos = getPaddingLeft(); ypos += mHeight; } xpos += childw + PAD_H; } } if(MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED) { height = ypos + mHeight; } else if(MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { if(ypos + mHeight < height) { height = ypos + mHeight; } } height += 5; // Fudge to avoid clipping bottom of last row. setMeasuredDimension(width, height); } // end onMeasure() @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final int width = r - l; int xpos = getPaddingLeft(); int ypos = getPaddingTop(); for(int i = 0; i < getChildCount(); i++) { final View child = getChildAt(i); if(child.getVisibility() != GONE) { final int childw = child.getMeasuredWidth(); final int childh = child.getMeasuredHeight(); if(xpos + childw > width) { xpos = getPaddingLeft(); ypos += mHeight; } child.layout(xpos, ypos, xpos + childw, ypos + childh); xpos += childw + PAD_H; } } } // end onLayout() } 

Проблема с первым ответом:

 child.measure( MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST)); 

В ListView, например, элементам списка передается значение heightMeasureSpec 0 (НЕУТОЧНЕННОЕ), и поэтому здесь все значения имеют значение MeasureSpec размера 0 (AT_MOST). Это означает, что весь PredicateLayout невидим (высота 0).

В качестве быстрого исправления я изменил размер ChildSizeSpec следующим образом:

 int childHeightMeasureSpec; if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST); } else { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } 

а потом

 child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), childHeightMeasureSpec); 

который, кажется, работает для меня, хотя не обрабатывает режим EXACT, что было бы намного сложнее.

Моя измененная версия, основанная на ранее опубликованной:

  • Он работает в редакторе макетов Eclipse
  • Он центрирует все предметы по горизонтали

     import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; public class FlowLayout extends ViewGroup { private int line_height; public static class LayoutParams extends ViewGroup.LayoutParams { public final int horizontal_spacing; public final int vertical_spacing; /** * @param horizontal_spacing Pixels between items, horizontally * @param vertical_spacing Pixels between items, vertically */ public LayoutParams(int horizontal_spacing, int vertical_spacing, ViewGroup.LayoutParams viewGroupLayout) { super(viewGroupLayout); this.horizontal_spacing = horizontal_spacing; this.vertical_spacing = vertical_spacing; } /** * @param horizontal_spacing Pixels between items, horizontally * @param vertical_spacing Pixels between items, vertically */ public LayoutParams(int horizontal_spacing, int vertical_spacing) { super(0, 0); this.horizontal_spacing = horizontal_spacing; this.vertical_spacing = vertical_spacing; } } public FlowLayout(Context context) { super(context); } public FlowLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { assert (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED); final int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight(); int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom(); final int count = getChildCount(); int line_height = 0; int xpos = getPaddingLeft(); int ypos = getPaddingTop(); int childHeightMeasureSpec; if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST); } else { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child.getVisibility() != GONE) { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), childHeightMeasureSpec); final int childw = child.getMeasuredWidth(); line_height = Math.max(line_height, child.getMeasuredHeight() + lp.vertical_spacing); if (xpos + childw > width) { xpos = getPaddingLeft(); ypos += line_height; } xpos += childw + lp.horizontal_spacing; } } this.line_height = line_height; if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED) { height = ypos + line_height; } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { if (ypos + line_height < height) { height = ypos + line_height; } } setMeasuredDimension(width, height); } @Override protected ViewGroup.LayoutParams generateDefaultLayoutParams() { return new LayoutParams(1, 1); // default of 1px spacing } @Override protected android.view.ViewGroup.LayoutParams generateLayoutParams( android.view.ViewGroup.LayoutParams p) { return new LayoutParams(1, 1, p); } @Override protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { if (p instanceof LayoutParams) { return true; } return false; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final int count = getChildCount(); final int width = r - l; int xpos = getPaddingLeft(); int ypos = getPaddingTop(); int lastHorizontalSpacing = 0; int rowStartIdx = 0; for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child.getVisibility() != GONE) { final int childw = child.getMeasuredWidth(); final int childh = child.getMeasuredHeight(); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (xpos + childw > width) { final int freeSpace = width - xpos + lastHorizontalSpacing; xpos = getPaddingLeft() + freeSpace / 2; for (int j = rowStartIdx; j < i; ++j) { final View drawChild = getChildAt(j); drawChild.layout(xpos, ypos, xpos + drawChild.getMeasuredWidth(), ypos + drawChild.getMeasuredHeight()); xpos += drawChild.getMeasuredWidth() + ((LayoutParams)drawChild.getLayoutParams()).horizontal_spacing; } lastHorizontalSpacing = 0; xpos = getPaddingLeft(); ypos += line_height; rowStartIdx = i; } child.layout(xpos, ypos, xpos + childw, ypos + childh); xpos += childw + lp.horizontal_spacing; lastHorizontalSpacing = lp.horizontal_spacing; } } if (rowStartIdx < count) { final int freeSpace = width - xpos + lastHorizontalSpacing; xpos = getPaddingLeft() + freeSpace / 2; for (int j = rowStartIdx; j < count; ++j) { final View drawChild = getChildAt(j); drawChild.layout(xpos, ypos, xpos + drawChild.getMeasuredWidth(), ypos + drawChild.getMeasuredHeight()); xpos += drawChild.getMeasuredWidth() + ((LayoutParams)drawChild.getLayoutParams()).horizontal_spacing; } } } } 

Я обновил этот образец, чтобы исправить ошибку, теперь каждая строка может иметь разную высоту!

 import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; /** * ViewGroup that arranges child views in a similar way to text, with them laid * out one line at a time and "wrapping" to the next line as needed. * * Code licensed under CC-by-SA * * @author Henrik Gustafsson * @see http://stackoverflow.com/questions/549451/line-breaking-widget-layout-for-android * @license http://creativecommons.org/licenses/by-sa/2.5/ * * Updated by Aurélien Guillard * Each line can have a different height * */ public class FlowLayout extends ViewGroup { public static class LayoutParams extends ViewGroup.LayoutParams { public final int horizontal_spacing; public final int vertical_spacing; /** * @param horizontal_spacing Pixels between items, horizontally * @param vertical_spacing Pixels between items, vertically */ public LayoutParams(int horizontal_spacing, int vertical_spacing) { super(0, 0); this.horizontal_spacing = horizontal_spacing; this.vertical_spacing = vertical_spacing; } } public FlowLayout(Context context) { super(context); } public FlowLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { assert (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED); final int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight(); int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom(); final int count = getChildCount(); int line_height = 0; int xpos = getPaddingLeft(); int ypos = getPaddingTop(); int childHeightMeasureSpec; if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST); } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); } else { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child.getVisibility() != GONE) { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), childHeightMeasureSpec); final int childw = child.getMeasuredWidth(); if (xpos + childw > width) { xpos = getPaddingLeft(); ypos += line_height; } xpos += childw + lp.horizontal_spacing; line_height = child.getMeasuredHeight() + lp.vertical_spacing; } } if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED) { height = ypos + line_height; } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { if (ypos + line_height < height) { height = ypos + line_height; } } setMeasuredDimension(width, height); } @Override protected ViewGroup.LayoutParams generateDefaultLayoutParams() { return new LayoutParams(1, 1); // default of 1px spacing } @Override protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { if (p instanceof LayoutParams) { return true; } return false; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final int count = getChildCount(); final int width = r - l; int xpos = getPaddingLeft(); int ypos = getPaddingTop(); int lineHeight = 0; for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child.getVisibility() != GONE) { final int childw = child.getMeasuredWidth(); final int childh = child.getMeasuredHeight(); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (xpos + childw > width) { xpos = getPaddingLeft(); ypos += lineHeight; } lineHeight = child.getMeasuredHeight() + lp.vertical_spacing; child.layout(xpos, ypos, xpos + childw, ypos + childh); xpos += childw + lp.horizontal_spacing; } } } } 

Некоторые из различных ответов здесь дают мне ClassCastException в редакторе макетов Exclipse. В моем случае я хотел использовать ViewGroup.MarginLayoutParams, а не создавать свои собственные. В любом случае, обязательно верните экземпляр LayoutParams, который требуется для вашего собственного classа макета в generateLayoutParams. Это то, на что похожа моя, просто замените MarginLayoutParams тем, что вам нужна ViewGroup:

 @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new MarginLayoutParams(getContext(), attrs); } @Override protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { return p instanceof MarginLayoutParams; } 

Похоже, этот метод получает вызов для назначения объекта LayoutParams для каждого дочернего элемента в ViewGroup.

  LinearLayout row = new LinearLayout(this); //get the size of the screen Display display = getWindowManager().getDefaultDisplay(); this.screenWidth = display.getWidth(); // deprecated for(int i=0; i 

Я адаптировал некоторую часть ode выше и реализовал компоновку streamа, которая центрирует все дочерние представления, горизонтальные и вертикальные. Это соответствует моим потребностям.

 public class CenteredFlowLayout extends ViewGroup { private int lineHeight; private int centricHeightPadding; private final int halfGap; public static final List LINE_CHILDREN = new ArrayList(); public static class LayoutParams extends ViewGroup.LayoutParams { public final int horizontalSpacing; public final int verticalSpacing; public LayoutParams(int horizontalSpacing, int verticalSpacing) { super(0, 0); this.horizontalSpacing = horizontalSpacing; this.verticalSpacing = verticalSpacing; } } public CenteredFlowLayout(Context context) { super(context); halfGap = getResources().getDimensionPixelSize(R.dimen.half_gap); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight(); int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom(); final int maxHeight = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom(); final int count = getChildCount(); int lineHeight = 0; int xAxis = getPaddingLeft(); int yAxis = getPaddingTop(); int childHeightMeasureSpec; if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST); } else { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child.getVisibility() != GONE) { final CentricFlowLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams(); child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), childHeightMeasureSpec); final int childMeasuredWidth = child.getMeasuredWidth(); lineHeight = Math.max(lineHeight, child.getMeasuredHeight() + lp.verticalSpacing); if (xAxis + childMeasuredWidth > width) { xAxis = getPaddingLeft(); yAxis += lineHeight; } else if (i + 1 == count) { yAxis += lineHeight; } xAxis += childMeasuredWidth + lp.horizontalSpacing; } } this.lineHeight = lineHeight; if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED) { height = yAxis + lineHeight; } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { if (yAxis + lineHeight < height) { height = yAxis + lineHeight; } } if (maxHeight == 0) { maxHeight = height + getPaddingTop(); } centricHeightPadding = (maxHeight - height) / 2; setMeasuredDimension(width, disableCenterVertical ? height + getPaddingTop() : maxHeight); } @Override protected CentricFlowLayout.LayoutParams generateDefaultLayoutParams() { return new CentricFlowLayout.LayoutParams(halfGap, halfGap); } @Override protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { if (p instanceof LayoutParams) { return true; } return false; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final int count = getChildCount(); final int width = r - l; int yAxis = centricHeightPadding + getPaddingTop() + getPaddingBottom(); View child; int measuredWidth; int lineWidth = getPaddingLeft() + getPaddingRight(); CentricFlowLayout.LayoutParams lp; int offset; LINE_CHILDREN.clear(); for (int i = 0; i < count; i++) { child = getChildAt(i); lp = (LayoutParams) child.getLayoutParams(); if (GONE != child.getVisibility()) { measuredWidth = child.getMeasuredWidth(); if (lineWidth + measuredWidth + lp.horizontalSpacing > width) { offset = (width - lineWidth) / 2; layoutHorizontalCentricLine(LINE_CHILDREN, offset, yAxis); lineWidth = getPaddingLeft() + getPaddingRight() + measuredWidth + lp.horizontalSpacing; yAxis += lineHeight; LINE_CHILDREN.clear(); LINE_CHILDREN.add(child); } else { lineWidth += measuredWidth + lp.horizontalSpacing; LINE_CHILDREN.add(child); } } } offset = (width - lineWidth) / 2; layoutHorizontalCentricLine(LINE_CHILDREN, offset, yAxis); } private void layoutHorizontalCentricLine(final List children, final int offset, final int yAxis) { int xAxis = getPaddingLeft() + getPaddingRight() + offset; for (View child : children) { final int measuredWidth = child.getMeasuredWidth(); final int measuredHeight = child.getMeasuredHeight(); final CentricFlowLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams(); child.layout(xAxis, yAxis, xAxis + measuredWidth, yAxis + measuredHeight); xAxis += measuredWidth + lp.horizontalSpacing; } } } 

Попробуйте установить LP LayoutParams как WRAP_CONTENT .

Установка mlp будет WRAP_CONTENT , WRAP_CONTENT гарантирует, что ваши TextView (s) t будут достаточно широкими и достаточно высокими, чтобы удерживать «Hello» или любую строку, которую вы вставляете в них. Я думаю, что я не знаю, насколько широки ваши t . setSingleLine(true) .

  • Создание с помощью каталога Intellij 2017.2 / out дублирует файлы в каталоге / build
  • JAVA не может статически ссылаться на нестатические поля
  • Когда / почему вызывать System.out.flush () в Java
  • Библиотека PDF для рендеринга файлов PDF в Android
  • Упорядочение аргументов String.equals ()
  • NoClassDefFoundError - Eclipse и Android
  • Сортировка TreeMap по значению
  • Могу ли я последовательно переключать асинхронную задачу (начиная с одной после завершения предыдущей асинтезы)
  • Как выйти из приложения Java из программы
  • Как Facebook добавляет номера значков на значок приложения в Android?
  • Proguard и размышления в Android
  • Давайте будем гением компьютера.