6

I am using a base adapter to set data in a listview dynamically. I tried to set listview height dynamically. It works perfectly if the textview inside it is single line. However if the textview is multiline, the height is not set properly. The height is set considering single line textview only. How can I set correctly set the height of listview items containing multiline textview. Here's the code:

Fragment code:

ListView mlistNews=(ListView)rootView.findViewById(R.id.news_listView);
mlistNews.setAdapter(new NewsAdapter(getActivity(),params_news));
Utils.setListViewHeightBasedOnItems(mlistNews);

Utility function:

public static boolean setListViewHeightBasedOnItems(ListView listView) {

    ListAdapter listAdapter = listView.getAdapter();
    if (listAdapter != null) {

        int numberOfItems = listAdapter.getCount();

        // Get total height of all items.
        int totalItemsHeight = 0;
        for (int itemPos = 0; itemPos < numberOfItems; itemPos++) {
            View item = listAdapter.getView(itemPos, null, listView);
            item.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
            totalItemsHeight += item.getMeasuredHeight();
        }

        // Get total height of all item dividers.
        int totalDividersHeight = listView.getDividerHeight() *
                (numberOfItems - 1);
        // Get padding
        int totalPadding = listView.getPaddingTop() + listView.getPaddingBottom();

        // Set list height.
        ViewGroup.LayoutParams params = listView.getLayoutParams();
        params.height = totalItemsHeight + totalDividersHeight + totalPadding;
        listView.setLayoutParams(params);
        listView.requestLayout();

        return true;

    } else {
        return false;
    }

}

dashboard_list_news.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp">
<TextView
    android:layout_width="0dp"
    android:id="@+id/news_item_text"
    android:layout_weight="1"
    android:layout_height="wrap_content"
    android:layout_gravity="left|center_vertical"
    android:layout_marginLeft="5dp"/>

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/chevron"
    android:layout_gravity="center_vertical|right"
    android:layout_marginLeft="5dp"
    android:layout_marginRight="10dp" />

</LinearLayout>

listView:

<ListView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/rounded_shape_list"
            android:id="@+id/news_listView"
            android:paddingTop="5dp"
            android:paddingBottom="5dp"
            android:divider="#6DCB99"
            android:dividerHeight="1dp" />
nlmm01
  • 871
  • 9
  • 12
  • You can keep listview rows of same height either by setting a specific height for TextView or try to set max lines feature for textview. – Karthika PB Mar 24 '15 at 05:49
  • Don't set the list item height. Because if its height is wrap content then i think it is auto adjust the row height. – SANAT Mar 24 '15 at 05:57
  • @KarthikaPB Textview content is dynamic and is inserted by adapter. It can be single line or multiline depending on content. – nlmm01 Mar 24 '15 at 06:04
  • @nlmm01 you should do any of the two suggested ways to keep equal listview rows – Karthika PB Mar 24 '15 at 06:12
  • I don't want listview rows to be of equal height. It is dependent on content. Also, it's the height of listview itseld that I am worried about, not the items. Posting a screenshot in original question to show issue. – nlmm01 Mar 24 '15 at 06:25
  • Updated the question with screenshot – nlmm01 Mar 24 '15 at 06:32
  • @nlmm01 check the accepted answer in this link http://stackoverflow.com/questions/16733129/how-to-show-fixed-number-of-rows-in-a-listview – Karthika PB Mar 24 '15 at 07:34

5 Answers5

7

As Teemu mentions you need to constrain the width. Sample code below.

float px = 300 * (listView.getResources().getDisplayMetrics().density);
item.measure(View.MeasureSpec.makeMeasureSpec((int)px, View.MeasureSpec.AT_MOST), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
baradas
  • 454
  • 1
  • 5
  • 14
  • 2
    why 300 ? what that number means? – Wackaloon Jul 07 '16 at 11:01
  • @AlexanderAgeichenko Have a look at my answer http://stackoverflow.com/questions/29225539/set-listview-height-dynamically-based-on-multiline-textview-inside-it/38803846#38803846 – Bhuro Aug 06 '16 at 11:37
3

Could the problem be this call:

item.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED))

Here you are not constraining the width, so the TextView expands to any width it requires to show all the text in single line. Therefore you will always get the height that equals to single line text? I think you should contrain the width here to what is available from the list view.

Teemu Lätti
  • 121
  • 1
  • 5
0

You can try with this custom listview to avoid listhieght having multiline textview problem

 <namespace.epicurio.NestedListView
           android:id="@+id/listComments"
           android:layout_width="fill_parent"
           android:layout_height="wrap_content"
           android:layout_marginBottom="2dp"
           android:divider="#000000"
           android:dividerHeight="1.0sp">
         </namespace.epicurio.NestedListView>


    public class NestedListView extends ListView implements OnTouchListener, OnScrollListener {

        private int listViewTouchAction;
        private static final int MAXIMUM_LIST_ITEMS_VIEWABLE = 99;

        public NestedListView(Context context, AttributeSet attrs) {
            super(context, attrs);
            listViewTouchAction = -1;
            setOnScrollListener(this);
            setOnTouchListener(this);
        }

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem,
                int visibleItemCount, int totalItemCount) {
            if (getAdapter() != null && getAdapter().getCount() > MAXIMUM_LIST_ITEMS_VIEWABLE) {
                if (listViewTouchAction == MotionEvent.ACTION_MOVE) {
                    scrollBy(0, -1);
                }
            }
        }

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);

            int newHeight = 0;
            final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
            int heightSize = MeasureSpec.getSize(heightMeasureSpec);
            if (heightMode != MeasureSpec.EXACTLY) {
                ListAdapter listAdapter = getAdapter();
                if (listAdapter != null && !listAdapter.isEmpty()) {
                    int listPosition = 0;
                    for (listPosition = 0; listPosition < listAdapter.getCount()
                            && listPosition < MAXIMUM_LIST_ITEMS_VIEWABLE; listPosition++) {
                        View listItem = listAdapter.getView(listPosition, null, this);
                        //now it will not throw a NPE if listItem is a ViewGroup instance
                        if (listItem instanceof ViewGroup) {
                            listItem.setLayoutParams(new LayoutParams(
                                    LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
                        }
                        listItem.measure(widthMeasureSpec, heightMeasureSpec);
                        newHeight += listItem.getMeasuredHeight();
                    }
                    newHeight += getDividerHeight() * listPosition;
                }
                if ((heightMode == MeasureSpec.AT_MOST) && (newHeight > heightSize)) {
                    if (newHeight > heightSize) {
                        newHeight = heightSize;
                    }
                }
            } else {
                newHeight = getMeasuredHeight();
            }
            setMeasuredDimension(getMeasuredWidth(), newHeight);
        }

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (getAdapter() != null && getAdapter().getCount() > MAXIMUM_LIST_ITEMS_VIEWABLE) {
                if (listViewTouchAction == MotionEvent.ACTION_MOVE) {
                    scrollBy(0, 1);
                }
            }
            return false;
        }
    }
SuperDude
  • 91
  • 2
  • 6
0

Try this code:

listview.post(new Runnable() {
    @Override
    public void run() {
        calculateListHeight(listview.getWidth());
    }
});

public void calculateListHeight(int width) {
    final ListAdapter adapter = listview.getAdapter();

    // Calculate the height of the ListView to display all items
    int totalHeight = listview.getPaddingTop() + listview.getPaddingBottom();
    for (int i = 0; i < adapter.getCount(); i++) {
        View item = adapter.getView(i, null, this);
        item.measure(
                View.MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
                View.MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
        );
        totalHeight += item.getMeasuredHeight();
    }
    ViewGroup.LayoutParams params = listview.getLayoutParams();
    params.height = totalHeight + (listview.getDividerHeight() * (adapter.getCount() - 1));

    listview.setLayoutParams(params);
    listview.requestLayout();
}
Hideme Sri
  • 193
  • 9
0

for @AlexanderAgeichenko Here is my implementation with @baradas and the Question

        int totalItemsHeight = 0;
        float px =  (customListView.getResources().getDisplayMetrics().density);
        totalItemsHeight = (int)px;
        Log.d(YOUR_TAG, " datta forloop ...  px " + px);
        Log.d(YOUR_TAG, " datta forloop ...  totalItemsHeight = (int)px=  " + totalItemsHeight);
        for (int itemPos = 0; itemPos < datta.length(); itemPos++) {
            View item = CustomListAdptr.getView(itemPos, null, customListView);
////            item.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
////                    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
            item.getMeasuredHeightAndState();
            Log.d(YOUR_TAG, " datta forloop item.getHeight() " + item.getHeight());
            Log.d(YOUR_TAG, " datta forloop item.getMeasuredHeight() " + item.getMeasuredHeight());
            Log.d(YOUR_TAG, " datta forloop item.getMeasuredHeightAndState() " + item.getMeasuredHeightAndState());
            int itmHeight = item.getHeight();
            Log.d(YOUR_TAG, " datta forloop ...  itmHeight " + itmHeight);
            item.measure(View.MeasureSpec.makeMeasureSpec((int)px, View.MeasureSpec.AT_MOST),
                    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
            totalItemsHeight = item.getMeasuredHeight();
//            Log.d(YOUR_TAG, " datta forloop ...  item.getMeasuredHeight() " + item.getMeasuredHeight());
            Log.d(YOUR_TAG, " datta forloop ...  totalItemsHeight=item.getMeasuredHeight()  " + totalItemsHeight);
        }
        Log.d(YOUR_TAG, " datta forloop ...  totalItemsHeight ==  " + totalItemsHeight);

        // Get total height of all item dividers.
        int totalDividersHeight = customListView.getDividerHeight() *
                (numberOfItems - 1);
        // Get padding
        int totalPadding = customListView.getPaddingTop() + customListView.getPaddingBottom();

        // Set list height.
        ViewGroup.LayoutParams params = customListView.getLayoutParams();
        params.height = totalItemsHeight + totalDividersHeight + totalPadding;
        customListView.setLayoutParams(params);
        customListView.requestLayout();
        customListView.canScrollList(0);

And the out put was

D/YOUR_TAG:  onViewCompleted() if(success) setting the list data
D/YOUR_TAG:  datta counts 3
D/YOUR_TAG:  datta forloop ...  px 2.0
D/YOUR_TAG:  datta forloop ...  totalItemsHeight = (int)px=  2
D/YOUR_TAG:  datta forloop item.getMeasuredHeightAndState() 0
D/YOUR_TAG:  datta forloop item.getMeasuredHeight() 0
D/YOUR_TAG:  datta forloop item.getHeight() 0
D/YOUR_TAG:  datta forloop ...  itmHeight 0
D/YOUR_TAG:  datta forloop ...  totalItemsHeight=item.getMeasuredHeight()  1709
D/YOUR_TAG:  datta forloop item.getMeasuredHeightAndState() 0
D/YOUR_TAG:  datta forloop item.getMeasuredHeight() 0
D/YOUR_TAG:  datta forloop item.getHeight() 0
D/YOUR_TAG:  datta forloop ...  itmHeight 0
D/YOUR_TAG:  datta forloop ...  totalItemsHeight=item.getMeasuredHeight()  1709
D/YOUR_TAG:  datta forloop item.getMeasuredHeightAndState() 0
D/YOUR_TAG:  datta forloop item.getMeasuredHeight() 0
D/YOUR_TAG:  datta forloop item.getHeight() 0
D/YOUR_TAG:  datta forloop ...  itmHeight 0
D/YOUR_TAG:  datta forloop ...  totalItemsHeight=item.getMeasuredHeight()  1709
D/YOUR_TAG:  datta forloop ...  totalItemsHeight ==  1709
D/YOUR_TAG:  getView() -> unit_option= 0 :: class java.lang.String

WITH 110 float px = 110 *(customListView.getResources().getDisplayMetrics().density);

D/YOUR_TAG:  onViewCompleted() if(success) setting the list data
D/YOUR_TAG:  datta counts 3
D/YOUR_TAG:  datta forloop ...  px 220.0
D/YOUR_TAG:  datta forloop ...  totalItemsHeight = (int)px=  220
D/YOUR_TAG:  datta forloop item.getMeasuredHeightAndState() 0
D/YOUR_TAG:  datta forloop item.getMeasuredHeight() 0
D/YOUR_TAG:  datta forloop item.getHeight() 0
D/YOUR_TAG:  datta forloop ...  itmHeight 0
D/YOUR_TAG:  datta forloop ...  totalItemsHeight=item.getMeasuredHeight()  159
D/YOUR_TAG:  datta forloop item.getMeasuredHeightAndState() 0
D/YOUR_TAG:  datta forloop item.getMeasuredHeight() 0
D/YOUR_TAG:  datta forloop item.getHeight() 0
D/YOUR_TAG:  datta forloop ...  itmHeight 0
D/YOUR_TAG:  datta forloop ...  totalItemsHeight=item.getMeasuredHeight()  159
D/YOUR_TAG:  datta forloop item.getMeasuredHeightAndState() 0
D/YOUR_TAG:  datta forloop item.getMeasuredHeight() 0
D/YOUR_TAG:  datta forloop item.getHeight() 0
D/YOUR_TAG:  datta forloop ...  itmHeight 0
D/YOUR_TAG:  datta forloop ...  totalItemsHeight=item.getMeasuredHeight()  159
D/YOUR_TAG:  datta forloop ...  totalItemsHeight ==  159
Bhuro
  • 348
  • 4
  • 22
  • getMeasuredHeight get in effect when Global layout content is changed ... means adding / deleting/ moving / hiding visibility (GONE only) of any view ... – Bhuro Aug 08 '16 at 13:13