55

I need a multi-line layout, which would behave as horizontal linear layout, but when there is not enough space to place new widget it would expand to next line, just like words in text. Widgets would be added there at runtime, and should go with wrap_content. Actually, there would be buttons.

Is there any widgets with such behaviour? Or give a suggestion about how to write such layout by myself.

Finally it should look like this:

draft for a multi-line layout

Raiv
  • 5,731
  • 1
  • 33
  • 51

5 Answers5

23

Check the comments: this will do the job

/*
*  Copyright 2011 Sherif
*/

private void populateText(LinearLayout ll, View[] views , Context mContext) { 
    Display display = getWindowManager().getDefaultDisplay();
    ll.removeAllViews();
    int maxWidth = display.getWidth() - 20;

    LinearLayout.LayoutParams params;
    LinearLayout newLL = new LinearLayout(mContext);
    newLL.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
            LayoutParams.WRAP_CONTENT));
    newLL.setGravity(Gravity.LEFT);
    newLL.setOrientation(LinearLayout.HORIZONTAL);

    int widthSoFar = 0;

    for (int i = 0 ; i < views.length ; i++ ){
        LinearLayout LL = new LinearLayout(mContext);
        LL.setOrientation(LinearLayout.HORIZONTAL);
        LL.setGravity(Gravity.CENTER_HORIZONTAL|Gravity.BOTTOM);
        LL.setLayoutParams(new ListView.LayoutParams(
                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
        //my old code
        //TV = new TextView(mContext);
        //TV.setText(textArray[i]);
        //TV.setTextSize(size);  <<<< SET TEXT SIZE
        //TV.measure(0, 0);
        views[i].measure(0,0);
        params = new LinearLayout.LayoutParams(views[i].getMeasuredWidth(),
                LayoutParams.WRAP_CONTENT);
        //params.setMargins(5, 0, 5, 0);  // YOU CAN USE THIS
        //LL.addView(TV, params);
        LL.addView(views[i], params);
        LL.measure(0, 0);
        widthSoFar += views[i].getMeasuredWidth();// YOU MAY NEED TO ADD THE MARGINS
        if (widthSoFar >= maxWidth) {
            ll.addView(newLL);

            newLL = new LinearLayout(mContext);
            newLL.setLayoutParams(new LayoutParams(
                    LayoutParams.FILL_PARENT,
                    LayoutParams.WRAP_CONTENT));
            newLL.setOrientation(LinearLayout.HORIZONTAL);
            newLL.setGravity(Gravity.LEFT);
            params = new LinearLayout.LayoutParams(LL
                    .getMeasuredWidth(), LL.getMeasuredHeight());
            newLL.addView(LL, params);
            widthSoFar = LL.getMeasuredWidth();
        } else {
            newLL.addView(LL);
        }
    }
    ll.addView(newLL);
}
bughi
  • 1,838
  • 1
  • 13
  • 24
Sherif elKhatib
  • 45,786
  • 16
  • 89
  • 106
  • @AndersMetnik it is defined in the code (`LL = new LinearLayout(mContext);`) – Sherif elKhatib Apr 30 '12 at 08:41
  • Hmm my IDE complains that LL is undefined, my guess is the i can just write LinearLayout in front of that (LL=new Lin...) And its golden? 2nd Q: How do i populate Views[] Tried making a View[] views, but it doesnt seem to have any add method or a like. Can i just use ArrayList and by chaning it a bit? – Anders Metnik Apr 30 '12 at 08:44
  • @AndersMetnik I am not sure if you are using the exact code, but there are no problems in this code. Anyway, of course you can use an ArrayList. views[] is an array of view so you can use it like this: views = new View[3]; views[0] = new TextView(); views[1] = new Bla(); views[2] = new Bla(); – Sherif elKhatib Apr 30 '12 at 09:00
  • 1
    Thanks, a reminder to everyone else, remember to set the ll(original linear layout) to vertical – Anders Metnik Apr 30 '12 at 12:20
  • @SherifelKhatib It propogates correctly, but somehow when i use EditText's it "locks" their sizes, so when typing into them, they wont resize. you know why? – Anders Metnik Apr 30 '12 at 12:38
  • @AndersMetnik I think it will be a bit hard to implement EditTexts and to allow them to resize and rearrange. Anyway, the EditText is not resizing because we are specifying the width in this line: `params = new LinearLayout.LayoutParams(TV.getMeasuredWidth(), LayoutParams.WRAP_CONTENT);` and `params = new LinearLayout.LayoutParams(LL.getMeasuredWidth(), LL.getMeasuredHeight());` – Sherif elKhatib Apr 30 '12 at 12:43
  • Yeah okay, I see now, Thanks a lot for the help. I know It's hard, I have extended the threshold (-20 you set early) to about 50 so they have some more space to waggle around in. Think thats the best solution for me :-) – Anders Metnik Apr 30 '12 at 12:48
19

Sherif's answer was good, but didn't handle the case where there may be extra views on either side of the LinearLayout in question. I've updated and cleaned up the code to handle this case:

/**
 * Copyright 2011 Sherif 
 * Updated by Karim Varela to handle LinearLayouts with other views on either side.
 * @param linearLayout
 * @param views : The views to wrap within LinearLayout
 * @param context
 * @param extraView : An extra view that may be to the right or left of your LinearLayout.
 * @author Karim Varela
 **/
private void populateViews(LinearLayout linearLayout, View[] views, Context context, View extraView)
{
    extraView.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);

    // kv : May need to replace 'getSherlockActivity()' with 'this' or 'getActivity()'
    Display display = getSherlockActivity().getWindowManager().getDefaultDisplay();
    linearLayout.removeAllViews();
    int maxWidth = display.getWidth() - extraView.getMeasuredWidth() - 20;

    linearLayout.setOrientation(LinearLayout.VERTICAL);

    LinearLayout.LayoutParams params;
    LinearLayout newLL = new LinearLayout(context);
    newLL.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
    newLL.setGravity(Gravity.LEFT);
    newLL.setOrientation(LinearLayout.HORIZONTAL);

    int widthSoFar = 0;

    for (int i = 0; i < views.length; i++)
    {
        LinearLayout LL = new LinearLayout(context);
        LL.setOrientation(LinearLayout.HORIZONTAL);
        LL.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
        LL.setLayoutParams(new ListView.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));

        views[i].measure(0, 0);
        params = new LinearLayout.LayoutParams(views[i].getMeasuredWidth(), LayoutParams.WRAP_CONTENT);
        params.setMargins(5, 0, 5, 0);

        LL.addView(views[i], params);
        LL.measure(0, 0);
        widthSoFar += views[i].getMeasuredWidth();
        if (widthSoFar >= maxWidth)
        {
            linearLayout.addView(newLL);

            newLL = new LinearLayout(context);
            newLL.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
            newLL.setOrientation(LinearLayout.HORIZONTAL);
            newLL.setGravity(Gravity.LEFT);
            params = new LinearLayout.LayoutParams(LL.getMeasuredWidth(), LL.getMeasuredHeight());
            newLL.addView(LL, params);
            widthSoFar = LL.getMeasuredWidth();
        }
        else
        {
            newLL.addView(LL);
        }
    }
    linearLayout.addView(newLL);
}

'

Karim Varela
  • 7,562
  • 10
  • 53
  • 78
  • good answer, i have one question, in this layout, is it posible to remove particular item ? thanks in advance – EugenUngurean Feb 08 '14 at 21:51
  • Yes, a LinearLayout is a child of ViewGroup so you can call removeView() on the LinearLayout. – Karim Varela Feb 18 '14 at 22:06
  • I could also suggest to retrieve window manager from passed context: WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display display = windowManager.getDefaultDisplay(); – MaiOM Jun 17 '14 at 09:10
  • if you don't need extraView: add some code (if (extraView != null)...) – Volodymyr Kulyk Sep 20 '16 at 09:02
5

You can handle this problem now easily with Google's Flexbox layout (https://github.com/google/flexbox-layout).

blade
  • 804
  • 1
  • 9
  • 18
2

Assuming you have Constraint Layout version 2.0.0 or higher, I believe you could use a ConstraintLayout with a androidx.constraintlayout.helper.widget.Flow child. The flow could have app:flow_wrapMode=chain or aligned depending on how you would like the layout to look.

Each of the list elements would be siblings of your Flow (ie. nested within the ConstraintLayout but without any constraints). Each element would have to have an id and those ids would have to be listed in the value for the Flow's app:constraint_referenced_ids property.

For more information you can check out these resources:

JHowzer
  • 3,684
  • 4
  • 30
  • 36
0

Now you can use Material Design Chips.

https://github.com/material-components/material-components-android/blob/master/docs/components/Chip.md

enter image description here