Expandable Listview inside scrollview

Expandable listview inside scrollview

When we have a ListView expandable inside ScrollView, we must make this in our layout, always expandable listview must be the last view in our layout:

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_expandable_scroll_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fillViewport="true">

    <LinearLayout
        android:id="@+id/LinearLayout1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin">

<!-- your code --->

 <ExpandableListView
            android:id="@+id/activity_expandable_list_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/white"/>

    </LinearLayout>
</ScrollView>

In your onCreate

mListView = (ExpandableListView) findViewById(R.id.activity_expandable_list_view);
        MyExpandableListAdapter adapter = new MyExpandableListAdapter(this,
                mGroups);
        mListView.setAdapter(adapter);
        mListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {

            @Override
            public boolean onGroupClick(ExpandableListView parent, View v,
                                        int groupPosition, long id) {
                setListViewHeight(parent, groupPosition);
                return false;
            }
        });

You need this function:

    private void setListViewHeight(ExpandableListView listView,
                                   int group) {
        ExpandableListAdapter listAdapter = (ExpandableListAdapter) listView.getExpandableListAdapter();
        int totalHeight = 0;
        int desiredWidth = View.MeasureSpec.makeMeasureSpec(listView.getWidth(),
                View.MeasureSpec.EXACTLY);
        for (int i = 0; i < listAdapter.getGroupCount(); i++) {
            View groupItem = listAdapter.getGroupView(i, false, null, listView);
            groupItem.measure(desiredWidth, View.MeasureSpec.UNSPECIFIED);

            totalHeight += groupItem.getMeasuredHeight();

            if (((listView.isGroupExpanded(i)) && (i != group))
                    || ((!listView.isGroupExpanded(i)) && (i == group))) {
                for (int j = 0; j < listAdapter.getChildrenCount(i); j++) {
                    View listItem = listAdapter.getChildView(i, j, false, null,
                            listView);
                    listItem.measure(desiredWidth, View.MeasureSpec.UNSPECIFIED);

                    totalHeight += listItem.getMeasuredHeight();

                }
            }
        }

        ViewGroup.LayoutParams params = listView.getLayoutParams();
        int height = totalHeight
                + (listView.getDividerHeight() * (listAdapter.getGroupCount() - 1));
        if (height < 10)
            height = 200;
        params.height = height;
        listView.setLayoutParams(params);
        listView.requestLayout();

    }

Download code

41 thoughts on “Expandable Listview inside scrollview

  1. Hello
    it is working for me but i tried to understand the content of the function setListViewHeight but i didn’t understand it
    please if u have time to explain what u are doing in it i will be very happy
    thank u in advance

    • Hi!!! of course!!!
      I hope that my explanation will help you

      The first step, we create variable height and width.

      for each group we add group’s height.

      totalHeight += groupItem.getMeasuredHeight();

      if group is expandable, for each item we add item’s height.

      totalHeight += listItem.getMeasuredHeight();

      We use params

      ViewGroup.LayoutParams params = listView.getLayoutParams(); 

      LayoutParams are used by views to tell their parents how they want to be laid out. See ViewGroup Layout Attributes for a list of all child view attributes that this class supports.

      Then we add divider height * number of group and we get total height in List.

      int height = totalHeight
                  + (listView.getDividerHeight() * (listAdapter.getGroupCount() - 1));

      getDividerHeight ()
      Returns the height of the divider that will be drawn between each item in the list.

      Now we check the minimum.

      if (height &lt; 10)
              height = 200;

      we add height to params

      params.height = height;

      the last line we use
      requestLayout()
      Call this when something has changed which has invalidated the layout of this view.

  2. Hi,

    Thanks for the code. It works great once the group item is clicked but it only shows one group item initially. Any help is appreciated!

  3. It works fine for me .

    But I have a problem in my case I don’t want to re-create activity on orientation change so I restrict it through manifest file so resolution specification I calculated in portrait mode reflected in landscape also , that’s why when I rotate my device it shows extra space in after last child .

    Please help…

    Thanks in advance 🙂

  4. If you want to set height of exp. listview, when activity will start, you can use this function:
    setListViewHeightForGroups(your_expList_AfterAcceptingAdapter);

    public void setListViewHeightForGroups(ExpandableListView listView){
    ExpandableListAdapter listAdapter = (ExpandableListAdapter) listView.getExpandableListAdapter();
    int totalHeight = 0;
    int desiredWidth = View.MeasureSpec.makeMeasureSpec(listView.getWidth(),
    View.MeasureSpec.EXACTLY);
    for (int i = 0; i < listAdapter.getGroupCount(); i++) {
    View groupItem = listAdapter.getGroupView(i, false, null, listView);
    groupItem.measure(desiredWidth, View.MeasureSpec.UNSPECIFIED);

    totalHeight += groupItem.getMeasuredHeight();
    }
    ViewGroup.LayoutParams params = listView.getLayoutParams();
    int height = totalHeight
    + (listView.getDividerHeight() * (listAdapter.getGroupCount() – 1));
    if (height < 10)
    height = 200;
    params.height = height;
    listView.setLayoutParams(params);
    listView.requestLayout();
    }

  5. Is this condition right?

    if (((listView.isGroupExpanded(i)) && (i != group))
    || ((!listView.isGroupExpanded(i)) && (i == group))) {

    Seems like its never satisfies the condition.

    • oh, i got it, i was using in groupexpand and not on groupclick. i changed the condition, so its working on groupexpand

  6. Thank you for this nice solution !

    I just have an issue, I set android:dividerHeight=”5dp” in my ExpandableListView but when doing that I only see the first item.

    What did I miss ?

    Thank you

  7. Thanks! It works (almost).

    However , it only works after you click on a group item.
    What about when you just open the layout and haven’t opened any group item?

  8. for (int j = 0; j < 5; j++) {
    Group group = new Group("Test " + j);
    for (int i = 0; i < 500; i++) {
    group.children.add("Sub Item" + i);
    }
    mGroups.append(j, group);
    }
    i give this much of count it hide some values

    • is this correct??please help me….
      private void setListViewHeight(ExpandableListView listView,
      int group) {
      ExpandableListAdapter listAdapter = (ExpandableListAdapter) listView.getExpandableListAdapter();
      int totalHeight = 0;
      int desiredWidth = View.MeasureSpec.makeMeasureSpec(listView.getWidth(),
      View.MeasureSpec.AT_MOST);
      for (int i = 0; i < listAdapter.getGroupCount(); i++) {
      View groupItem = listAdapter.getGroupView(i, false, null, listView);
      groupItem.measure(desiredWidth, View.MeasureSpec.UNSPECIFIED);

      totalHeight += groupItem.getMeasuredHeight();

      if (((listView.isGroupExpanded(i)) && (i != group))
      || ((!listView.isGroupExpanded(i)) && (i == group))) {
      for (int j = 0; j < listAdapter.getChildrenCount(i); j++) {
      View listItem = listAdapter.getChildView(i, j, false, null,
      listView);
      listItem.measure(desiredWidth, View.MeasureSpec.UNSPECIFIED);

      totalHeight += listItem.getMeasuredHeight();

      }

      }

      ViewGroup.LayoutParams params = listView.getLayoutParams();
      int height = totalHeight
      + (listView.getDividerHeight() * (listAdapter.getGroupCount() – 1));
      if (height < 10)
      height = 200;
      params.height = height;
      listView.setLayoutParams(params);
      listView.requestLayout();

      }

      }

  9. I have put explistview inside Linearlayout. when I click on the last group, it don’t scroll to end of last group. when I remove Linearlayout, it’s ok. how can I fix it ?? This is my code:

  10. Thanks a lot , its working fine .

    But if Expandable view having a DividerHeight then below line is required inside inner for loop. Neither it was creating spacing problem and hiding the Group.

    totalHeight = totalHeight+ (listView.getDividerHeight()*listAdapter.getChildrenCount(i)-1);
    /******************************************************************/
    private void setListViewHeight(ExpandableListView listView,
    int group) {
    ExpandableListAdapter listAdapter = listView.getExpandableListAdapter();
    int totalHeight = 0;
    int desiredWidth = View.MeasureSpec.makeMeasureSpec(listView.getWidth(),
    View.MeasureSpec.EXACTLY);
    for (int i = 0; i < listAdapter.getGroupCount(); i++) {
    View groupItem = listAdapter.getGroupView(i, false, null, listView);
    groupItem.measure(desiredWidth, View.MeasureSpec.UNSPECIFIED);
    totalHeight += groupItem.getMeasuredHeight();
    if (((listView.isGroupExpanded(i)) && (i != group))
    || ((!listView.isGroupExpanded(i)) && (i == group))) {
    for (int j = 0; j < listAdapter.getChildrenCount(i); j++) {

    View listItem = listAdapter.getChildView(i, j, false, null,
    listView);
    listItem.measure(desiredWidth, View.MeasureSpec.UNSPECIFIED);

    totalHeight += listItem.getMeasuredHeight();
    }
    totalHeight = totalHeight+ (listView.getDividerHeight()*listAdapter.getChildrenCount(i)-1);
    }
    }

    ViewGroup.LayoutParams params = listView.getLayoutParams();
    int height = totalHeight
    + (listView.getDividerHeight() * (listAdapter.getGroupCount() – 1));
    if (height < 10)
    height = 200;
    params.height = height;
    listView.setLayoutParams(params);
    listView.requestLayout();

    }

  11. It worked for me. The problem is the initial view only shows the first group (or first item of the list group) and after clicking that is when the rest of the groups be only view-able.

  12. It worked for me. But facing one problem , the problem is the initial view only shows the first group (or first item of the list group) and after clicking that is when the rest of the groups be visible.

    I have to click on first group. then ‘setOnGroupClickListener’ invokes and set dynamic height.

    I try to call ‘setListViewHeight’ method, out side of ‘ ‘setOnGroupClickListener’ block. like below

    setListViewHeight(expListView, groupPosition);

    but then problem is what will be what will be groupPosition value? then I placed

    inside for loop,

    for(int i = 1; i< expandableList.size(); i++){
    setListViewHeight(expandableListView,i);
    }
    But it show unwanted extra space below expandable listview. it is actually measuring extra height.

    what else I can do?

  13. Hi Cabezas,
    I am using above code, working absolutely fine. But, when i try to expand list item and expand on other item. The list view height is varying on the basis of previous expanded item.i.e unnecessary height is appending at the bottom of the list view.
    Can you help to fix this issue please?
    Thanks in Advance
    Sri

  14. I am Great Thankful to you, for such a nice and informative tutorial, This tutorial Saved my life, i was facing the same problem for three days

Leave a Reply

Your email address will not be published. Required fields are marked *