0

I'm creating an app which has a flow layout. The FlowLayout implementation I found from the web (I don't remember exactly from where). Now, when my layout width greater than sum of the widths of its children, I need to fill the empty space, giving to each the proper width. I tried to give them MATCH_PARENT as width, but it didn't worked and each element was created from the "new line".
Please help me to solve my problem. Thanks in advance.

Here is a FlowLayout class implementation, which I use:

public class FlowLayout extends ViewGroup {
public static final String TAG = FlowLayout.class.getSimpleName();

private int mHorizontalSpacing = 10;
private int mVerticalSpacing = 10;

public FlowLayout(Context context) {
    super(context);
}

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();
    }
}

@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);

    int width = 0;
    int height = getPaddingTop();

    int currentWidth = getPaddingLeft();
    int currentHeight = 0;

    boolean breakLine = false;

    final int count = getChildCount();

    Log.d(TAG, "child count = " + count);

    for (int i = 0; i < count; i++) {
        View child = getChildAt(i);
        LayoutParams lp = (LayoutParams) child.getLayoutParams();
        measureChild(child, widthMeasureSpec, heightMeasureSpec);

        Log.d(TAG, "child " + i + " mw=" + child.getMeasuredWidth() + " mh=" + child.getMeasuredHeight());

        if (breakLine || currentWidth + child.getMeasuredWidth() > widthSize) {
            height += currentHeight + mVerticalSpacing;
            currentHeight = 0;
            if (currentWidth > width) width = currentWidth;
            currentWidth = getPaddingLeft();
        }

        int spacing = mHorizontalSpacing;
        if (lp.spacing != Integer.MAX_VALUE) {
            spacing = lp.spacing;
        }

        lp.x = currentWidth;
        lp.y = height;

        Log.d(TAG, "child " + i + " x=" + lp.x + " y=" + lp.y);

        currentWidth += child.getMeasuredWidth() + spacing;
        int childHeight = child.getMeasuredHeight();
        if (childHeight > currentHeight) currentHeight = childHeight;

        breakLine = lp.breakLine;
    }

    // after last row (patched by yuku)
    {
        height += currentHeight;
        if (currentWidth > width) width = currentWidth;
    }

    width += getPaddingRight();
    height += getPaddingBottom();

    Log.d(TAG, "onMeasure w=" + width + " h=" + height + " widthMeasureSpec=" + Integer.toHexString(widthMeasureSpec) + " heightMeasureSpec=" + Integer.toHexString(heightMeasureSpec));

    // don't resolve height (patched by yuku)
    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 checkLayoutParams(ViewGroup.LayoutParams p) {
    return p instanceof LayoutParams;
}

@Override public LayoutParams generateDefaultLayoutParams() {
    return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}

@Override public LayoutParams generateLayoutParams(AttributeSet attrs) {
    return new LayoutParams(getContext(), attrs);
}

@Override public LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
    return new LayoutParams(p.width, p.height);
}

public static class LayoutParams extends ViewGroup.LayoutParams {
    public boolean breakLine;
    public int spacing = Integer.MAX_VALUE;

    private int x;
    private int y;

    public LayoutParams(int width, int height) {
        super(width, height);
    }

    public LayoutParams(Context context, AttributeSet attrs) {
        super(context, attrs);

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout_LayoutParams);
        try {
            spacing = a.getDimensionPixelSize(R.styleable.FlowLayout_LayoutParams_layout_spacing, Integer.MAX_VALUE);
            breakLine = a.getBoolean(R.styleable.FlowLayout_LayoutParams_layout_breakLine, false);
        } finally {
            a.recycle();
        }       
    }
}
}

I want to achieve this effect:

enter image description here

instead of this:

enter image description here

Karlen Kishmiryan
  • 7,322
  • 5
  • 35
  • 45
  • Actually no one can answer a question about a custom unknown 3rd party control. But if it was the built-in LinearLayout, I would suggest to set width to 0 and weight to 1 on any element from the group. – vortexwolf Dec 26 '12 at 22:05
  • Hmm I think this is a custom Layout proposed by Romain Guy in a video. Karlen: you should at least post the exact code for the FlowLayout. Also, you might want to try to explain what you want to happen vs what is actually happening in more detail. – dmon Dec 26 '12 at 22:43
  • vortexx, thanks for your help, I'll try it ASAP. @dmon I've added the info you want. – Karlen Kishmiryan Dec 30 '12 at 09:03

1 Answers1

0

I think I got it, it's a bit tricky, but here's more or less what you need to do. In your original for loop, keep track of whether or not you've gone to a new line. Let's say you call this variable isOneLine and initialize it to true, set it to false as soon as you reset currentWidth to 0. Then, outside of your for loop:

if (isOneLine) {
  //There's always N + 1 padding "spaces"
  int paddingBetween = (widthSize - currentWidth)/(count + 1); 

  for (int i = 0; i < count; i++) {
    View child = getChildAt(i);
    LayoutParams lp = (LayoutParams) child.getLayoutParams();
    measureChild(child, widthMeasureSpec, heightMeasureSpec);

    int spacing = mHorizontalSpacing;
    if (lp.spacing != Integer.MAX_VALUE) {
        spacing = lp.spacing;
    }
    //Not sure exactly what spacing is here, but I assume you have to take it into account somewhere...
    lp.x = paddingBetween + currentWidth;
    lp.y = 0;

    currentWidth += child.getMeasuredWidth() + spacing + paddingBetween;
  }
}
dmon
  • 30,048
  • 8
  • 87
  • 96
  • Thank you for your answer @dmon, but I have a question: I can't find the line, where `currentWidth` resets to `0`. So where I must set the `isOneLine`'s value to `false`? – Karlen Kishmiryan Dec 31 '12 at 08:36
  • Right, you don't reset it to 0, it's reset to paddingLeft: `if (breakLine || currentWidth + child.getMeasuredWidth() > widthSize) {... currentWidth = getPaddingLeft(); }` – dmon Jan 01 '13 at 17:14
  • I've tried as you said, but, unfortunately, it doesn't effect to all flow layouts (I don't know why). But however there are some flow layouts, which works as I want. Are there any other suggestions? – Karlen Kishmiryan Jan 10 '13 at 09:46