Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Line-breaking widget layout for Android

I'm trying to create an activity that presents some data to the user. The data is such that it can be divided into 'words', each being a widget, and sequence of 'words' would form the data ('sentence'?), the ViewGroup widget containing the words. As space required for all 'words' in a 'sentence' would exceed the available horizontal space on the display, I would like to wrap these 'sentences' as you would a normal piece of text.

The following code:

public class WrapTest extends Activity {     /** Called when the activity is first created. */     @Override     public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         LinearLayout l = new LinearLayout(this);         LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(                 LinearLayout.LayoutParams.FILL_PARENT,                 LinearLayout.LayoutParams.WRAP_CONTENT);         LinearLayout.LayoutParams mlp = new LinearLayout.LayoutParams(                 new ViewGroup.MarginLayoutParams(                         LinearLayout.LayoutParams.WRAP_CONTENT,                         LinearLayout.LayoutParams.WRAP_CONTENT));         mlp.setMargins(0, 0, 2, 0);          for (int i = 0; i < 10; i++) {             TextView t = new TextView(this);             t.setText("Hello");             t.setBackgroundColor(Color.RED);             t.setSingleLine(true);             l.addView(t, mlp);         }          setContentView(l, lp);     } } 

yields something like the left picture, but I would want a layout presenting the same widgets like in the right one.

non-wrapping

wrapping

Is there such a layout or combination of layouts and parameters, or do I have to implement my own ViewGroup for this?

like image 790
Henrik Gustafsson Avatar asked Feb 14 '09 17:02

Henrik Gustafsson


1 Answers

I made my own layout that does what I want, but it is quite limited at the moment. Comments and improvement suggestions are of course welcome.

The activity:

package se.fnord.xmms2.predicate;  import se.fnord.android.layout.PredicateLayout; import android.app.Activity; import android.graphics.Color; import android.os.Bundle; import android.widget.TextView;  public class Predicate extends Activity {     /** Called when the activity is first created. */     @Override     public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);          PredicateLayout l = new PredicateLayout(this);         for (int i = 0; i < 10; i++) {             TextView t = new TextView(this);             t.setText("Hello");             t.setBackgroundColor(Color.RED);             t.setSingleLine(true);             l.addView(t, new PredicateLayout.LayoutParams(2, 0));         }          setContentView(l);     } } 

Or in an XML layout:

<se.fnord.android.layout.PredicateLayout     android:id="@+id/predicate_layout"     android:layout_width="fill_parent"      android:layout_height="wrap_content" /> 

And the Layout:

package se.fnord.android.layout;  import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup;  /**  * ViewGroup that arranges child views in a similar way to text, with them laid  * out one line at a time and "wrapping" to the next line as needed.  *   * Code licensed under CC-by-SA  *    * @author Henrik Gustafsson  * @see http://stackoverflow.com/questions/549451/line-breaking-widget-layout-for-android  * @license http://creativecommons.org/licenses/by-sa/2.5/  *  */ public class PredicateLayout extends ViewGroup {      private int line_height;      public PredicateLayout(Context context) {         super(context);     }          public PredicateLayout(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);          // The next line is WRONG!!! Doesn't take into account requested MeasureSpec mode!         int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();         final int count = getChildCount();         int line_height = 0;                  int xpos = getPaddingLeft();         int ypos = getPaddingTop();                  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),                         MeasureSpec.makeMeasureSpec(height, MeasureSpec.UNSPECIFIED));                                  final int childw = child.getMeasuredWidth();                 line_height = Math.max(line_height, child.getMeasuredHeight() + lp.height);                                  if (xpos + childw > width) {                     xpos = getPaddingLeft();                     ypos += line_height;                 }                                  xpos += childw + lp.width;             }         }         this.line_height = line_height;                  if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED){             height = ypos + line_height;          } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST){             if (ypos + line_height < height){                 height = ypos + line_height;             }         }         setMeasuredDimension(width, height);     }      @Override     protected LayoutParams generateDefaultLayoutParams() {         return new LayoutParams(1, 1); // default of 1px spacing     }      @Override     protected boolean checkLayoutParams(LayoutParams p) {         return (p instanceof LayoutParams);     }      @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;                 }                 child.layout(xpos, ypos, xpos + childw, ypos + childh);                 xpos += childw + lp.width;             }         }     } } 

With the result:

Wrapped widgets

like image 147
9 revs, 7 users 71% Avatar answered Oct 13 '22 01:10

9 revs, 7 users 71%