Friday, July 12, 2013

Dynamic resize of views and offscreen view size calculation in LinearLayout

The layout is usually handled by a set of xml files, where we specify how the components of our view group are displayed.
It should be the preferred way of generating and composing the views. It is possible to merge layouts, to inflate custom views etc.
There are some occasions, though, where the xml syntax and attributes are not powerful enough.
For example, we can have a vertical linear layout where, at some point, we inflate a custom view. We don't know how big (and, above all), tall is this view at compile time. The problem is, all or some views below it can disappear because our "big" view takes all available space.

A solution might be to wrap the views in a relative layout. All good, all fine, in that way we can specify that the big view must be below a child, and above another. Then we align the first view to the top, and the last to the bottom.

But the problem is that the height of the relative layout can't be wrapped!
As stated also in the documentation (which should have read before spending a couple of hours in adapting the layout :P)
Note that you cannot have a circular dependency between the size of the RelativeLayout and the position of its children. For example, you cannot have a RelativeLayout whose height is set to WRAP_CONTENT and a child set to ALIGN_PARENT_BOTTOM.
The only way is adjusting the layout at runtime: we must calculate the height of the big view  and if its bottom point is at the end of the layout and there are still views to draw, then we need to calculate their value and resize it accordingly.

A problem is that we can't get simply the height of the offscreen views! Since they are offscreen, Android will give us 0 as the height.

What we can do, is using the class called View.MeasureSpec and calculate the view size like this:



Then, we need to find a way to adjust the size of the big view; again, we can't do that in onCreate() or other similar callbacks, as the view is not rendered yet and we will get 0 as height of the view.
We could override the View.onMeasure() method but as it gets called multiple times by the framework, it is not very reliable.
Best thing is adding a ViewTreeObserver and do all the calculations in its callback:



The code of this example is available here:
https://github.com/nalitzis/TestDynamicViews




2 comments: