Android ListView rows in ScrollView not fully displayed - clipped
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.
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, 2020Comments
-
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:
-
PawelPredki about 12 yearsThanks for a quick answer. So your suggestion would be, for example, to instantiate those row views programatically inside the Scrollview one by one?
-
waqaslam about 12 yearsNo. 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 about 12 yearsI 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 about 12 yearsthen 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 about 12 yearsThis 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 about 12 yearsSo 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 about 12 yearsNo, 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 about 12 yearsThis 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 about 12 yearsNo 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 about 12 yearsIf 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.html "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 about 12 yearsAdd 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 over 10 yearsThis works for me but I don't have the issue of wrapping text like the original question was aksing about. Thanks!
-
cnfw almost 10 yearsThis 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 almost 10 yearsparams.height is the final height that is set.you can add a fixed buffer in case u have to keep padding
-
CoDe over 9 yearswhat if required ExpandableListView instead ListView ?
-
Krunal Shah over 9 yearsHow to implement load more data?
-
adamdport about 9 yearsThis answer is copied almost verbatim from here: stackoverflow.com/a/3495908/1221537
-
rekire almost 8 yearsThis is a bad solution! This will create many useless views. The idea itself is fine but not that way.
-
james almost 8 yearsIt 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 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 over 5 yearsIs that really creating new views or just getting what's already there? Seems to me the latter