Android ListView rows in ScrollView not fully displayed - clipped

35,737

Solution 1

It's a BAD practice to encapsulate ListView within a ScrollView because ListView itself contains scrolling capabilities. You should implement a solution that does not contain such hierarchy of views and I hope it will do the magic :)

Solution 2

Use this method created by https://stackoverflow.com/users/205192/dougw

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

Solution 3

Here resource of main layout with ScrollView:

<ScrollView android:layout_height="fill_parent" android:layout_width="fill_parent">
<LinearLayout android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@+id/parentLayout"/>
</ScrollView>

Here the code to insert items:

parentLayout.removeAllViews();
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
for (int i = comments.size() - 1; i >= 0; i--) {
CommentInfo comment = comments.get(i);
View view = inflater.inflate(your_resource_id, null, false);

TextView commentsContent =(TextView)view.findViewById(R.id.commentContent);
if (commentsContent != null) {
    String data = String.format("%s (by %s, %s)",  comment.getCommentText(), comment.getUserName(),
        commentsContent.setTextSize(st.getTextSize());
    commentsContent.setText(data);

}

parentLayout.addView(view, 0);

}       

Solution 4

I had the same problem in my project.You need to create simple LinearLayout inside ScrollView. After that you need create new View with your listview item xml using LayoutInflater. After creation put all data in new View and add to LinearLayout as child view:

linearLayot.addView(newView, position_you_need).

Hope it would help you!

Solution 5

I took the recommendation of not using a ListView element inside a ScrollView to heart and decided to use a slightly brute force method to achieve what I need. Since there is a constant number of up to five list rows that need to be displayed I removed the ListView instantiation from the xml file and replaced it with five instances of rows:

<include android:id="@+id/info_comment_1" layout="@layout/chat_single_message" />
<include android:id="@+id/info_comment_2" layout="@layout/chat_single_message" />
<include android:id="@+id/info_comment_3" layout="@layout/chat_single_message" />
<include android:id="@+id/info_comment_4" layout="@layout/chat_single_message" />
<include android:id="@+id/info_comment_5" layout="@layout/chat_single_message" />

In the Activity class I declare five placeholders for these views:

private RelativeLayout mChatMessages[] = new RelativeLayout[COMMENTS_NUMBER];

and initialize them with:

mChatMessages[0] = (RelativeLayout) mMoreInfoLayout.findViewById(R.id.info_comment_1);
mChatMessages[1] = (RelativeLayout) mMoreInfoLayout.findViewById(R.id.info_comment_2);
mChatMessages[2] = (RelativeLayout) mMoreInfoLayout.findViewById(R.id.info_comment_3);
mChatMessages[3] = (RelativeLayout) mMoreInfoLayout.findViewById(R.id.info_comment_4);
mChatMessages[4] = (RelativeLayout) mMoreInfoLayout.findViewById(R.id.info_comment_5);

Then, whenever a new message is received I use the ChatAdapter (the same I used for the ListView previously) and call its getView() method:

protected void updateChatMessages() {
  int msgCount = mChatAdapter.getCount();
  for (int i = 0; i < COMMENTS_NUMBER; i++) {
    if (msgCount <= i) {
      mChatMessages[i].setVisibility(View.GONE);
    } else {
      mChatMessages[i] = (RelativeLayout) mChatAdapter.getView(i, mChatMessages[i], null); 
      mChatMessages[i].setVisibility(View.VISIBLE);
    }   
  }
}

I don't inflate the pariculat views ever again since the only thing that changes is the content of each row, not the layout. This means there is no performance penalty here.

This is basically a manual implementation of a ListView with a limited maximum number of elements. This time, however, ScrollView is able to fit them nicely and nothing gets clipped.

For a dynamic number of rows the approach suggested by Layko could be employed with the views being instantiated programatically and added to the LinearLayout inside the ScrollView.

Share:
35,737
PawelPredki
Author by

PawelPredki

I'm a PhD student from Poland and a freelancer programmer. I'm part of a small startup, called Tellyo, which allows users to grab and share interesting moments from their favorite TV shows. We have done successful tests with Spain's RTVE and Finland's MTV3 and we're currently waiting for bigger things to come. I mostly program in C/C++ and Java but I'm also able to do some scripting in PHP. I have experience developing mobile applications, mostly in Android.

Updated on March 19, 2020

Comments

  • PawelPredki
    PawelPredki about 4 years

    I encountered a problem when embedding a ListView inside a ScrollView, or at least that's where I guess the problem comes from. The ListView element is a fairly simple one:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/item_root"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/general_background_list_middle" 
        android:paddingTop="4dp"
        android:paddingBottom="4dp"
        android:paddingRight="0dp"
        android:paddingLeft="0dp">
    
        <ImageView
            android:id="@+id/chat_friends_avatar"       
            android:layout_width="45dp"
            android:layout_height="45dp"
            android:layout_marginLeft="7dp"
            android:layout_marginRight="8dp"
            android:paddingRight="0dp" 
            android:paddingLeft="0dp"
            android:layout_alignParentLeft="true"
            android:layout_alignParentTop="true"
            android:src="@drawable/friends_icon_avatar_default"/>
    
        <TextView
            android:id="@+id/chat_message_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_toRightOf="@id/chat_friends_avatar"
            android:layout_alignParentTop="true"
            android:layout_marginRight="35dp"
            android:maxLines="10"
            android:textSize="12dp"/>
    
        <TextView
            android:id="@+id/chat_friend_name"
            android:layout_width="140dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="3dp"
            style="@style/SubText"
            android:layout_toRightOf="@id/chat_friends_avatar"      
            android:layout_below="@id/chat_message_text" />
    
        <TextView
            android:id="@+id/chat_message_time"
            android:layout_width="80dp"
            android:layout_height="wrap_content"
            android:layout_marginRight="8dp"
            android:layout_marginTop="3dp"
            style="@style/SubText"
            android:layout_alignParentRight="true"
            android:layout_below="@id/chat_message_text" />
    
    </RelativeLayout>   
    

    However, when I embed a list of such elements in a ScrollView, in between some other elements, the rows are not fully displayed, they are clipped (see image below) if the text is wrapped. The ListView is instantiated as follows in the ScrollView:

    <ListView
    android:id="@+id/info_chat_listview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="center"
    android:cacheColorHint="@color/frame_background_color"
    android:clickable="false"
    android:divider="@null"
    android:footerDividersEnabled="false"
    android:focusable="false" >
    </ListView> 
    

    If the height of the ListView is set to "wrap_content" only the first element is shown. That's why I'm using a method to calculate the height of the rows of the list:

    private int getCommentsListHeight() {
            if (mChatAdapter != null && mChatAdapter.getCount() != 0) {
                if (mChatList != null) {// && mCommentsListItemHeight == 0) {
                    mCommentsListItemHeight = 0;
                    for (int i = 0; i < mChatAdapter.getCount(); i++) {
                        // Get view item height
                        View viewItem = mChatAdapter
                                .getView(i, new View(OnAirActivity.this), mChatList);
                        viewItem.measure(0, 0);
                        Logger.d(LOGTAG, "View " + i + " measured height = " + viewItem.getMeasuredHeight());
                        mCommentsListItemHeight += viewItem.getMeasuredHeight();
                    }
                }
                //return mChatAdapter.getCount() * mCommentsListItemHeight;
                return mCommentsListItemHeight;
            } else {
                return 0;
            }
        }
    

    Unfortunately, in case when the text inside the TextView is wrapped, even over several lines, the height of the row element returned by the getMeasuredHeight() method is constant. Also the getLineCount() called on the TextView inside the row element returns 1 even if the text is wrapped.

    On the other hand, if this ListView is embedded in a LinearLayout, everything works fine and the full list is displayed with no clipping.

    Do you have any suggestions as to what might be wrong here? I really don't like the idea of manually measuring the height of the list elements and it apparently doesn't work but why can't android nicely stretch the ListView inside the ScrollView to fit it all in there?

    Clipped list: see image

  • PawelPredki
    PawelPredki about 12 years
    Thanks for a quick answer. So your suggestion would be, for example, to instantiate those row views programatically inside the Scrollview one by one?
  • waqaslam
    waqaslam about 12 years
    No. You should use ListView but dont put it inside ScrollView. You may customize row items by overriding getView method from adapter used for the ListView
  • PawelPredki
    PawelPredki about 12 years
    I am using an adapter and this works fine in this other activity I mentioned where the ListView is within a LinearLayout. I have to use ScrollView here as my Activity contains many elements that don't fit on the screen, including the chat ListView that's the source of all the problems...
  • waqaslam
    waqaslam about 12 years
    then perhaps i would suggest you to use ListView's headers and/or footers to set all other views which you have at the moment inside ScrollView.
  • PawelPredki
    PawelPredki about 12 years
    This is a bit of an overkill since the majority of the elements in the currently used ScrollView are NOT the list that is troublesome... I understand there is no way to make the ListView work fine inside the ScrollView so I need to think about which workaround/solution is the best in my case. Thanks for the help!
  • PawelPredki
    PawelPredki about 12 years
    So you're suggesting putting the layouts in the following order: <ScrollView> <LinearLayout> <... my other views ...> <LinearLayout> <ProblematicListView> </LinearLayout> <... more views ...> </LinearLayout> </ScrollView> Isn't this pretty much the same? The ListView is still inside the ScrollView? Right now I went for the 'manual' way by doing <include android:id="@+id/chat_comment_x" layout="mylayout">, x=0..5 And I have a placeholder array for those five Layouts which I fill out using the same adapter I used for my list view. In effect I have a dynamic 5-element list and it works fine.
  • Layko Andrey
    Layko Andrey about 12 years
    No, I suggest you do the following: <ScrollView> <LinearLayout> <... my other views ...> <LinearLayout></LinearLayout> </ScrollView>. The main idea is to put you list items to linear layout, not in special listview layout. All items would be scrolled and shown without clipping.
  • PawelPredki
    PawelPredki about 12 years
    This would be a good solution if the only elements in my ScrollView were the comments view. And in that case putting a ListView inside a LinearLayout would be the best solution. In my case, there are many more elements inside the LinearLayout inside the ScrollView so I would first need to know where to put the comments. I guess both approaches are fine.
  • Layko Andrey
    Layko Andrey about 12 years
    No problems to add any element into ScrollView after and before LinearLayout. I just cut some code to show exact you need. In real file i have many controls before LinearLaout and after including one more LinearLayout which is also used as ListView.
  • PawelPredki
    PawelPredki about 12 years
    If you have other child elements inside the ScrollView except for the LinearLayout then this is against what Android tells you to do here: developer.android.com/reference/android/widget/ScrollView.ht‌​ml "A ScrollView is a FrameLayout, meaning you should place one child in it containing the entire contents to scroll; this child may itself be a layout manager with a complex hierarchy of objects"
  • Layko Andrey
    Layko Andrey about 12 years
    Add top layout, LinearLayout for example, as ScrollView child and add to it child layout any other controls including LinearLayouts that would be used as list container. I do the same thing, it works properly.
  • Keith Entzeroth
    Keith Entzeroth over 10 years
    This works for me but I don't have the issue of wrapping text like the original question was aksing about. Thanks!
  • cnfw
    cnfw almost 10 years
    This works fine, but then when I added padding around the listview to take the text off the edge of the screen, a small bit gets clipped again. What should I add to fix this?
  • Rajesh Wadhwa
    Rajesh Wadhwa almost 10 years
    params.height is the final height that is set.you can add a fixed buffer in case u have to keep padding
  • CoDe
    CoDe over 9 years
    what if required ExpandableListView instead ListView ?
  • Krunal Shah
    Krunal Shah over 9 years
    How to implement load more data?
  • adamdport
    adamdport about 9 years
    This answer is copied almost verbatim from here: stackoverflow.com/a/3495908/1221537
  • rekire
    rekire almost 8 years
    This is a bad solution! This will create many useless views. The idea itself is fine but not that way.
  • james
    james almost 8 years
    It can show listview item, but footer view cannot able to display when i added with Listview.addfooter(View).Actually footer layout is exist. How to make able to see footerview?
  • rekire
    rekire over 5 years
    @Denny each call of listAdapter.getView(i, null, listView) will create a new view and just for the measurement. That is very bad for the performance.
  • Denny
    Denny over 5 years
    Is that really creating new views or just getting what's already there? Seems to me the latter