如何在Android中執行類似FlowLayout
的操作?我該如何做一些類似於Android中的FlowLayout?
回答
如果你看着我在Devoxx大學日的演講(可在parleys.com上找到),你將學會如何自己做。在演講期間,我在舞臺上寫了一個實現FlowLayout
的實現,以展示編寫自定義佈局的簡單程度。
執行託管here。
不,根據我所知,沒有辦法讓小部件這樣包裝。
我沒有足夠的知名度向羅曼蓋伊的回答發表評論,但這就是答案應該是的地方(我創建了一個帳戶只是爲了分享我的編輯)。
無論如何,我看到其他人已經發現他非常酷的FlowLayout解決方案有一些問題。 我可以自己找到一個,我和其他人一樣,看到有些孩子被剪斷。 查看算法的詳細信息,它似乎是計算高度時非常簡單的錯誤。當最後一個孩子被放在一條新線上時,身高並沒有被正確計算。 我清理了一下計算(有一個奇怪的使用「高度」與currentHeight)。
以下更改解決的,「如果一個新行最後一個孩子被剪裁」的問題:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int widthLimit = MeasureSpec.getSize(widthMeasureSpec) - getPaddingRight();
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
boolean growHeight = widthMode != MeasureSpec.UNSPECIFIED;
int width = 0;
int currentWidth = getPaddingLeft();
int currentHeight = getPaddingTop();
int maxChildHeight = 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()) > widthLimit)))
{
newLine = true;
currentHeight += maxChildHeight + mVerticalSpacing;
width = Math.max(width, currentWidth - spacing);
currentWidth = getPaddingLeft();
maxChildHeight = 0;
}
else
{
newLine = false;
}
maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight());
lp.x = currentWidth;
lp.y = currentHeight;
currentWidth += child.getMeasuredWidth() + spacing;
breakLine = lp.breakLine;
}
if (newLine == false)
{
width = Math.max(width, currentWidth - spacing);
}
width += getPaddingRight();
int height = currentHeight + maxChildHeight + getPaddingBottom();
setMeasuredDimension(resolveSize(width, widthMeasureSpec),
resolveSize(height, heightMeasureSpec));
}
這太好了。感謝您解決一個明顯的問題。 – 2014-04-30 03:12:33
謝謝@凱文。你幫了我很多:) – Gaurav 2016-12-05 03:26:21
尼斯簡單自足的FlowLayout代碼在這裏(只是通過簡單的幾gist.github文件) :
http://hzqtc.github.io/2013/12/android-custom-layout-flowlayout.html
然而,開箱即用的活性沒有工作對我來說,加載自定義佈局。
我發現[)使用2 PARAM .inflate(稱之爲from this example]此變通:
@Override
protected void onCreate(Bundle savedInstanceState)
{
// ..
setContentView(R.layout.main_res_layout_activity_main);
ViewGroup flowContainer = getFlowLayoutView();
// ..
}
ViewGroup getFlowLayoutView()
{
LayoutInflater inflater = getLayoutInflater();
ViewGroup flowLayout =
(ViewGroup)
inflater.inflate(
R.layout.main_res_layout_activity_main,
(FlowLayout) findViewById(R.id.flow_container)
);
return flowLayout;
}
像以前的答案之一,我開始與這裏的解決方案:http://hzqtc.github.io/2013/12/android-custom-layout-flowlayout.html
我將它擴展到了如下所示的兒童身高。
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
// Custom layout that wraps child views to a new line
public class FlowLayout extends ViewGroup {
private int marginHorizontal;
private int marginVertical;
public FlowLayout(Context context) {
super(context);
init();
}
public FlowLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FlowLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() { // Specify the margins for the children
marginHorizontal = getResources().getDimensionPixelSize(R.dimen.activity_half_horizontal_margin);
marginVertical = getResources().getDimensionPixelSize(R.dimen.activity_half_vertical_margin);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int childLeft = getPaddingLeft();
int childTop = getPaddingTop();
int lowestBottom = 0;
int lineHeight = 0;
int myWidth = resolveSize(100, widthMeasureSpec);
int wantedHeight = 0;
for (int i = 0; i < getChildCount(); i++) {
final View child = getChildAt(i);
if (child.getVisibility() == View.GONE) {
continue;
}
child.measure(getChildMeasureSpec(widthMeasureSpec, 0, child.getLayoutParams().width),
getChildMeasureSpec(heightMeasureSpec, 0, child.getLayoutParams().height));
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
lineHeight = Math.max(childHeight, lineHeight);
if (childWidth + childLeft + getPaddingRight() > myWidth) { // Wrap this line
childLeft = getPaddingLeft();
childTop = marginVertical + lowestBottom; // Spaced below the previous lowest point
lineHeight = childHeight;
}
childLeft += childWidth + marginHorizontal;
if (childHeight + childTop > lowestBottom) { // New lowest point
lowestBottom = childHeight + childTop;
}
}
wantedHeight += childTop + lineHeight + getPaddingBottom();
setMeasuredDimension(myWidth, resolveSize(wantedHeight, heightMeasureSpec));
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int childLeft = getPaddingLeft();
int childTop = getPaddingTop();
int lowestBottom = 0;
int myWidth = right - left;
for (int i = 0; i < getChildCount(); i++) {
final View child = getChildAt(i);
if (child.getVisibility() == View.GONE) {
continue;
}
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
if (childWidth + childLeft + getPaddingRight() > myWidth) { // Wrap this line
childLeft = getPaddingLeft();
childTop = marginVertical + lowestBottom; // Spaced below the previous lowest point
}
child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
childLeft += childWidth + marginHorizontal;
if (childHeight + childTop > lowestBottom) { // New lowest point
lowestBottom = childHeight + childTop;
}
}
}
}
我用這個作爲包裝多行TextEdits的解決方案。希望能幫助到你!
在某些情況下非常有用。 [這是修訂](https://stackoverflow.com/a/45123806/4515489),支持MarginLayoutParams。 – jk7 2017-07-16 00:07:05
很好的解決方案!只有在初始化方法中設置margin後,margin才能工作,但不能從layout中設置。 – 2017-12-03 13:03:54
這裏是自定義類,您可以通過添加動態視圖(也稱爲FlowLayout)來實現如下佈局。
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
/*
Created By Dhavalkumar Solanki
* */
public class FlowLayout extends ViewGroup {
private int line_height_space;
public static class LayoutParams extends ViewGroup.LayoutParams {
public int horizontal_spacing;
public 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_space = 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_space = Math.max(line_height_space, child.getMeasuredHeight() + lp.vertical_spacing);
if (xpos + childw > width) {
xpos = getPaddingLeft();
ypos += line_height_space;
}
xpos += childw + lp.horizontal_spacing;
}
}
this.line_height_space = line_height_space;
if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED) {
height = ypos + line_height_space;
} else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
if (ypos + line_height_space < height) {
height = ypos + line_height_space;
}
}
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();
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_space;
}
child.layout(xpos, ypos, xpos + childw, ypos + childh);
xpos += childw + lp.horizontal_spacing;
}
}
}
}
實施例:
text_view。XML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tool="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="5dp">
<TextView
android:id="@+id/tvText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="19sp"
android:background="@drawable/unselected_tag"
android:textColor="@color/colorPrimary"
tool:text="Temp" />
</RelativeLayout>
activity_flow_layou_demo.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/tvTitleBusiness"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Business Interest "
android:textColor="@color/colorPrimary"
android:textSize="25sp" />
<com.example.tristateandroid2.radardemo.FlowLayout
android:id="@+id/flowBusiness"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</com.example.tristateandroid2.radardemo.FlowLayout>
</LinearLayout>
<LinearLayout
android:layout_marginTop="@dimen/activity_horizontal_margin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/tvTitlePrivate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Private Interest "
android:textColor="@color/colorPrimary"
android:textSize="25sp" />
<com.example.tristateandroid2.radardemo.FlowLayout
android:id="@+id/flowPrivate"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</com.example.tristateandroid2.radardemo.FlowLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>
</RelativeLayout>
FlowLayouDemo.java
import android.graphics.Color;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.ArrayList;
public class FlowLayouDemo extends AppCompatActivity {
private TextView tvTitleBusiness;
private FlowLayout flowBusiness;
private TextView tvTitlePrivate;
private FlowLayout flowPrivate;
private ArrayList<TagModel> arrayList;
private void findViews() {
tvTitleBusiness = (TextView) findViewById(R.id.tvTitleBusiness);
flowBusiness = (FlowLayout) findViewById(R.id.flowBusiness);
tvTitlePrivate = (TextView) findViewById(R.id.tvTitlePrivate);
flowPrivate = (FlowLayout) findViewById(R.id.flowPrivate);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_flow_layou_demo);
findViews();
addLayouts();
}
private void addLayouts() {
if (arrayList == null) {
arrayList = new ArrayList<>();
}
flowBusiness.removeAllViews();
flowPrivate.removeAllViews();
for (int i = 0; i < 75; i++) {
final boolean[] selected = {false};
View view = this.getLayoutInflater().inflate(R.layout.text_view, null);
final TextView textView = (TextView) view.findViewById(R.id.tvText);
if (i % 5 == 0) {
arrayList.add(new TagModel(i, false, "Business VIEW : " + i));
textView.setText("Busi VIEW To IS : " + i);
} else {
arrayList.add(new TagModel(i, false, "TEXT IS : " + i));
textView.setText("Busi IS : " + i);
}
textView.setBackgroundResource(R.drawable.unselected_tag);
textView.setTextColor(Color.parseColor("#3F51B5"));
textView.setTag(i);
if(i<=50){
flowBusiness.addView(view);
}else {
textView.setText("Priv View : "+i);
flowPrivate.addView(view);
}
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (selected[0]) {
selected[0] = false;
textView.setBackgroundResource(R.drawable.unselected_tag);
textView.setTextColor(Color.parseColor("#3F51B5"));
} else {
selected[0] = true;
textView.setBackgroundResource(R.drawable.selected_tag);
textView.setTextColor(Color.parseColor("#FFFFFF"));
}
}
});
}
}
}
不錯的課程,但它需要一些工作來支持從右到左的佈局方向。 – 2016-12-07 12:07:46
如何在中心製作物品。 – 2018-02-10 14:36:03
你應該使用FlexboxLayout
與flexWrap="wrap"
屬性。
<com.google.android.flexbox.FlexboxLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:flexWrap="wrap">
<!-- contents go here -->
</com.google.android.flexbox.FlexboxLayout>
對於構建說明,請參閱the github repo.
更多關於這 - https://android-developers.googleblog.com/2017/02/build-flexible-layouts-with.html
的修訂,以@MattNotEquals()FlowLayout支持MarginLayoutParams。
這只是MarginLayoutParms的最低限度實現,用於支持左側,右側,頂部和底部邊距。
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
/**
* Original version courtesy of MattNotEquals() at http://stackoverflow.com/a/34169798/4515489 - 4/13/17.
* 7/15/17 Revised to support MarginLayoutParams.
*/
public class FlowLayout extends ViewGroup {
// Custom layout that wraps child views to a new line.
public FlowLayout(Context context) {
super(context);
}
public FlowLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FlowLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int childLeft = getPaddingLeft();
int childTop = getPaddingTop();
int lowestBottom = 0;
int lineHeight = 0;
int myWidth = resolveSize(100, widthMeasureSpec);
int wantedHeight = 0;
for (int i = 0; i < getChildCount(); i++) {
final View child = getChildAt(i);
if (child.getVisibility() == View.GONE) {
continue;
}
child.measure(getChildMeasureSpec(widthMeasureSpec, 0, child.getLayoutParams().width),
getChildMeasureSpec(heightMeasureSpec, 0, child.getLayoutParams().height));
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
lineHeight = Math.max(childHeight, lineHeight);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
childLeft += lp.leftMargin;
childTop += lp.topMargin;
if (childLeft + childWidth + lp.rightMargin + getPaddingRight() > myWidth) { // Wrap this line
childLeft = getPaddingLeft() + lp.leftMargin;
childTop = lowestBottom + lp.topMargin; // Spaced below the previous lowest point
lineHeight = childHeight;
}
childLeft += childWidth + lp.rightMargin;
if (childTop + childHeight + lp.bottomMargin > lowestBottom) { // New lowest point
lowestBottom = childTop + childHeight + lp.bottomMargin;
}
}
wantedHeight += lowestBottom + getPaddingBottom(); // childTop + lineHeight + getPaddingBottom();
setMeasuredDimension(myWidth, resolveSize(wantedHeight, heightMeasureSpec));
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int childLeft = getPaddingLeft();
int childTop = getPaddingTop();
int lowestBottom = 0;
int myWidth = right - left;
for (int i = 0; i < getChildCount(); i++) {
final View child = getChildAt(i);
if (child.getVisibility() == View.GONE) {
continue;
}
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
childLeft += lp.leftMargin;
childTop += lp.topMargin;
if (childLeft + childWidth + lp.rightMargin + getPaddingRight() > myWidth) { // Wrap this line
childLeft = getPaddingLeft() + lp.leftMargin;
childTop = lowestBottom + lp.topMargin; // Spaced below the previous lowest point
}
child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
childLeft += childWidth + lp.rightMargin;
if (childTop + childHeight + lp.bottomMargin > lowestBottom) { // New lowest point
lowestBottom = childTop + childHeight + lp.bottomMargin;
}
}
}
@Override
public boolean shouldDelayChildPressedState() {
return false;
}
@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 FlowLayout.LayoutParams(getContext(), attrs);
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
if (lp instanceof LayoutParams) {
return new LayoutParams((LayoutParams) lp);
}
else if (lp instanceof MarginLayoutParams) {
return new LayoutParams((MarginLayoutParams) lp);
}
else
return super.generateLayoutParams(lp);
}
/**
* Per-child layout information for layouts that support margins.
*/
public static class LayoutParams extends MarginLayoutParams {
public LayoutParams(@NonNull Context c, @Nullable AttributeSet attrs) {
super(c, attrs);
}
public LayoutParams(int width, int height) {
super(width, height);
}
public LayoutParams(@NonNull ViewGroup.LayoutParams source) {
super(source);
}
public LayoutParams(@NonNull ViewGroup.MarginLayoutParams source) {
super(source);
}
public LayoutParams(@NonNull LayoutParams source) {
super(source);
}
}
}
我從最近2個小時就開始搔癢,只有這段代碼對我有用!你能爲RadioGroup提供流程佈局嗎? – 2017-12-03 12:35:40
另外,我想知道如何使用保證金?保證金= 10dp從版面正確地進入。 – 2017-12-03 12:57:45
@Pratik,如果你有一個小的RadioGroup,並且如果有空間並且如果沒有空間時流到下一行,你想要在旁邊顯示其他視圖,FlowLayout應該可以正常工作。當第一行超出房間時,導致RadioButton項目流向下一行的大型水平RadioGroup需要自定義RadioGroup類型的類,因爲RadioGroup擴展了LinearLayout。您可以使用此FlowLayout類的想法編寫自定義RadioGroup類。到目前爲止,我還沒有必要使用單選按鈕。我們通常使用Spinners或其他控件。 – jk7 2017-12-05 03:38:57
- 1. 我該如何做類似的事情?
- 2. 如何做類似於stackoverflow的類似標題搜索
- 3. 從CSV複製時我該如何做一個類似CSV的狀態字段?
- 4. 如何創建一個類似於android中的gmail的協議?
- 5. Android - 我該做的對嗎?
- 6. 如何創建類似於Android
- 7. 如何創建類似於Android
- 8. 如何在FlowLayout中
- 9. 製作一個類似於android的應用程序,類似iphone
- 10. 我該如何做一個可空類型的構造函數?
- 11. 如何構建一個類似於此的Android應用程序
- 12. 如何創建一個類似於fb的事件表單android
- 13. Android:如何製作一個類似於首選項的ListView?
- 14. 我該如何做一個Upsert表格?
- 15. 我該如何做一個$ .each jQuery
- 16. 我該如何做一個dryscrape會議?
- 17. 我該如何做一個Forget.aspx?
- 18. C#做一些類似Windows窗體的動態繼承?
- 19. 可以做一些類似「rspec --color --format doc」的東西嗎?
- 20. 如何做一個酒吧,一個類似的風格在android系統
- 21. 我該如何在java中實現類似apache的重載?
- 22. 類似於Android的java類Bundle類
- 23. 如何在android中實現類似於css中的on_hover?
- 24. 類我。至於類似乎
- 25. 如何在iPhone上做類似於Path的動畫按鈕?
- 26. 我該如何做Memcached HA?
- 27. 我該如何做「git update」?
- 28. 我有一些我不明白的android錯誤,我該如何解決它們?
- 29. ScrollView屏幕類似於Android
- 30. 創建類似於android
查看@ arsent的回答。 – 2018-01-04 10:43:22