6

I have a FrameLayout that loads Fragments by tapping on tabs in a TabWidget. I can't figure out how to make the height of the FrameLayout as tall as its content, so that the whole containing ScrollView will scroll together as one instead of a separate scrolling view.

Here's a visual example of this Fragment's structure:

enter image description here

As you can see, the Frame Layout Visible Height only reveals one row of the Fragment, when in fact, there are a few. I can scroll within the FrameLayout to see the other rows as it is now, but that's not what I'm going for. The FrameLayout is made up of a LinearLayout containing a GridView with their layout_heights set to wrap_content.

I tried hardcoding the height of the FrameLayout to something like 500dp and it works great except for the fact that it's no longer dynamically sized. Would I need to resize the FrameLayout programmatically each time a new image is loaded into the inner content? Is there a layout attribute I can set so it'll stretch its height to match its inner content?

Here's my layout xml file:

<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<LinearLayout
    android:orientation="vertical"

    android:layout_width="fill_parent"
    android:layout_height="wrap_content">
    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="200dp">
        <!-- CONTAINS USER INFO AND STATS -->
    </RelativeLayout>
    <android.support.v4.app.FragmentTabHost
        android:id="@android:id/tabhost"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">
        <LinearLayout
            android:orientation="vertical"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:background="#ffffff">
            <TabWidget
                android:id="@android:id/tabs"
                android:layout_width="fill_parent"
                android:layout_height="60dp"
                android:weightSum="2">
            </TabWidget>
            <FrameLayout
                android:id="@android:id/tabcontent"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                >
            </FrameLayout>
        </LinearLayout>
    </android.support.v4.app.FragmentTabHost>
</LinearLayout>
</ScrollView>

Thank you!


Since I'm going to set a bounty on this, I thought I'd share what I've figured out so far.

In the thumbnails, onSuccess when each image is loaded, I'm calling a function in the GridLayout that holds the images that counts the images and sets the height of the GridLayout. This works fine, although it seems like it'd be a bit inefficient.

What I'm doing is setting the GridLayout height and then calling requestLayout and invalidate on it and it's parent(s). This works, but not as the images loading. It'll work if I go to a different tab and return to the thumbnails, oddly enough. Which makes me think I'm not updating at the right time or on the right object.

Anyway, that said. Does anyone know how to make the height of a GridLayout expand to hold its contents (instead of scrolling) so I can scroll the entire page (including the top section)?


I should also add the GridView layout:

<GridView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/grid"
android:stretchMode="columnWidth"
android:gravity="center"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:drawSelectorOnTop="false"
android:fastScrollAlwaysVisible="false"
android:fastScrollEnabled="false"
android:numColumns="3"
android:choiceMode="none">
</GridView>
Keith
  • 4,144
  • 7
  • 25
  • 43
  • i like your way of describing your problem (i mean the pic), great job... – pskink Oct 24 '14 at 08:33
  • Please reconsider the layout: it is not the android way! For example the tabs should stay on top. – brescia123 Oct 24 '14 at 09:00
  • You're preaching to the choir @brescia123 ! I'm just matching a design given to me~ – Keith Oct 24 '14 at 09:46
  • and thanks @pskink ! i was hoping it'd help describe the problem – Keith Oct 24 '14 at 09:46
  • Change Height of – Pankaj Arora Oct 27 '14 at 09:54
  • While you claim to be using a `GridLayout`, your comments about inner scrolling and the posted XML show that you are in fact using a `GridView`, which is an independent scrolling container. `GridLayout` would not have this issue, but on the other hand it cannot resolve it's column width dynamically. – corsair992 Oct 27 '14 at 23:53
  • http://stackoverflow.com/questions/2312683/calculate-the-size-of-a-list-view-or-how-to-tell-it-to-fully-expand has some solutions to make `ListView` full size. Should be similar for `GridView` since the only difference are multiple items per row. Alternatively, use an actual `GridLayout`, those are no scalable window, and fill it in code with content. – zapl Oct 28 '14 at 09:31
  • Oh very interesting about the `GridLayout` vs `GridView`, I didn't even catch that. I looked into `GridLayout` and it doesn't seem to take an adapter, but requires XML layout? Either way thanks for the heads up; I'll check it out! – Keith Nov 02 '14 at 05:05

3 Answers3

4

I was in a similar situation but I had a ListView instead of a GridView. You are right in the part when you have to set the height dynamically each time you add an item or if you call notifyDataSetChanged().

THIS CODE IS FOR LISTVIEW WITH DIFFERENT HEIGHT FOR EACH ROW

private void setListViewHeightBasedOnChildren(MyQueueAdapter listAdapter) {

    int desiredWidth = MeasureSpec.makeMeasureSpec(
            mListView.getWidth(), MeasureSpec.AT_MOST);
    int totalHeight = 0;

    View view = null;
    int i;
    for (i = 0; i < listAdapter.getCount(); i++) {
        view = listAdapter.getView(i, view, mListView);

        view.measure(desiredWidth, MeasureSpec.UNSPECIFIED);

        totalHeight += view.getMeasuredHeight();
    }

    ViewGroup.LayoutParams params = mListView.getLayoutParams();
    params.height = heightList
            + (mListView.getDividerHeight() * (listAdapter
                    .getCount() + 3));

    heightListComplete = params.height;
    mListView.setLayoutParams(params);

}

You need to modify this code according to your needs, you don't need the loop as the height of each row is static in your case. If you need more help let me know.

ALTERNATIVE

If you know the height of the view in dp you can easily convert the dp in px and set the height of your gridview according to number of rows.

Naveen
  • 1,703
  • 13
  • 22
  • Keep in mind that this would force the entire layout (with all the rows) to be resolved immediately (and all the images to be stored in memory simultaneously), instead of performing the lazy recycling of rows that `GridView` would otherwise perform. One way to avoid this would be to use `GridView`'s own scrolling by adding the top layout as it's header, but that would require all the tabs to contain a `GridView` layout (each setting it's own `ListAdapter` on the `GridView`). There might also be other possible solutions, but they would be significantly more complex to implement. – corsair992 Oct 28 '14 at 00:11
  • Yes that's why I said he doesn't need the loop. Height of only one view will suffice and if he already knows the height of the row in dp he can easily convert it to px and set the height of the gridview. – Naveen Oct 28 '14 at 04:17
  • You don't seem to have understood my comment. See the comments and accepted answer on the [answer](http://stackoverflow.com/questions/3495890/how-can-i-put-a-listview-into-a-scrollview-without-it-collapsing/3495908#3495908) you seem to have derived this from (and which should probably be attributed in your answer). – corsair992 Oct 28 '14 at 10:06
  • Thank you very much Naveen! I was changing the height using `rootView.height=` instead of using `layoutParams`. PHEW – Keith Oct 30 '14 at 08:41
2

When using dynamic sizes you'll run into problems once you put match_parent inside a wrap_content thing. One tries to get a small as it's content and the content tries to be as big as it's parent. Neither side will know how to scale properly in the end.

ScrollView is such a thing that falls in this category. It's a scalable window to it's content so it can't be wrap_content and the content can't be match_parent because it's parent is a virtual infinite space.

  • Change <ScrollView android:layout_height="wrap_content" to match_parent (or a fixed size).

To solve the size of the content

  • set the root layout (LinearLayout in your case) of your ScrollView to be a fixed size so it's content can be match_parent again.
  • use wrap_content all the way.
  • combine the two: wrap_content until a child defines an absolute size, then match_parent inside there.

The wrap_content route will only work if all the elements in the layout from inner to outer most expand properly based on their content. Nothing can rely on parent bounds unless you add some.

Your content looks rather dynamic in size. So it is likely that you need to use some code to manually set sizes based on content. E.g. if those images inside your tab frame are a GridView (essentially ScrollView with grid content again) you'll need to set it's size manually. More than 1 degree of freedom in wrapping dynamically sizing containers isn't solvable automatically.

zapl
  • 63,179
  • 10
  • 123
  • 154
-1

Parent of your frame layout is linear layout whose height is wrap_content. also, your framelayout's height is wrap_content. change both of them to fill_parent. using match_parent is more preferred now a days insted of fill_parent

Hirak Chhatbar
  • 3,159
  • 1
  • 27
  • 36
  • Thank you Hirak. That didn't change anything, unfortunately. Do you have any other ideas? – Keith Oct 24 '14 at 10:06
  • check every parents. linear layout has its parent relative layout with 200dp. when u say linear layout's height as match_parent, its actually 200dp. – Hirak Chhatbar Oct 24 '14 at 10:20
  • plz tell me what exactly u want. so that i can help according to ur particular requirement – Hirak Chhatbar Oct 24 '14 at 10:20
  • Thank you again Hirak, I tried setting every parent to match_parent but it didn't work. What I'm trying to do is have the FrameLayout's height be automatically changed when new items asyncronously downloaded from the internet get added to the FrameLayout view. Instead, it seems to set to the height of a single item instead of the fully loaded height which will be taller, of course. Do I need to tell it to remeasure when each item is downloaded? – Keith Oct 24 '14 at 10:23
  • aah... ok, let me explain. Actually what u define in xml is always static. it never changes. whatever is inside that view fits inself inside that without altering the size of layout in which it is present. As u can see in image above, Fragment Tab host and relative layout shares the screen (almost equally as it seems). Now what ever is inside that fragment tab host will takes its height. Its not like u will scroll on the screen and the relative layout will shift itself up and frame layout will get its height – Hirak Chhatbar Oct 24 '14 at 10:34
  • Ah yes, I see what you mean. Basically if I set the FrameLayout to 500dp, it scrolls all together (with the RelativeLayout above it), which is what I want since the FrameLayout will load images, potentially 100's and that top RelativeLayout is taking up too much space, so I'd like it to scroll with it. – Keith Oct 24 '14 at 10:45
  • (y) m glad i was able to help u. plz vote up if u found my answer helpful :D – Hirak Chhatbar Oct 24 '14 at 10:49
  • Oh, sorry, I would but that didn't solve the problem :( ~ Like I said if i set the FrameLayout to 500dp it works as I need it to, but if I set it to fill_parent it doesn't grow properly ~ – Keith Oct 24 '14 at 10:58
  • because with 500dp it is overlaying its parents layout. with fill_parent it only occupies what it have – Hirak Chhatbar Oct 24 '14 at 10:59
  • Oh sure I understand that. I'm looking for a way to change the height of the FrameLayout as new children are added so that the height matches the height of the content – Keith Oct 24 '14 at 11:16