1
votes

I have listView within scrollView (yes, I know thats bad approach. Better to use headers/footers). I know that scrollView and listView have both scroll and that causes some conflict. In this case I need to set height of list manually. And thats the problem since listView has custom adapter.

Layout:

<?xml version="1.0" encoding="utf-8"?>

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ScrollView01"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:scrollbars="none" >
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/fragment_lessons"
        android:background="#FAF9F9"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.cbsystematic.mobile.itvdn.LessonsFragment">

        <TextView
            android:id="@+id/course_title"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="Описание курса: "
            android:textSize="16sp"
            android:textStyle="bold"
            android:textColor="#000000"/>

        <TextView
            android:id="@+id/course_description"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:textSize="16sp"
            android:layout_below="@id/course_title"
            android:textColor="#000000"/>

        <TextView
            android:id="@+id/course_duration"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:textSize="14sp"
            android:textStyle="italic"
            android:layout_below="@id/course_description"
            android:textColor="#000000"/>

        <ListView android:id="@android:id/list"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_below="@id/course_duration"/>
        </RelativeLayout>
    </LinearLayout>
</ScrollView>

Custom List adapter:

class LessonsItemAdapter extends ArrayAdapter<LessonData> {
private Activity myContext;
private ArrayList<LessonData> datas;

public LessonsItemAdapter(Context context, int textViewResourceId, ArrayList<LessonData> objects){
    super(context, textViewResourceId, objects);

    myContext = (Activity) context;
    datas = objects;
}

public View getView(int position, View convertView, ViewGroup parent){
    ViewHolder viewHolder;

    if(convertView == null) {
        LayoutInflater inflater = myContext.getLayoutInflater();
        convertView = inflater.inflate(R.layout.lessons_list_item, null);

        viewHolder = new ViewHolder();
        viewHolder.postNameView = (TextView) convertView.findViewById(R.id.lessons_name);
        viewHolder.postDurationView = (TextView) convertView.findViewById(R.id.lessons_duration);

        convertView.setTag(viewHolder);
    } else {
        viewHolder = (ViewHolder) convertView.getTag();
    }

    //(position + 1) + ") " used to display 1) 2) 3) etc of lessons
    viewHolder.postNameView.setText((position + 1) + ") " + datas.get(position).getName());
    viewHolder.postDurationView.setText("Длительность: " + datas.get(position).getDuration().substring(0,8));

    return convertView;
}

static class ViewHolder{
    TextView postNameView;
    TextView postDurationView;
}
}

Method of finding height:

    public static void setListViewSize(AbsListView listView) {
    ListAdapter listAdapter = listView.getAdapter();
    if (listAdapter == null)
        return;

    int desiredWidth = MeasureSpec.makeMeasureSpec(listView.getWidth(), MeasureSpec.UNSPECIFIED);
    int totalHeight = 0;
    View view = null;
    for (int i = 0; i < listAdapter.getCount(); i++) {
        view = listAdapter.getView(i, view, listView);
        if (i == 0)
            view.setLayoutParams(new ViewGroup.LayoutParams(desiredWidth, AbsListView.LayoutParams.WRAP_CONTENT));

        view.measure(desiredWidth, MeasureSpec.UNSPECIFIED);
        totalHeight += view.getMeasuredHeight();
    }
    ViewGroup.LayoutParams params = listView.getLayoutParams();
    params.height = totalHeight + (listView.getMeasuredHeight() * (listAdapter.getCount() + 1));
    listView.setLayoutParams(params);
    listView.requestLayout();
}

Thats how I set the content of list:

    mAdapter = new LessonsItemAdapter(getActivity(),
            R.layout.lessons_list_item, parserJson.getLessons(courseUrl).getLessonsArray());

    // Set the adapter
    mListView = (AbsListView) view.findViewById(android.R.id.list);
    ((AdapterView<ListAdapter>) mListView).setAdapter(mAdapter);

    // Set OnItemClickListener so we can be notified on item clicks
    mListView.setOnItemClickListener(this);
    ListHelper.setListViewSize(mListView);

And thats the result:

enter image description here

The entire View is scrolling. But the last item always shows only by half. You see, the field Duration ("Длительность") is not showing. Although the last item of list is clicakble.

Please help me with that method of defining the total height. Thanks in advance. Appreciate any help.

3
android:layout_height="match_parent"? it should be android:layout_height="wrap_content" when you are setting heigh programaticallyBhavik Mehta
root view of the lessons_list_item layout is linear or relative ?user1140237
@BhavikMehta changed to fill_parent. Didn't help.AnZyuZya
@user1140237 Its a relative layout inside list layout wrapped into scroll viewAnZyuZya
Apply android:layout_marginBottom="20dp" for ListView.Piyush

3 Answers

1
votes

Use this piece of code. It will reset the size of listview according to number of items.

public static void setListViewHeightBasedOnChildren(ListView listView) {
    ListAdapter listAdapter = listView.getAdapter();
    if (listAdapter == null)
        return;

    int desiredWidth = MeasureSpec.makeMeasureSpec(listView.getWidth(),
            MeasureSpec.UNSPECIFIED);
    int totalHeight = 0;
    View view = null;
    for (int i = 0; i < listAdapter.getCount(); i++) {
        view = listAdapter.getView(i, view, listView);
        if (i == 0)
            view.setLayoutParams(new ViewGroup.LayoutParams(desiredWidth,
                    LayoutParams.WRAP_CONTENT));

        view.measure(desiredWidth, MeasureSpec.UNSPECIFIED);
        totalHeight += view.getMeasuredHeight();
    }
    ViewGroup.LayoutParams params = listView.getLayoutParams();
    params.height = totalHeight
            + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
    listView.setLayoutParams(params);
    listView.requestLayout();
}
1
votes

Your ListView in your layout file should be like

 <ListView android:id="@android:id/list"
            android:layout_width="match_parent"
             android:layout_height="wrap_content" 
            android:layout_below="@id/course_duration"/>

Now in your code where you want the ListView's height to be adjusted according to its children place the following method.

public static void setListViewHeightBasedOnChildren(ListView listView) {
            ListAdapter listAdapter = listView.getAdapter();
            if (listAdapter == null) {
                // pre-condition
                return;
            }

            int totalHeight = 0;
            for (int i = 0; i < listAdapter.getCount(); i++) {
                View listItem = listAdapter.getView(i, null, listView);
                listItem.measure(0, 0);
                totalHeight += listItem.getMeasuredHeight();
            }

            ViewGroup.LayoutParams params = listView.getLayoutParams();
            params.height = totalHeight
                    + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
            listView.setLayoutParams(params);
        }

just call it by passing your ListView inside it

  setListViewHeightBasedOnChildren(list);

Simple but powerful solution

0
votes

try code below from this blog

@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);
}