Android ListView rows in ScrollView not fully displayed - clipped - android

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:

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

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 :)

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

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!

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.

I can see the ListView is inside a ViewPager; one other simple approach to resolving this issue is to add
app:layout_behavior="#string/appbar_scrolling_view_behavior" to the ViewPager in your layout xml as seen below.
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
To prevent the same behavior at the bottom of the list, you can also add android:layout_marginBottom="?attr/actionBarSize" to the ViewPager like so
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:layout_marginBottom="?attr/actionBarSize"/>
This is coming late, but I hope it helps any other person.

try it..
after create all view add bellow line for ScrollView location on screen (x,y)
ScrollView scrollView = (ScrollView)findViewById(R.id.scrollView);
scrollView.smoothScrollTo(0,0);// top location zero index

I had a similar problem. I have a
RelativeLayout
listView
includeLayout
where I include some bottom nav beneath the listView with this
<include
android:id="#+id/includeLayout"
layout="#layout/bottom_nav_bar"
and my listView was clipped - not taking the full height available between the header and bottom nav. I tried various xml settings suggested in this and other threads, but what worked for me was to add
android:layout_alignBottom="#+id/includeLayout"
to my listView. That seemed to pull the listView down to the top of the bottom nav, so that the listView is now using the full available height (and it scrolls as needed).

This works for me
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="#dimen/activity_vertical_margin">
<TextView
android:id="#+id/tv_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="17sp"
android:text="#string/text_list_devices" />
<ListView
android:id="#+id/lv_paired"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/activity_vertical_margin"
android:cacheColorHint="#00000000"
android:layout_above="#+id/signup_t"
android:layout_below="#id/tv_status"
/>
<Button
android:id="#+id/signup_t"
style="?android:textAppearanceSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textColor="#android:color/white"
android:layout_alignParentBottom="true"
android:text="Print All Records"
android:typeface="sans"
android:layout_marginLeft="45dp"
android:layout_marginRight="45dp"
android:textSize="24sp"
android:background="#drawable/selector_for_button"
android:textStyle="bold" />
</RelativeLayout>

This is BAD practice. But there are some situations we can not avoid using that. For example dynamic e-commerce layouts we may put multiple lists or recycle views but you don't want to scroll inside a single item height (if accidentally wanted!!). I faced this kind of problem. I fixed using a simple way. I don't tell this is the correct way but it may help some.
!! I used to recycle the view.
(01) Create an Interface to return view height.
public interface AfterViewLoadListener {
void onViewHeightMeasured(int height, String mode);
}
(02) implement with your activity
public class *Activity extends AppCompatActivity implements AfterViewLoadListener{
/** your codes **/
final SimpleListRecycleAdapter order_adapter = new SimpleListRecycleAdapter(this,"ORDER");
}
#Override
public void onViewHeightMeasured(int height, String mode) {
if(mode.equals("ORDER") && height > 0){
recycleView.setMinimumHeight(height);
}
}
(03) inside the recycle view custom adapter
AfterViewLoadListener viewLoadListener = null;
public SimpleListRecycleAdapter(AfterViewLoadListener listener, String mode) {
if(listener instanceof AfterViewLoadListener){
viewLoadListener = listener;
}
this.mode = mode;
}
(04) override the onViewAttachedToWindow method
#Override
public void onViewAttachedToWindow(#NonNull SimpleListViewHolder holder) {
super.onViewAttachedToWindow(holder);
View view = holder.itemView;
view.measure(0, 0);
this.viewMinHeight = view.getMeasuredHeight();
if(!firstFlag){
firstFlag = true;
viewLoadListener.onViewHeightMeasured(this.viewMinHeight*filtered.length(),mode);
}
}
(05) That's it. It worked for me.

Related

Issues with ScrollView and setting layouts

Here I'm new to android development. Don't mind if something isn't clear.
I did enough research on this issue, before marking this as duplicate please update the solution.
Here I'm adding an image with arrangement of layouts what I want to implement here.
Here the listView is being shown as only 1 item. If I scroll, all the items will be scrolled in the shown in this height.
Here my xml structure is
<LinearLayout>(parent)
<ScrollView>
<LinearLayout>
<LinearLayout>
weight: __
-----------
</LinearLayout>
<ListView>
h:match_parent
w:wrap_content
weight:__
</ListView>
<LinearLayout>
weight:__
--------------
</LinearLayout>
</LinearLayout>
</ScrollView>
</LinearLayout>
I'm getting numbers of data sets from DB and have to show those sets in middle of the both LinearLayouts. For now I'm using ListView but seems impossible to use here as my expectation.
Please suggest whether I can I perform in ListView here or how can I use numbers of LinearLayout blocks for each dataset?
Update
This is what I expect as shown in the pic.
Try this
<LinearLayout>(parent)
<NestedScrollView>
<LinearLayout>
<LinearLayout>
w:wrap_content
h:wrap_content
</LinearLayout>
<ListView>
w:match_p__arent
h:match_p__arent
weight:1
NestedScroll:true
</ListView>
<LinearLayout>
w:match_p__arent
h:match_p__arent
weight:1
</LinearLayout>
</LinearLayout>
</NestedScrollView>
</LinearLayout>
You need this method for your ListView
public static void setListViewHeightBasedOnItems(ListView target_Listview, int limit) // LIMIT 0 FOR SHOWING ALLL CONTENTS
{
if (limit == 0) {
ListAdapter listAdapter = target_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++) {
// if(itemPos < 4)
// {
View item = listAdapter.getView(itemPos, null, target_Listview);
item.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
Log.e("" + itemPos, "" + item.getMeasuredHeight());
totalItemsHeight += item.getMeasuredHeight();
// }
}
// Get total height of all item dividers.
int totalDividersHeight = target_Listview.getDividerHeight() * (numberOfItems - 1);
// Set list height.
ViewGroup.LayoutParams params = target_Listview.getLayoutParams();
params.height = totalItemsHeight + totalDividersHeight;
target_Listview.setLayoutParams(params);
target_Listview.requestLayout();
// return true;
}
else {
}
}
else {
ListAdapter listAdapter = target_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++) {
if (itemPos < limit) {
View item = listAdapter.getView(itemPos, null, target_Listview);
item.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
Log.e("" + itemPos, "" + item.getMeasuredHeight());
totalItemsHeight += item.getMeasuredHeight();
}
}
// Get total height of all item dividers.
int totalDividersHeight = target_Listview.getDividerHeight() * (numberOfItems - 1);
// Set list height.
ViewGroup.LayoutParams params = target_Listview.getLayoutParams();
params.height = totalItemsHeight + totalDividersHeight;
target_Listview.setLayoutParams(params);
target_Listview.requestLayout();
}
}
}
After setting adapter on ListView Just call the method and Pass your ListView in the parameters with your list size as second parameter.
One Suggestion from my side : You should use RecyclerView Instead of listview . Just need to write one line for that after setting up everything(adapter etc)
recyclerView.HasFixedSize();
Hope my answer will help.
Might need more clarification on what exactly it is you're trying to accomplish.
If the ListView is supposed to be longer and to scroll through more items on the list, I might recommend using the RecyclerView. Its a little bit more setup, but allows for a lot more complex layouts to be created based off the concept.
There are a lot of great references to learn the ways of RecyclerView if you want to search further:
this one might help you setup the solution to your problem
How to create RecyclerView with multiple view type?
Android Developer website tutorial for RecyclerView
https://developer.android.com/guide/topics/ui/layout/recyclerview.html
Post from Code Path that helped me supplement all of it together.
https://guides.codepath.com/android/using-the-recyclerview
try this way
<LinearLayout>
<LinearLayout>
<LinearLayout>
weight: __
-----------
</LinearLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ListView>
h:match_parent
w:wrap_content
weight:__
</ListView>
</ScrollView>
<LinearLayout>
weight:__
--------------
</LinearLayout>
</LinearLayout>
I have tried in my Android studio and mine is working i am able to scroll view .mine is little bit deffrent then you.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorAccent">
<LinearLayout
android:id="#+id/first"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#color/colorAccent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:text="#string/hello" />
</LinearLayout>
<ListView
android:id="#+id/listview"
android:layout_width="match_parent"
android:layout_height="250dp"
android:background="#color/black_overlay"
android:layout_below="#+id/first" />
<LinearLayout
android:id="#+id/second"
android:layout_width="match_parent"
android:layout_height="550dp"
android:background="#color/colorAccent"
android:layout_below="#+id/listview"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:text="#string/hello" />
</LinearLayout>
</RelativeLayout>
</ScrollView>

Height of a listview

I have been trying to make a layout for my app, where I set the android:layout_height="wrap_content".
This is my code ::
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.hp.money.DashBoard">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.CardView
android:id="#+id/cv_amount_display"
android:layout_width="match_parent"
android:layout_height="200dp">
.....
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
card_view:cardCornerRadius="10dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/last_10_transaction" />
<ListView
android:id="#+id/last_transactions_lv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
</ScrollView>
I am using a ListView inside a ScrollView, which is a bad idea, I know! It leads to some scrolling issues, I believe. But I have to dynamically update data on my screen during runtime, and I think only the listview can handle it. If there's any other View to do it, please suggest.
Now, the problem is that even if the Data source of the ListView has 10 items, the height of the ListView always remains equal to the size of one listView element, but the height of the listview is set to wrap_content, so probably it is supposed to resize accordingly. But it doesn't! How to fix this?
This is how it looks, even though the Listview has 10 items!
The ListView is the one which has a heading as LAST 10 TRANSACTIONS
Why I used a ScrollView??
I put a scrollview, because, the number of enteries in the listview can be many, so I want that when the user scrolls to see the entries not currently visible int the listview, the whole page gets scrolled, and not only the listview.
What do you mean by " dynamically insert data into the Listview during runtime". If you are modifying list data dynamically then ListView can handle it internally. You no need to do anything in that case. So you can remove the ScrollView so it works fine.
You can not add a ListView inside ScrollView .
For considering your needs you can set ListView height dynamically to fulfill your needs ...
After setting up your list adapter simply call this 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);
}
As others pointed, you can't put a ListView inside a ScrollView (anyways it doesn't make sense, ListView has its own scroll)
But I have to dynamically insert data into the Listview during runtime, if there's any other way to do it, please suggest.
I can't see why you think you can't, and want to know why you think a ScrollView will do it.
If you have created your own adapter to fill that ListView you should have something like this:
class listViewAdapter extends BaseAdapter
{
public View getView(int position, View convertView, ViewGroup parent)
{
}
public int getCount()
{
}
public Object getItem(int position) {
}
public long getItemId(int position){
}
}
Then you should create a data field and a setter
private Arraylist<DataType> data;
public void setData(Arraylist<DataType> data)
{
data = data;
}
Then you can use it like this:
ListView lv = findViewById...
ListViewAdapter adapter = new ListViewAdapter();
adapter.setData(data);
lv.setAdapter(new ListViewAdapter());
When you have added or deleted data you can reload it by calling
adapter.notifySetDataChanged();

ScrollView including ListView in Android

I would like to have a ScrollView that contains a ListView, which is obviously undesirable since a ListView has its own scroll bar.
Essentially I would like to have a RelativeLayout with a number of views forming the header of the page, followed by a ListView, similar to Facebook's profile page with the images followed by the feed.
If I simply put a ListView inside a RelativeLayout then only the ListView area is scrollable instead of the whole page.
Is there a way to do this without including the header (RelativeLayout) as the first element in the ListView?
It looks like there is a method on ListView called addHeaderView. I can use this to add the RelativeLayout at the top. Thanks for your input.
Actually I haven't done this myself, but I kinda know the trick to achieve your goal and give a sexy look to your layout.
First get rid of that ScrollView.
Then put a monitor(listener) on your ListView to detect how much your ListView has scrolled and consider a reasonable threshold to detect *.
Now what is * ? it is the time you should "Hide" or "slowly fade" your header items. when user scrolls down the ListView and reaches that threshold slowly fade or hide the header. Then again when user scrolls up use a threshold to decide when to make your header items visible.
As far as I know this is the idea. Also try checking github and android-arsenal I guess there should be a library for this that will ease your work.
Use this class
public class Helper {
public static void getListViewSize(ListView myListView) {
ListAdapter myListAdapter = myListView.getAdapter();
if (myListAdapter == null) {
// do nothing return null
return;
}
//set listAdapter in loop for getting final size
int totalHeight = 0;
for (int size = 0; size < myListAdapter.getCount(); size++) {
View listItem = myListAdapter.getView(size, null, myListView);
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
//setting listview item in adapter
ViewGroup.LayoutParams params = myListView.getLayoutParams();
params.height = totalHeight + (myListView.getDividerHeight() * (myListAdapter.getCount() - 1));
myListView.setLayoutParams(params);
// print height of adapter on log
Log.i("height of listItem:", String.valueOf(totalHeight));
}
}
and in the activity class, use,
Helper.getListViewSize(ur listview name);
Try this Code.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp" >
<LinearLayout
android:id="#+id/headerLayout"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="TextView" />
<TextView
android:id="#+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="TextView" />
</LinearLayout>
<ListView
android:id="#+id/listView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/headerLayout" >
</ListView>
</RelativeLayout>

ScrollView > LinearLayout > PreferenceFragment is zero height

I have an activity that has a ScrollView with a vertical LinearLayout that has two fragments that are PreferenceFragment instances as shown in the following layout file:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_height="wrap_content"
android:layout_width="match_parent"
>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context="com.foo.app.SettingsActivity">
<fragment
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:name="com.foo.app.SettingsFragment"
android:id="#+id/fragment_settings"/>
<fragment
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:name="com.foo.app.NotificationSettingsFragment"
android:id="#+id/fragment_notification_settings"/>
</LinearLayout>
The problem is that the two fragments show up with just their PreferenceCategory title and the actual fragment UI is zero height and not visible. Oddly, it is possible to scroll each fragment individually and see the missing fragment UI. It is as if each Fragment is inside a ScrollView.
What I expected was for the two fragments to be sized to wrap their content and there be a single vertical slider to scroll the LinearLayout containing both fragments.
In case it is relevant, the two fragments extends android.preference.PreferenceFragment and do not define their layout in a layout file. Instead they load their Preference UI as follows:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.pref_notification);
}
TIA for your help.
Remove the outer ScrollView. I had the same problem and that solved it for me.
I haven't found it in the documentation yet, but apparently the PreferenceFragment supplies its own ScrollView around the PreferenceScreen. Somehow that leads to a wrap_content height just large enough to show the first element (e.g. a category header).
The question is quite old but in case someone else stumbles upon this, I've managed to find a solution. Just like #Ewoks mentioned, I had to settle for a fixed height as well (which didn't always play well with varying dpi of the devices).
But with the help of this code I managed to make the height dynamic, which wraps the content nicely.
In your PreferenceFragment class do as follows:
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getView() != null) {
ListView listView = (ListView) getView().findViewById(android.R.id.list);
Adapter adapter = listView.getAdapter();
if (adapter != null) {
int height = 0;
//int height = listView.getPaddingTop() + listView.getPaddingBottom();
for (int i = 0; i < adapter.getCount(); i++) {
View item = adapter.getView(i, null, listView);
item.measure(0, 0);
height += item.getMeasuredHeight();
}
FrameLayout frame = (FrameLayout) getActivity().findViewById(R.id.content_frame); //Modify this for your fragment
ViewGroup.LayoutParams param = frame.getLayoutParams();
param.height = height + (listView.getDividerHeight() * adapter.getCount());
frame.setLayoutParams(param);
}
}
}
Hope this helps someone!
Given that you can't remove the outer ScrollView, what worked for me is to change it to be a android.support.v4.widget.NestedScrollView and then in Activity's onCreate to run:
findViewById(R.id.nested_scroll_view).setNestedScrollingEnabled(false);
Try setting the the android:layout_weight in each fragment to roughly the size of each settings list. E.g.
<fragment
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.3"
android:name="com.foo.app.SettingsFragment"
android:id="#+id/fragment_settings"/>
<fragment
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.7"
android:name="com.foo.app.NotificationSettingsFragment"
android:id="#+id/fragment_notification_settings"/>

Android Why the Horizontal scroll does not fit the whole width

This a ListView screenshot of my problem:
This is the layout XML:
<LinearLayout
android:id="#+id/viewer_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/background_dark"
android:orientation="vertical" >
<EditText
android:id="#+id/viewer_filter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableRight="#android:drawable/ic_menu_search"
android:hint="#string/hint_filter"
android:background="#android:color/white"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="3dp"
android:inputType="text"
android:paddingLeft="4dp"
android:selectAllOnFocus="true" >
</EditText>
<EditText
android:id="#+id/viewer_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableRight="#android:drawable/ic_menu_search"
android:hint="#string/hint_search"
android:background="#android:color/white"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_marginTop="3dp"
android:layout_marginBottom="5dp"
android:inputType="text"
android:paddingLeft="4dp"
android:selectAllOnFocus="true" >
</EditText>
</LinearLayout>
<HorizontalScrollView
android:id="#+id/viewer_hscroll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/viewer_top" >
<ListView
android:id="#+id/viewer_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</ListView>
</HorizontalScrollView>
There are 3 problems in this scenario:
The Horizontal scrollview does not cover the full screen width (I drew a thick red line to mark the end)
The Horizontal scrollview does not scroll horizontally
The ListView rows are not of uniform width (this can be seen by the background color ending) (see the getView code below for details)
private static final int listRowLayout = android.R.layout.activity_list_item;
private Map<String, Integer> mColors = new HashMap<String, Integer>();
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// No logs here to keep ListView performance good
ViewHolder holder;
int color;
if( convertView == null ) {
convertView = mInflater.inflate(listRowLayout, parent, false);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(android.R.id.text1);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
String data = mData.get(position);
// A compiled regex is faster than String.Contains()
Matcher m = ViewHolder.regex.matcher(data);
if( m.find() ) {
color = mColors.get(m.group(1));
} else {
color = mColors.get("V");
}
holder.text.setText(data);
holder.text.setBackgroundColor(color);
return convertView;
}
private static class ViewHolder {
TextView text;
static Pattern regex = Pattern.compile(" ([VIDWEF])/");
}
}
I encountered the exact same issue in trying to display a log file. I have a dedicated activity to display the log file:
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_log);
// Read in lines from the log file
File clientLogFile = new File(LOG_FILE);
ArrayList<String> lines = new ArrayList<String>();
try
{
Scanner scanner = new Scanner((Readable) new BufferedReader(new FileReader(clientLogFile)));
try
{
while(scanner.hasNextLine())
{
lines.add(scanner.nextLine());
}
}
finally
{
scanner.close();
}
}
catch (FileNotFoundException e)
{
lines.add("No log file");
}
// Create a simple adaptor that wraps the lines for the ListView
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list_item,lines);
// Create a ListView dynamically to overide onMeasure()
final ListView listView = new ListView(this)
{
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
// Override onMeasure so we can set the width of the view to the widest line in the log file
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// Find maximum width of item in list and set scroll width equal to that
int maxWidth = 0;
for(int i=0; i<getAdapter().getCount(); i++)
{
View listItem = getAdapter().getView(i, null, this);
listItem.measure(0, 0);
int width = listItem.getMeasuredWidth();
if(width > maxWidth)
{
maxWidth = width;
}
}
// Set width of measured dimension
setMeasuredDimension(maxWidth, getMeasuredHeight());
}
};
// Add to scroll view
HorizontalScrollView horizontalScrollView = (HorizontalScrollView)findViewById(R.id.logScrollView);
horizontalScrollView.addView(listView);
// Set adaptor
listView.setAdapter(adapter);
// Enable fast scroll
listView.setFastScrollEnabled(true);
// Scroll to end
listView.post(new Runnable(){
public void run() {
listView.setSelection(listView.getCount() - 1);
}});
}
The onCreate method reads the log file and then dynamically adds a ListView to a HorizontalScrollView with onMeasure() overridden. The onMeasure() code determines the maximum width of the views in the adaptor and sets the ListView width to be that.
My activity_view_log.xml layout file is therefore very simple:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="5dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:paddingTop="5dp"
>
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/logScrollView">
</HorizontalScrollView>
</RelativeLayout>
In order to have finer grained control of the lines in the ListView I give my adapter my own layout file in list_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#android:id/text1"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:inputType="text|none"
/>
At the end of by onCreate() I enable fast scroll and also scroll to the end of the lines in the log file.
I would probably reverse what you are doing. Create a ListView and make each item in the listview horizontally scrollable. This way items only scroll when they need to, and it does not scroll the entire screen. And you get complete control over the dimensions of each list item. To do this use a custom listview adapter as mentioned in the comments. There is also a possible duplicate of your question here: Android horizontal scroll list
In order to solve the 3 problems I had to make all the components (the horizontal scroll view, the list view and it's items) have a "fill_parent" width (I think it's the same as "match_parent"). In addition I had the listview's onMeasure(...) overridden to calculate the max width of it's items and set it via setMeasuredDimension(...). This will measure the view by it's widest item, not by it's first, as it is implemented now.
This is the solution I found.
The root of all evil :-) is that ListView is not designed to efficiently deal with rows of different length. To determine the ListView width, instead of looking at all the rows, only 3 rows are taken as average.
So, if the 3 rows are by chance short rows, the width will be clipped for the longer rows, it explains the problems I experienced.
To bypass this I calculated the maximum row length for all data, and I padded shorter rows with spaces, it solved all 3 problems I described in the question.
The code for padding (executed inside getView() )
holder.text.setText(String.format("%1$-" + mLen + "s", data));

Categories

Resources