Float left with wrap in Android - android

I'm interesting to get the same effect like in css float: left style. I'm using now RelativeLayout and android:layout_toRightOf but need views to goes to next line when they can't fit. How to achieve this in android layout ?
<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">
<Button
android:id="#+id/a"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="300dp" />
<Button
android:id="#+id/b"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/a"
android:width="300dp" />
<Button
android:id="#+id/c"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/b"
android:width="300dp" />
</RelativeLayout>
I need something like in this example
On wider screen i need to get for example 3 buttons in one line, but on smaller screen have to 2 buttons in one line and third in second line. Like in images below.
Smaller device
Wider device

For single rows use a LinearLayout with horizontal orientation instead of a RelativeLayout.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<!-- add your views here -->
</LinearLayout>
For multiple rows you way want to automatically "flow" your elements to a new line. You can take a look at the android flow-layout project or try to implement your own according to this custom implementation
/** Custom view which extends {#link RelativeLayout}
* and which places its children horizontally,
* flowing over to a new line whenever it runs out of width.*/
public class HorizontalFlowLayout
extends RelativeLayout
{
/** Constructor to use when creating View from code.*/
public HorizontalFlowLayout(Context context)
{
super(context);
}
/** Constructor that is called when inflating View from XML.*/
public HorizontalFlowLayout(Context context, AttributeSet attrs)
{
super(context, attrs);
}
/** Perform inflation from XML and apply a class-specific base style.*/
public HorizontalFlowLayout(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
// need to call super.onMeasure(...) otherwise get some funny behaviour
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
// increment the x position as we progress through a line
int xpos = getPaddingLeft();
// increment the y position as we progress through the lines
int ypos = getPaddingTop();
// the height of the current line
int line_height = 0;
// go through children
// to work out the height required for this view
// call to measure size of children not needed I think?!
// getting child's measured height/width seems to work okay without it
//measureChildren(widthMeasureSpec, heightMeasureSpec);
View child;
MarginLayoutParams childMarginLayoutParams;
int childWidth, childHeight, childMarginLeft, childMarginRight, childMarginTop, childMarginBottom;
for (int i = 0; i < getChildCount(); i++)
{
child = getChildAt(i);
if (child.getVisibility() != GONE)
{
childWidth = child.getMeasuredWidth();
childHeight = child.getMeasuredHeight();
if (child.getLayoutParams() != null
&& child.getLayoutParams() instanceof MarginLayoutParams)
{
childMarginLayoutParams = (MarginLayoutParams)child.getLayoutParams();
childMarginLeft = childMarginLayoutParams.leftMargin;
childMarginRight = childMarginLayoutParams.rightMargin;
childMarginTop = childMarginLayoutParams.topMargin;
childMarginBottom = childMarginLayoutParams.bottomMargin;
}
else
{
childMarginLeft = 0;
childMarginRight = 0;
childMarginTop = 0;
childMarginBottom = 0;
}
if (xpos + childMarginLeft + childWidth + childMarginRight + getPaddingRight() > width)
{
// this child will need to go on a new line
xpos = getPaddingLeft();
ypos += line_height;
line_height = childMarginTop + childHeight + childMarginBottom;
}
else
// enough space for this child on the current line
line_height = Math.max(
line_height,
childMarginTop + childHeight + childMarginBottom);
xpos += childMarginLeft + childWidth + childMarginRight;
}
}
ypos += line_height + getPaddingBottom();
if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED)
// set height as measured since there's no height restrictions
height = ypos;
else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST
&& ypos < height)
// set height as measured since it's less than the maximum allowed
height = ypos;
setMeasuredDimension(width, height);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
// increment the x position as we progress through a line
int xpos = getPaddingLeft();
// increment the y position as we progress through the lines
int ypos = getPaddingTop();
// the height of the current line
int line_height = 0;
View child;
MarginLayoutParams childMarginLayoutParams;
int childWidth, childHeight, childMarginLeft, childMarginRight, childMarginTop, childMarginBottom;
for (int i = 0; i < getChildCount(); i++)
{
child = getChildAt(i);
if (child.getVisibility() != GONE)
{
childWidth = child.getMeasuredWidth();
childHeight = child.getMeasuredHeight();
if (child.getLayoutParams() != null
&& child.getLayoutParams() instanceof MarginLayoutParams)
{
childMarginLayoutParams = (MarginLayoutParams)child.getLayoutParams();
childMarginLeft = childMarginLayoutParams.leftMargin;
childMarginRight = childMarginLayoutParams.rightMargin;
childMarginTop = childMarginLayoutParams.topMargin;
childMarginBottom = childMarginLayoutParams.bottomMargin;
}
else
{
childMarginLeft = 0;
childMarginRight = 0;
childMarginTop = 0;
childMarginBottom = 0;
}
if (xpos + childMarginLeft + childWidth + childMarginRight + getPaddingRight() > r - l)
{
// this child will need to go on a new line
xpos = getPaddingLeft();
ypos += line_height;
line_height = childHeight + childMarginTop + childMarginBottom;
}
else
// enough space for this child on the current line
line_height = Math.max(
line_height,
childMarginTop + childHeight + childMarginBottom);
child.layout(
xpos + childMarginLeft,
ypos + childMarginTop,
xpos + childMarginLeft + childWidth,
ypos + childMarginTop + childHeight);
xpos += childMarginLeft + childWidth + childMarginRight;
}
}
}
}

Use this code to mek your three buttons in 1 line for any screen size device.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:weightSum="3" >
<Button
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"/>
<Button
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"/>
<Button
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"/>
</LinearLayout>

Related

onMeasure call to getMeasuredWidth or Height return 0 for children

I'm trying to dynamically add items to the my CustomView and make the final height the size of all the children added together. The only problem is the child.getMeasuredHeight or child.getMeasuredWidth always returns a value of 0 during run time. When I'm debugging it will randomly 1 out of 10 times seem to actually contain the data I was actually expecting with a value of 192. If I also hard code a value into the layout parameter instead of using WRAP_CONTENT it still shows a value of 0. Is there something that I'm doing wrong.
xml file
<CustomLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_marginTop="80dp"
android:orientation="vertical"
android:layout_width="200dp"
android:layout_height="100dp"
android:id="#+id/custom_layout"
android:background="#drawable/custom_shape"/>
</CustomLayout>
Here's a part of my CustomLayout.java class
public class CustomLayout extends LinearLayout {
public CustomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomLayout(Context context) {
super(context);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
int maxHeight = 0;
int maxWidth = 0;
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
for(int i=0; i <count; i++) {
final View child = getChildAt(i);
if(child.getVisibility() != GONE) {
maxHeight += child.getMeasuredHeight();
}
}
setMeasuredDimension(widthSize,maxHeight);
}
In a part of my main activity
Button b = new Button(this);
b.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
b.setText("Sample Test");
b.setTextSize(14);
mCustomView.addView(b);
You will need to ask each child of the ViewGroup to measure itself before you can access its dimensions. This is done with a call to [measureChild](https://developer.android.coma/reference/android/view/ViewGroup.html#measureChild(android.view.View, int, int)) or [measureChildWithMargins](https://developer.android.coma/reference/android/view/ViewGroup.html#measureChild(android.view.View, int, int)).
Take a look at the developer guide for ViewGroup to see how the child measurements are obtained.
// Iterate through all children, measuring them and computing our dimensions
// from their size.
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
// Measure the child.
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
// Update our size information based on the layout params. Children
// that asked to be positioned on the left or right go in those gutters.
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp.position == LayoutParams.POSITION_LEFT) {
mLeftWidth += Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
} else if (lp.position == LayoutParams.POSITION_RIGHT) {
mRightWidth += Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
} else {
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
}
maxHeight = Math.max(maxHeight,
child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
childState = combineMeasuredStates(childState, child.getMeasuredState());
}
}

How to set Scroll view in my layout

I am created a DashbordLayout to view list of button. I need to set scroll viewer in my layout. My layout is shown here:
<com.psybo.shahi.localareaportal.DashboardLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:background="#f8f9fe">
<Button
android:id="#+id/btn_news_feed"
style="#style/DashboardButton"
android:drawableTop="#drawable/place"
android:text="#string/my_place" />
<Button
android:id="#+id/btn_friends"
style="#style/DashboardButton"
android:drawableTop="#drawable/education"
android:text="#string/education" />
<!-- Messages Button -->
<Button
android:id="#+id/btn_messages"
style="#style/DashboardButton"
android:drawableTop="#drawable/bus"
android:text="#string/bus_time" />
<Button
android:id="#+id/btn_places"
style="#style/DashboardButton"
android:drawableTop="#drawable/train"
android:text="#string/train_time" />
<Button
android:id="#+id/btn_events"
style="#style/DashboardButton"
android:drawableTop="#drawable/auto"
android:text="#string/auto" />
<Button
android:id="#+id/btn_photos"
style="#style/DashboardButton"
android:drawableTop="#drawable/taxi"
android:text="#string/taxi" />
<Button
android:id="#+id/btn_goods"
style="#style/DashboardButton"
android:drawableTop="#drawable/goods"
android:text="#string/goods" />
<Button
android:id="#+id/btn_business"
style="#style/DashboardButton"
android:drawableTop="#drawable/business"
android:text="#string/business" />
<Button
android:id="#+id/btn_hospital"
style="#style/DashboardButton"
android:drawableTop="#drawable/hospital"
android:text="#string/hospital" />
<Button
android:id="#+id/btn_bleed"
style="#style/DashboardButton"
android:drawableTop="#drawable/blood"
android:text="#string/bleed_bank" />
<Button
android:id="#+id/btn_pani"
style="#style/DashboardButton"
android:drawableTop="#drawable/pani"
android:text="#string/pani" />
<Button
android:id="#+id/btn_emergency"
style="#style/DashboardButton"
android:drawableTop="#drawable/emergency"
android:text="#string/emergency" />
<Button
android:id="#+id/btn_feedback"
style="#style/DashboardButton"
android:drawableTop="#drawable/feedback"
android:text="#string/feedback" />
<Button
android:id="#+id/btn_we"
style="#style/DashboardButton"
android:drawableTop="#drawable/we"
android:text="#string/we" />
This layout is created with following class is given bellow.
package com.psybo.shahi.localareaportal;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
public class DashboardLayout extends ViewGroup {
private static final int UNEVEN_GRID_PENALTY_MULTIPLIER = 10;
private int mMaxChildWidth = 0;
private int mMaxChildHeight = 0;
public DashboardLayout(Context context) {
super(context, null);
}
public DashboardLayout(Context context, AttributeSet attrs) {
super(context, attrs, 0);
}
public DashboardLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mMaxChildWidth = 0;
mMaxChildHeight = 0;
// Measure once to find the maximum child size.
int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST);
int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST);
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
mMaxChildWidth = Math.max(mMaxChildWidth, child.getMeasuredWidth());
mMaxChildHeight = Math.max(mMaxChildHeight, child.getMeasuredHeight());
}
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
mMaxChildWidth, MeasureSpec.EXACTLY);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
mMaxChildHeight, MeasureSpec.EXACTLY);
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
setMeasuredDimension(
resolveSize(mMaxChildWidth, widthMeasureSpec),
resolveSize(mMaxChildHeight, heightMeasureSpec));
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int width = r - l;
int height = b - t;
final int count = getChildCount();
// Calculate the number of visible children.
int visibleCount = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
++visibleCount;
}
if (visibleCount == 0) {
return;
}
int bestSpaceDifference = Integer.MAX_VALUE;
int spaceDifference;
// Horizontal and vertical space between items
int hSpace = 0;
int vSpace = 0;
int cols = 1;
int rows;
while (true) {
rows = (visibleCount - 1) / cols + 1;
hSpace = ((width - mMaxChildWidth * cols) / (cols + 1));
vSpace = ((height - mMaxChildHeight * rows) / (rows + 1));
spaceDifference = Math.abs(vSpace - hSpace);
if (rows * cols != visibleCount) {
spaceDifference *= UNEVEN_GRID_PENALTY_MULTIPLIER;
}
if (spaceDifference < bestSpaceDifference) {
// Found a better whitespace squareness/ratio
bestSpaceDifference = spaceDifference;
if (rows == 1) {
break;
}
} else {
// This is a worse whitespace ratio, use the previous value of cols and exit.
--cols;
rows = (visibleCount - 1) / cols + 1;
hSpace = ((width - mMaxChildWidth * cols) / (cols + 1));
vSpace = ((height - mMaxChildHeight * rows) / (rows + 1));
break;
}
++cols;
}
hSpace = Math.max(0, hSpace);
vSpace = Math.max(0, vSpace);
// Re-use width/height variables to be child width/height.
width = (width - hSpace * (cols + 1)) / cols;
height = (height - vSpace * (rows + 1)) / rows;
int left, top;
int col, row;
int visibleIndex = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
row = visibleIndex / cols;
col = visibleIndex % cols;
left = hSpace * (col + 1) + width * col;
top = vSpace * (row + 1) + height * row;
child.layout(left, top,
(hSpace == 0 && col == cols - 1) ? r : (left + width),
(vSpace == 0 && row == rows - 1) ? b : (top + height));
++visibleIndex;
}
}
}
I am gives a Scrollviewer in the begin of layout.But the layout alignment changes like below image
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/ScrollView01"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
------- here DashbordLayout ------
</ScrollView>
I need to know how set scroll viewer in my alignment.
You need to set android:fillViewport="true" in your ScrollView
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/ScrollView01"
android:fillViewport="true"
android:layout_width="fill_parent"
android:layout_height="fill_parent">

Add TextView Dynamically To a view Android

I want to display multiple text view in a layout dynamically.
Suppose,User selects 10 items from drop down menu then I can display it to layout one after another.I need same view as attached image -
I have selected item in a list,now I am not getting how to display data in desire manner.When I try list view all data are display one after other vertically.
When I use Linearlayout data can be either added in horizontal or vertical order.So I am not getting correct way to display data.
Please help me in solving this issue.
Thanks in advance.
what you are looking for is called chips.why not use material design chips, you can refer it here. http://www.google.com/design/spec/components/chips.html#
you can implement like using grid view if you are not interested using chips just have one row of gridview and populate it there with one cell
Normally if you use LinearLayout with horizontal orientation, it will put your items as you want.
try this one..i used to acheive using custom view.if you are ok with this just give a try..
Custom View class is.
public class WrapLayout extends ViewGroup {
private int paddingHorizontal;
private int paddingVertical;
public WrapLayout(Context context) {
super(context);
init();
}
public WrapLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public WrapLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
paddingHorizontal = getResources().getDimensionPixelSize(10);
paddingVertical = getResources().getDimensionPixelSize(10);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int childLeft = getPaddingLeft();
int childTop = getPaddingTop();
int lineHeight = 0;
// 100 is a dummy number, widthMeasureSpec should always be EXACTLY for FlowLayout
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;
}
// let the child measure itself
child.measure(
getChildMeasureSpec(widthMeasureSpec, 0, child.getLayoutParams().width),
getChildMeasureSpec(heightMeasureSpec, 0, child.getLayoutParams().height));
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
// lineheight is the height of current line, should be the height of the heightest view
lineHeight = Math.max(childHeight, lineHeight);
if (childWidth + childLeft + getPaddingRight() > myWidth) {
// wrap this line
childLeft = getPaddingLeft();
childTop += paddingVertical + lineHeight;
lineHeight = childHeight;
}
childLeft += childWidth + paddingHorizontal;
}
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 lineHeight = 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();
lineHeight = Math.max(childHeight, lineHeight);
if (childWidth + childLeft + getPaddingRight() > myWidth) {
childLeft = getPaddingLeft();
childTop += paddingVertical + lineHeight;
lineHeight = childHeight;
}
child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
childLeft += childWidth + paddingHorizontal;
}
}
}
and my xml is :
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/container"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/editText_sample"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Add It"
android:id="#+id/addit"
/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- i mean where you put the WrapLayout class in your project that package name -->
<your.package.name.WrapLayout
android:id="#+id/flow_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"/>
</ScrollView>
</LinearLayout>
And finally in MainActivity
ViewGroup flowContainer;
In OnCreate
flowContainer= (ViewGroup) findViewById(R.id.flow_container);
final EditText text=(EditText)findViewById(R.id.editText_sample);
Button addIT=(Button)findViewById(R.id.addit);
addIT.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String data=text.getText().toString();
flowContainer.addView(createTextView(data),
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
});
Copy this function within mainactivity
private View createTextView(String text) {
TextView textView = new TextView(this);
textView.setText(text);
textView.setGravity(Gravity.CENTER);
textView.setCompoundDrawablesWithIntrinsicBounds(0,0,R.mipmap.ic_launcher,0);
return textView;
}

How to create a menubar like layout?

In one of my activities, I'd like to have something like a 'menubar', positioned at the bottom of the screen in portrait mode and at the right of the screen in landscape mode.
The menubar is permanently visible and will house imagebuttons, which have the same height but may have different widths. According to the number of 'buttons' in the 'menubar' a proper padding is chosen, so that the available height or witdh is used properly.
Basically it should look like this:
I already considered using a 'split action bar', as suggested here. But in this case I only have the desired effect in portrait mode.
At the moment I consider to implement a custom gridview layout to get this effect, or to modify the dashboard pattern found here.
Maybe some of you already implemented something like a 'menubar like layout' and can point me in the right direction.
EDIT
I slightly modified the dashboard pattern code mentioned above, it's just a first proof of concept, but seems to do what I intended:
public class Nx1Layout extends ViewGroup {
private static final String TAG = "Nx1Layout";
private int mMaxChildWidth = 0;
private int mMaxChildHeight = 0;
private int mScreenOrientation = Configuration.ORIENTATION_PORTRAIT;
public Nx1Layout(Context context) {
super(context, null);
}
public Nx1Layout(Context context, AttributeSet attrs) {
super(context, attrs, 0);
}
public Nx1Layout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setScreenOrientation(int screenOrientation) {
mScreenOrientation = screenOrientation;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mMaxChildWidth = 0;
mMaxChildHeight = 0;
// Measure once to find the maximum child size.
int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST);
int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.AT_MOST);
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
mMaxChildWidth = Math.max(mMaxChildWidth, child.getMeasuredWidth());
mMaxChildHeight = Math.max(mMaxChildHeight, child.getMeasuredHeight());
}
// Measure again for each child to be exactly the same size.
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxChildWidth, MeasureSpec.EXACTLY);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxChildHeight, MeasureSpec.EXACTLY);
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
setMeasuredDimension(resolveSize(mMaxChildWidth, widthMeasureSpec),resolveSize(mMaxChildHeight, heightMeasureSpec));
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int width = r - l;
int height = b - t;
final int count = getChildCount();
// Calculate the number of visible children.
int visibleCount = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
++visibleCount;
}
if (visibleCount == 0) {
return;
}
// Horizontal and vertical space between items
int hSpace = 0;
int vSpace = 0;
int cols, rows;
if(mScreenOrientation == Configuration.ORIENTATION_PORTRAIT) {
cols = visibleCount;
rows = 1;
} else {
cols = 1;
rows = visibleCount;
}
hSpace = ((width - mMaxChildWidth * cols) / (cols + 1));
vSpace = ((height - mMaxChildHeight * rows) / (rows + 1));
// Lay out children based on calculated best-fit number of rows and cols.
// If we chose a layout that has negative horizontal or vertical space, force it to zero.
hSpace = Math.max(0, hSpace);
vSpace = Math.max(0, vSpace);
// Re-use width/height variables to be child width/height.
width = (width - hSpace * (cols + 1)) / cols;
height = (height - vSpace * (rows + 1)) / rows;
int left, top;
int col, row;
int visibleIndex = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
row = visibleIndex / cols;
col = visibleIndex % cols;
left = hSpace * (col + 1) + width * col;
top = vSpace * (row + 1) + height * row;
child.layout(left, top, (hSpace == 0 && col == cols - 1) ? r : (left + width), (vSpace == 0 && row == rows - 1) ? b : (top + height));
++visibleIndex;
}
}
}
Here's some activity code to test it:
public class TestActivity extends SherlockFragmentActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
// setup ui
super.onCreate(savedInstanceState);
setContentView(R.layout.ui_test);
Nx1Layout layout = (Nx1Layout)findViewById(R.id.layout1);
layout.setScreenOrientation(getScreenOrientation());
}
public int getScreenOrientation()
{
Display getOrient = getWindowManager().getDefaultDisplay();
int orientation = Configuration.ORIENTATION_UNDEFINED;
if(getOrient.getWidth() == getOrient.getHeight()){
orientation = Configuration.ORIENTATION_SQUARE;
} else{
if(getOrient.getWidth() < getOrient.getHeight()){
orientation = Configuration.ORIENTATION_PORTRAIT;
}else {
orientation = Configuration.ORIENTATION_LANDSCAPE;
}
}
return orientation;
}}
here's the layout file for portrait mode:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.yourpackagename.Nx1Layout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="70dp"
android:id="#+id/layout1">
<ImageView
android:id="#+id/image1"
android:layout_width="70dp"
android:layout_height="70dp"
android:contentDescription="some description"
android:scaleType="centerInside"
android:padding="2dp"
android:src="#drawable/test_draw" />
<ImageView
android:id="#+id/image2"
android:layout_width="70dp"
android:layout_height="70dp"
android:contentDescription="some description"
android:scaleType="centerInside"
android:padding="2dp"
android:src="#drawable/test_draw" />
<ImageView
android:id="#+id/image3"
android:layout_width="70dp"
android:layout_height="70dp"
android:contentDescription="some description"
android:scaleType="centerInside"
android:padding="2dp"
android:src="#drawable/test_draw" />
</com.yourpackagename.Nx1Layout>
</LinearLayout>
and here the layout file for landscape mode:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.yourpackagename.Nx1Layout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="70dp"
android:layout_height="match_parent"
android:id="#+id/layout1">
<ImageView
android:id="#+id/image1"
android:layout_width="70dp"
android:layout_height="70dp"
android:contentDescription="some description"
android:scaleType="centerInside"
android:padding="2dp"
android:src="#drawable/test_draw" />
<ImageView
android:id="#+id/image2"
android:layout_width="70dp"
android:layout_height="70dp"
android:contentDescription="some description"
android:scaleType="centerInside"
android:padding="2dp"
android:src="#drawable/test_draw_narrow" />
<ImageView
android:id="#+id/image3"
android:layout_width="70dp"
android:layout_height="70dp"
android:contentDescription="some description"
android:scaleType="centerInside"
android:padding="2dp"
android:src="#drawable/test_draw" />
</com.yourpackagename.Nx1Layout>
</LinearLayout>
last but not least some drawable I used:
Feel free to comment on this solution...

How can I do something like a FlowLayout in Android?

How can I do something like a FlowLayout in Android?
You should use FlexboxLayout with flexWrap="wrap" attribute.
<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>
For build instructions, see the github repo.
implementation 'com.google.android:flexbox:2.0.1'
More about this - https://android-developers.googleblog.com/2017/02/build-flexible-layouts-with.html
I don't have enough reputation to post a comment to Romain Guy's answer but that's where this answer should be (I created an account just to share my edit).
Anyway, I see other people have found out his pretty cool FlowLayout solution has some issues.
I could find one myself and I saw, as others, that some children were clipped.
Looking in details at the algorithm it seems to be a very simple mistake in the calculation of the height. When the very last child is the one being put on a new line, then the height was not properly computed.
I cleaned up a bit the computation (there was a weird use of "height" vs. currentHeight).
The following change fixes the problem of "last child is clipped if on a new line":
#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));
}
There is a library from Google, called "flexbox-layout". You should check it out.
To use it in RecyclerView, you can use something like that:
val layoutManager = FlexboxLayoutManager(activity)
layoutManager.flexDirection = FlexDirection.ROW
layoutManager.flexWrap = FlexWrap.WRAP
layoutManager.justifyContent = JustifyContent.FLEX_START
layoutManager.alignItems = AlignItems.FLEX_START
recyclerView.layoutManager=layoutManager
Here is the custom class where you can achive layout like following with adding dynamicaly view (Also called 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;
}
}
}
}
Example :
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"));
}
}
});
}
}
}
There is now built in support in ConstraintLayout using a Flow widget. It has many options that can be used to achieve many type of flows.
Example:
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.helper.widget.Flow
android:layout_width="0dp"
android:layout_height="wrap_content"
app:constraint_referenced_ids="item_1,item_2,item_3"
app:flow_horizontalBias="0"
app:flow_horizontalGap="10dp"
app:flow_horizontalStyle="packed"
app:flow_verticalGap="8dp"
app:flow_wrapMode="aligned"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="#+id/item_1"
android:layout_width="50dp"
android:layout_height="50dp" />
<View
android:id="#+id/item_2"
android:layout_width="50dp"
android:layout_height="50dp" />
<View
android:id="#+id/item_3"
android:layout_width="50dp"
android:layout_height="50dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
Take a look in this post:
https://medium.com/#tapanrgohil/constraintlayout-flow-bye-bye-to-linerlayout-78fd7fa9b679
And here:
https://www.bignerdranch.com/blog/constraintlayout-flow-simple-grid-building-without-nested-layouts/
Like one of the previous answers, I started with the solution here: http://hzqtc.github.io/2013/12/android-custom-layout-flowlayout.html
I extended it to account for varying heights of children as below.
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;
}
}
}
}
I used this as a solution for wrapping multi-line TextEdits. Hope it helps!
A revision to #MattNotEquals() FlowLayout that supports MarginLayoutParams.
This is just a minimalist implementation of MarginLayoutParms to support left, right, top, and bottom margins.
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);
}
}
}
Nice simple self-contained FlowLayout code here (just a few concise gist.github files):
http://hzqtc.github.io/2013/12/android-custom-layout-flowlayout.html
However, the activity there out of the box didn't work for me to load the custom layout.
I found this work-around [ using the 2-param .inflate() call 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;
}

Categories

Resources