如果 ViewGroup 的 children 超出设备宽度,则强制移动到下一行
Force ViewGroup's children move to next line if they exceed the device width
我想要一个视图组,其中可能有一些水平放置在一起的视图。我的代码的问题是,一切都很好,但是当 children 计数越来越大时,所有 children 水平放置在一起并超过设备宽度。如果它们超过设备宽度,我需要强制它们移动到下一行。
这是我的代码:
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="92dp"
android:layout_marginTop="?attr/actionBarSize"
android:background="@color/divider"
android:padding="6dp">
<LinearLayout
android:id="@+id/linear_layout_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"></LinearLayout>
<ImageButton
android:id="@+id/image_button_add_item"
android:layout_width="78dp"
android:layout_height="match_parent"
android:background="@color/secondary_text"
android:contentDescription="@string/content_description_add_new_file"
android:src="@drawable/ic_plus"/>
</LinearLayout>
注意: linear_layout_container
视图组包含所有视图,它们都是 linearLayout
视图组的 children。
为了将它们移到下一行,您必须进行一些测量,但如果您想要一个简单的解决方案:
- 你可以直接制作它们
MATCH_PARRENT
- 或者给它们一个固定的宽度大小并调整重量(你需要用为
MATCH_PARRENT
和水平布局设置的线性布局来包装它们)。
- 或者您可以使用 2015 年 8 月推出的新 Percentage Layout。
LinearLayout 默认以这种方式工作。为了您的目的,您需要一个 FlowLayout。
Romain 家伙的 FlowLayout
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
public class FlowLayout extends ViewGroup {
private int mHorizontalSpacing;
private int mVerticalSpacing;
private Paint mPaint;
public FlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.FlowLayout);
try {
mHorizontalSpacing = a.getDimensionPixelSize(
R.styleable.FlowLayout_horizontalSpacing, 0);
mVerticalSpacing = a.getDimensionPixelSize(
R.styleable.FlowLayout_verticalSpacing, 0);
} finally {
a.recycle();
}
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(0xffff0000);
mPaint.setStrokeWidth(2.0f);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec)
- getPaddingRight();
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
boolean growHeight = widthMode != MeasureSpec.UNSPECIFIED;
int width = 0;
int height = getPaddingTop();
int currentWidth = getPaddingLeft();
int currentHeight = 0;
boolean breakLine = false;
boolean newLine = false;
int spacing = 0;
final int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
spacing = mHorizontalSpacing;
if (lp.horizontalSpacing >= 0) {
spacing = lp.horizontalSpacing;
}
if (growHeight
&& (breakLine || currentWidth + child.getMeasuredWidth() > widthSize)) {
height += currentHeight + mVerticalSpacing;
currentHeight = 0;
width = Math.max(width, currentWidth - spacing);
currentWidth = getPaddingLeft();
newLine = true;
} else {
newLine = false;
}
lp.x = currentWidth;
lp.y = height;
currentWidth += child.getMeasuredWidth() + spacing;
currentHeight = Math.max(currentHeight, child.getMeasuredHeight());
breakLine = lp.breakLine;
}
if (!newLine) {
height += currentHeight;
width = Math.max(width, currentWidth - spacing);
}
width += getPaddingRight();
height += getPaddingBottom();
setMeasuredDimension(resolveSize(width, widthMeasureSpec),
resolveSize(height, heightMeasureSpec));
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y
+ child.getMeasuredHeight());
}
}
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
boolean more = super.drawChild(canvas, child, drawingTime);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp.horizontalSpacing > 0) {
float x = child.getRight();
float y = child.getTop() + child.getHeight() / 2.0f;
canvas.drawLine(x, y - 4.0f, x, y + 4.0f, mPaint);
canvas.drawLine(x, y, x + lp.horizontalSpacing, y, mPaint);
canvas.drawLine(x + lp.horizontalSpacing, y - 4.0f, x
+ lp.horizontalSpacing, y + 4.0f, mPaint);
}
if (lp.breakLine) {
float x = child.getRight();
float y = child.getTop() + child.getHeight() / 2.0f;
canvas.drawLine(x, y, x, y + 6.0f, mPaint);
canvas.drawLine(x, y + 6.0f, x + 6.0f, y + 6.0f, mPaint);
}
return more;
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
@Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p.width, p.height);
}
public static class LayoutParams extends ViewGroup.LayoutParams {
int x;
int y;
public int horizontalSpacing;
public boolean breakLine;
public LayoutParams(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.FlowLayout_LayoutParams);
try {
horizontalSpacing = a
.getDimensionPixelSize(
R.styleable.FlowLayout_LayoutParams_layout_horizontalSpacing,
-1);
breakLine = a.getBoolean(
R.styleable.FlowLayout_LayoutParams_layout_breakLine,
false);
} finally {
a.recycle();
}
}
public LayoutParams(int w, int h) {
super(w, h);
}
}
}
只需使用 FlowLayout 扭曲您的视图。
您可以通过扩展 ViewGroup 来编写自己的代码。 github
中有一些图书馆
我想要一个视图组,其中可能有一些水平放置在一起的视图。我的代码的问题是,一切都很好,但是当 children 计数越来越大时,所有 children 水平放置在一起并超过设备宽度。如果它们超过设备宽度,我需要强制它们移动到下一行。
这是我的代码:
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="92dp"
android:layout_marginTop="?attr/actionBarSize"
android:background="@color/divider"
android:padding="6dp">
<LinearLayout
android:id="@+id/linear_layout_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"></LinearLayout>
<ImageButton
android:id="@+id/image_button_add_item"
android:layout_width="78dp"
android:layout_height="match_parent"
android:background="@color/secondary_text"
android:contentDescription="@string/content_description_add_new_file"
android:src="@drawable/ic_plus"/>
</LinearLayout>
注意: linear_layout_container
视图组包含所有视图,它们都是 linearLayout
视图组的 children。
为了将它们移到下一行,您必须进行一些测量,但如果您想要一个简单的解决方案:
- 你可以直接制作它们
MATCH_PARRENT
- 或者给它们一个固定的宽度大小并调整重量(你需要用为
MATCH_PARRENT
和水平布局设置的线性布局来包装它们)。 - 或者您可以使用 2015 年 8 月推出的新 Percentage Layout。
LinearLayout 默认以这种方式工作。为了您的目的,您需要一个 FlowLayout。
Romain 家伙的 FlowLayout
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
public class FlowLayout extends ViewGroup {
private int mHorizontalSpacing;
private int mVerticalSpacing;
private Paint mPaint;
public FlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.FlowLayout);
try {
mHorizontalSpacing = a.getDimensionPixelSize(
R.styleable.FlowLayout_horizontalSpacing, 0);
mVerticalSpacing = a.getDimensionPixelSize(
R.styleable.FlowLayout_verticalSpacing, 0);
} finally {
a.recycle();
}
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(0xffff0000);
mPaint.setStrokeWidth(2.0f);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec)
- getPaddingRight();
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
boolean growHeight = widthMode != MeasureSpec.UNSPECIFIED;
int width = 0;
int height = getPaddingTop();
int currentWidth = getPaddingLeft();
int currentHeight = 0;
boolean breakLine = false;
boolean newLine = false;
int spacing = 0;
final int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
spacing = mHorizontalSpacing;
if (lp.horizontalSpacing >= 0) {
spacing = lp.horizontalSpacing;
}
if (growHeight
&& (breakLine || currentWidth + child.getMeasuredWidth() > widthSize)) {
height += currentHeight + mVerticalSpacing;
currentHeight = 0;
width = Math.max(width, currentWidth - spacing);
currentWidth = getPaddingLeft();
newLine = true;
} else {
newLine = false;
}
lp.x = currentWidth;
lp.y = height;
currentWidth += child.getMeasuredWidth() + spacing;
currentHeight = Math.max(currentHeight, child.getMeasuredHeight());
breakLine = lp.breakLine;
}
if (!newLine) {
height += currentHeight;
width = Math.max(width, currentWidth - spacing);
}
width += getPaddingRight();
height += getPaddingBottom();
setMeasuredDimension(resolveSize(width, widthMeasureSpec),
resolveSize(height, heightMeasureSpec));
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y
+ child.getMeasuredHeight());
}
}
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
boolean more = super.drawChild(canvas, child, drawingTime);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp.horizontalSpacing > 0) {
float x = child.getRight();
float y = child.getTop() + child.getHeight() / 2.0f;
canvas.drawLine(x, y - 4.0f, x, y + 4.0f, mPaint);
canvas.drawLine(x, y, x + lp.horizontalSpacing, y, mPaint);
canvas.drawLine(x + lp.horizontalSpacing, y - 4.0f, x
+ lp.horizontalSpacing, y + 4.0f, mPaint);
}
if (lp.breakLine) {
float x = child.getRight();
float y = child.getTop() + child.getHeight() / 2.0f;
canvas.drawLine(x, y, x, y + 6.0f, mPaint);
canvas.drawLine(x, y + 6.0f, x + 6.0f, y + 6.0f, mPaint);
}
return more;
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
@Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p.width, p.height);
}
public static class LayoutParams extends ViewGroup.LayoutParams {
int x;
int y;
public int horizontalSpacing;
public boolean breakLine;
public LayoutParams(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.FlowLayout_LayoutParams);
try {
horizontalSpacing = a
.getDimensionPixelSize(
R.styleable.FlowLayout_LayoutParams_layout_horizontalSpacing,
-1);
breakLine = a.getBoolean(
R.styleable.FlowLayout_LayoutParams_layout_breakLine,
false);
} finally {
a.recycle();
}
}
public LayoutParams(int w, int h) {
super(w, h);
}
}
}
只需使用 FlowLayout 扭曲您的视图。
您可以通过扩展 ViewGroup 来编写自己的代码。 github
中有一些图书馆