Canberk Ozcelik
Canberk Ozcelik

Reputation: 641

ExpandableListView dynamic child height

I have a ExpandableListView which is inside an LinearLayout as container and set by using CustomAdapter.
For its children, I'm using onGroupClick() method to send an request to specific service and getting result as String, then filling child list of clicked group item.

The Problem is since I can't get the updated height (after service response has set to text view of child view's text view) the linearlayout container height doesn't increase the way it should. And it also creates a scrolling problem.

Though list child item xml is WRAP_CONTENT as below:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="@dimen/rowHeight"
android:background="@color/colorLightYellow">

<TextView
    android:id="@+id/tvTitle"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_centerVertical="true"
    android:layout_marginLeft="@dimen/marginGeneral"
    android:layout_toLeftOf="@+id/tvCount"
    android:gravity="center_vertical"
    android:paddingBottom="@dimen/marginGeneral"
    android:paddingTop="@dimen/marginGeneral"
    android:text="tvTitle"
    android:textColor="@color/colorGray"
    android:textSize="@dimen/fontSizeNormal" />
...
</RelativeLayout>

So the code part is little long stay with me:

@Override
public boolean onGroupClick(ExpandableListView parent, View v, final int groupPosition, long id) {
    Map<Item, List<ItemDetail>> childList = detailExpandableAdapter.getChildList();
    final Item item = detailExpandableAdapter.getGroup(groupPosition);
    if (childList.get(item).size() == 0) {
        startProgressDialog();

        GlobalApplication.getService().getItemDetails(Session.getCurrent().getSessionId(), getItem.item.itemNo, item.name, new ServiceCallback<GetItemDetails>() {
            @Override
            public void onSuccess(GetItemDetails response) {
                stopProgressDialog();

                List<ItemDetail> itemDetailList = null;

                if (GetItemDetails.isSuccess(response)) {
                    itemDetailList = response.getItemDetailList();
                } else {
                    itemDetail itemDetail = new ItemDetail();
                    itemDetail.resultDesc = response.getResult().getResultDesc();

                    if (StringUtils.isNullOrWhitespace(itemDetail.resultDesc)) {
                        itemDetail.resultDesc = Result.getGeneralFailResult().getResultDesc();
                    }

                    itemDetailList = new ArrayList<ItemDetail>();
                    itemDetailList.add(itemDetail);
                }

                if (itemDetailList != null) {
                    Map<Item, List<ItemDetail>> childList = detailExpandableAdapter.getChildList();

                    if (childList.containsKey(item)) {
                        childList.remove(item);
                    }
                    childList.put(item, itemDetailList);

                    detailExpandableAdapter.setChildList(childList);
                    detailExpandableAdapter.notifyDataSetChanged();
                    detailExpandableAdapter.notifyDataSetInvalidated();

                    listViewLastItems.expandGroup(groupPosition);
                }
            }

            @Override
            public void onFail() {
                stopProgressDialog();
            }
        });

        return false;
    }

    return false;
}

@Override
public void onGroupExpand(int groupPosition) {
    setExpandableListViewHeightStable(listViewLastItems, llListViewItemDetailContainer);
    if (lastExpanded != -1 && groupPosition != lastExpanded)
        listViewItems.collapseGroup(lastExpanded);
    lastExpanded = groupPosition;
}

public void setExpandableListViewHeight(ExpandableListView expandableListView, LinearLayout linearLayoutParent){
    try {
        ExpandableListAdapter expandableListAdapter = expandableListView.getExpandableListAdapter();

        int totalHeight = 0;
        int desiredWidth = View.MeasureSpec.makeMeasureSpec(expandableListView.getWidth(), View.MeasureSpec.UNSPECIFIED);

        for (int i = 0; i < expandableListAdapter.getGroupCount(); i++) {
            View groupItem = expandableListAdapter.getGroupView(i, false, null, expandableListView);
            groupItem.measure(desiredWidth, View.MeasureSpec.UNSPECIFIED);

            //Logger.debug("recalculateExpandableListViewHeight listItem:"+groupItem.getMeasuredHeight());
            totalHeight += groupItem.getMeasuredHeight();

            if (expandableListView.isGroupExpanded(i)){
                for (int j = 0; j < expandableListAdapter.getChildrenCount(i); j++) {
                    View listItemChild = expandableListAdapter.getChildView(i, j, false, null, expandableListView);

                    listItemChild.setLayoutParams(new ViewGroup.LayoutParams(desiredWidth, View.MeasureSpec.UNSPECIFIED));
                    listItemChild.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));

                    Logger.debug("recalculateExpandableListViewHeight listItemChild:" + listItemChild.getMeasuredHeight());
                    totalHeight += listItemChild.getMeasuredHeight();
                }
            }
        }

        linearLayoutParent.getLayoutParams().height = totalHeight + (expandableListAdapter.getGroupCount() * expandableListView.getDividerHeight());
        linearLayoutParent.requestLayout();
    } catch (Exception e) {
        Logger.printStackTrace(e);
    }
}  

Update: this is the linear layout I use as container

<LinearLayout
                android:id="@+id/llListViewItemContainer"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_below="@+id/tvItemDueDate"
                android:layout_marginTop="@dimen/marginGeneral"
                android:orientation="vertical"/>

Update 2: I'm adding ExpandableListView to LinearLayout dynamically.

listViewItems = new ExpandableListView(getContext());
listViewItems.setScrollContainer(false);
listViewItems.setDivider(new ColorDrawable(getResources().getColor(R.color.colorLightGray)));
listViewItems.setDividerHeight(UIHelper.convertDptoPixels(1));
listViewItems.setGroupIndicator(null);
listViewItems.setOnGroupClickListener(this);
listViewItems.setOnGroupExpandListener(this);
listViewItems.setOnGroupCollapseListener(this);
//generate empty child list
Map<Item, List<ItemDetail>> childMap = new HashMap<>();
for (Item item : getItems.getItemList()) {
    childMap.put(item, new ArrayList<ItemDetail>());
                }
detailExpandableAdapter = new detailExpandableAdapter(getActivity(), getItems.getItemList(), childMap);
listViewItems.setAdapter(detailExpandableAdapterF);
listViewItems.removeAllViews();
listViewItems.addView(listViewLastItems);
UIHelper.setExpandableListViewHeightStable(listViewItems, llListViewDetailContainer);

Upvotes: 2

Views: 3591

Answers (2)

Rahul
Rahul

Reputation: 10635

I would suggest you to use HeaderView property of ExpandableListView. AS ExpandableListView is a derived class of ListView so HeaderView property must be there as I believe.

Issues in using ListView inside ScrollView -

  1. Performance - If you going to play with measure property of ListView then it will surely affect recycling of cells.
  2. User Experience - Strange behaviour comes when ListView's parent is another scrollable view.

Better move your LinearLayout stuff inside another view, then inflate that view and either put it in Header or Footer of ListView as per your need.

 // Inflated View which is going to be use as Header of view
  ViewGroup headerView = (ViewGroup)getLayoutInflater().inflate(R.layout.list_header,expListView,false);
  // Add that view to Header
  your_expandale_listView.addHeaderView(headerView);

Upvotes: 0

Pravin Divraniya
Pravin Divraniya

Reputation: 4374

Use below given custom ExpandableListView class and override onMeasure method.

public class MyExpandableListView extends ExpandableListView {

    public MyExpandableListView(Context context) {
        super(context);
    }

    public MyExpandableListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyExpandableListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int heightMeasureSpec_custom = MeasureSpec.makeMeasureSpec(
                Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec_custom);
        ViewGroup.LayoutParams params = getLayoutParams();
        params.height = getMeasuredHeight();
    }
}

And use it like,

<com.app.custom.NonScrollExpandableListView 

    android:layout_width="match_parent"
    android:layout_height="wrap_content" /> 

Replace com.app.custom with your package name in which you put this custom class.

If possible use NestedScrollView instead of ScrollView, as it supports acting as both a nested scrolling parent and child on both new and old versions of Android. Nested scrolling is enabled by default.

Let me know if this help you or not. Happy Coding!!!

Upvotes: 7

Related Questions