I am uncertain of what type of layout to use for this certain scenario.
I basically want to have a horizontal linear layout that i can add views to. in this case buttons (displaying tags in an application) But each view will have a different width bases on the name of the tag it is displaying, so i want to add say 10 tags, I need a layout that will fit as many as it can on the 1st line, and then if it doesn't fit, automatically overflow to the next line.
Basically how a text view works with text, if the text is longer than the width it goes to the next line, except I want to do this with non-clickable buttons.
I thought of a grid layout, but then it would have the same no of "tags" on each line when you could have 2 tags with a long name on the first line and 7 with a short name on the second line.
Something that looks a bit like this:
I basically want the look of how stack overflow does it below here.
Answer: Your own custom Layout
:)
I know this is a late answer to this question. But it might help the OP or someone for sure.
You can extend ViewGroup
to create a custom layout like this one below. The advantage of this is you get the keep the view hierarchy flat.
MyFlowLayout
public class MyFlowLayout extends ViewGroup {
public MyFlowLayout(Context context) {
super(context);
}
public MyFlowLayout(Context context, AttributeSet attrs) {
this(context,attrs,0);
}
public MyFlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int realWidth = MeasureSpec.getSize(widthMeasureSpec);
int currentHeight = 0;
int currentWidth = 0;
int currentChildHookPointx = 0;
int currentChildHookPointy = 0;
int childCount = this.getChildCount();
for(int i = 0; i < childCount; i++) {
View child = getChildAt(i);
this.measureChild(child, widthMeasureSpec, heightMeasureSpec);
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
//check if child can be placed in the current row, else go to next line
if(currentChildHookPointx + childWidth > realWidth) {
//new line
currentWidth = Math.max(currentWidth, currentChildHookPointx);
//reset for new line
currentChildHookPointx = 0;
currentChildHookPointy += childHeight;
}
int nextChildHookPointx;
int nextChildHookPointy;
nextChildHookPointx = currentChildHookPointx + childWidth;
nextChildHookPointy = currentChildHookPointy;
currentHeight = Math.max(currentHeight, currentChildHookPointy + childHeight);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
lp.x = currentChildHookPointx;
lp.y = currentChildHookPointy;
currentChildHookPointx = nextChildHookPointx;
currentChildHookPointy = nextChildHookPointy;
}
currentWidth = Math.max(currentChildHookPointx, currentWidth);
setMeasuredDimension(resolveSize(currentWidth, widthMeasureSpec),
resolveSize(currentHeight, heightMeasureSpec));
}
@Override
protected void onLayout(boolean b, int left, int top, int right, int bottom) {
//call layout on children
int childCount = this.getChildCount();
for(int i = 0; i < childCount; i++) {
View child = this.getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y + child.getMeasuredHeight());
}
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MyFlowLayout.LayoutParams(getContext(), attrs);
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new MyFlowLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
@Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new MyFlowLayout.LayoutParams(p);
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof MyFlowLayout.LayoutParams;
}
public static class LayoutParams extends ViewGroup.MarginLayoutParams {
int spacing = -1;
int x = 0;
int y = 0;
LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
TypedArray t = c.obtainStyledAttributes(attrs, R.styleable.FlowLayout_Layout);
spacing = t.getDimensionPixelSize(R.styleable.FlowLayout_Layout_layout_space, 0);
t.recycle();
}
LayoutParams(int width, int height) {
super(width, height);
spacing = 0;
}
public LayoutParams(MarginLayoutParams source) {
super(source);
}
LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
}
}
Usage in a layout.xml file
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:cl="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.merryapps.customlayout.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
<com.merryapps.customlayout.MyFlowLayout
android:id="@+id/flw1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FF0000">
<Button
android:id="@+id/b1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello"
cl:layout_space="20dp"/>
<Button
android:id="@+id/b2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/world"/>
<Button
android:id="@+id/b4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/world"/>
<Button
android:id="@+id/b5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/world"/>
<Button
android:id="@+id/b6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/world"/>
</com.merryapps.customlayout.MyFlowLayout>
<Button
android:id="@+id/b3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/world"
android:textAllCaps="false"/>
</LinearLayout>
For show type of view one must go for flow layout :- There are many libraries available on Git Following is the example of, blazsolar/FlowLayout
Add this line in app.gradle
compile "com.wefika:flowlayout:<version>"
Usage:-
<com.wefika.flowlayout.FlowLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="start|top">
<View
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Lorem ipsum" />
</com.wefika.flowlayout.FlowLayout>
For detail implementation follow below link-
https://github.com/blazsolar/FlowLayout
You can try this links too:- https://github.com/xiaofeng-han/AndroidLibs/tree/master/flowlayoutmanager (try this) https://github.com/ApmeM/android-flowlayout https://gist.github.com/hzqtc/7940858
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With