There are 4 kinds of layouts in Android:
- Linear – Views can be aligned horizontally or vertically
- Relative – Views are located according to the relationship to each other
- List View – Views are displayed one by one vertically and scrollable
- Grid View – Views are displayed inside a vertical scrollable grid
For these non-scrollable layout, views should not exceed the maximum height/width of the parent view. Sometimes there is a need for autowrap/autobreak layout, in which a view will occupy the next “line” when it reach the border of its parent, as shown in below figure.
This kind of layout is very useful when working on keypad/remote controller like apps, in which user can define various number of buttons and each button can have different width according to its content. Another benefit of autobreak feature is that there is no need of extra routine/configuration for the handling of different resolution and rotation event.
We can implement such layout based on relative layout with below steps:
- Walk through all the views and accumulate the width horizontally
- Mark the head of current “line” and the previous view
- For each view, compare accumulated width against layout width
- If there is enough width left for current view, align it to the right of previous view, and update the reference of previous view.
- If there is not enough width left for current view, align it to the bottom of head view, and update the reference of head view.
Below code implements a simple version of such layout, assuming that buttons is of the same height but different width.
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RelativeLayout layout = (RelativeLayout) findViewById(R.id.main_relative_layout); for (int i = 0; i < btns.length; i++) { btns[i] = new VKButton(this); btns[i].setId(BUTTON_ID_BASE+i); layout.addView(btns[i]); //add button into the layout dynamically } layout.post(new Runnable() { //post a Runnable that call reLayout to layout object @Override public void run() { reLayout(); } }); } protected void reLayout() { // TODO Auto-generated method stub int totalWidth; int curWidth; int layoutPadX; RelativeLayout layout = (RelativeLayout) findViewById(R.id.main_relative_layout); int w = layout.getMeasuredWidth(); //get width of current layout totalWidth = 0; layoutPadX = layout.getPaddingLeft() + layout.getPaddingRight(); w = w - layoutPadX; Button upBtn = null, leftBtn = btns[0]; for (int i = 0; i < btns.length; i++) { //create a layout parameter first RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT); curWidth = btns[i].getMeasuredWidth(); //get the width, beware of the caller site if (i > 0) { lp.addRule(RelativeLayout.END_OF, btns[i - 1].getId()); //add END_OF property by default. if (totalWidth + curWidth > w) { //check if need to wrap upBtn = leftBtn; leftBtn = btns[i]; totalWidth = curWidth; lp.removeRule(RelativeLayout.END_OF); //remove the END_OF for wrap case } else { totalWidth += curWidth; } if (upBtn != null) //add below property for none-first "line" lp.addRule(RelativeLayout.BELOW, upBtn.getId()); } else { totalWidth += curWidth; } btns[i].setLayoutParams(lp); //set layout parameter for button } }
Notice that, the calling of getMeasuredWidth inside onCreate will turns out to be 0, so let the layout.post() do the tricky part.
Comments on this entry are closed.
Mhm, the relative and the wrapper?
Don’t have time to study it now, but very promising!
remove rule needs min 17 version. but my min version was 14.
i cannot change min version is there any other trick.
You can use this to remove Rule
lp.addRule(RelativeLayout.END_OF, 0);