arberb
arberb

Reputation: 960

ListView in Scroll View my scrollview moves to top of listview. How do I prevent this?

I have a listview in my scroll view underneath almost a page worth of scroll before that but once my listview gets populated the scrollview moves to the top of the list view. how can I fix this/prevent this from happening?

SCROLL VIEW XML:

<ScrollView
    android:id="@+id/tvscrollview"
    android:layout_marginTop="8.0dip" 
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <LinearLayout
        android:id="@+id/linearLayout1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >

        <include 
            layout="@layout/a" />

        <include 
            layout="@layout/b" />

        <include 
            layout="@layout/c" />

        <include 
            layout="@layout/d" />


<ListView
    android:id="@+id/listview"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content" />

    </LinearLayout>
</ScrollView>

I've tried doing sv.fullScroll(ScrollView.FOCUS_UP); and all that stuff to my scrollview but it doesnt work

Upvotes: 8

Views: 5746

Answers (13)

Gray
Gray

Reputation: 855

I tried the above answers, but neither of them worked. Below is my final solution. Just override the onFinishInflate method, then do a post to scrollTo(0, 0).

@Override
protected void onFinishInflate() {
    super.onFinishInflate();
        this.post(new Runnable() {
            @Override
            public void run() {
                scrollTo(0, 0);
            }
        });
}

Upvotes: 0

Bharat singh
Bharat singh

Reputation: 504

Just Do one thing before add items in your list

listview.setFocusable(false);

after that you can again do that

listview.setFocusable(true);

if needed it will work for sure

Upvotes: 2

Reaz Murshed
Reaz Murshed

Reputation: 24211

Though its a very late answer to this question and this answer has already an accepted answer, I thought putting how I solved my problem here might help others to find everything related to this problem in a single SO thread.

So here's my situation: I had a ScrollView with a ListView at the end of the layout. I know its a very bad practice to do so. I could achieve the same behaviour I wanted by attaching a header to the ListView. But anyway, my ListView got the focus when it was populated with the data and the the page was scrolled automatically to the bottom where the ListView started.

I tried with the accepted answer here, but it didn't work for me. I tried using a dummy EditText at the top of the layout so that it could request the focus automatically, but it didn't work either. Because the ListView was getting the focus after the data is loaded from a REST Api call.

Then I found this answer and this really helped. So I thought putting it in here so that others might get help from a single thread having the same problem like me.

mainScrollView.fullScroll(ScrollView.FOCUS_UP); didn't work for me either as the list was populated after the other views were populated. So I had to scroll the ScrollView to the top after the ListView is populated. So here's how I solved my problem.

mScrollView.setEnabled(false);
mIntroducerListView.setFocusable(false);
mListAdapter.notifyDataSetChanged();

// Force scroll to the top of the scroll view.
// Because, when the list view gets loaded it focuses the list view
// automatically at the bottom of this page.
mScrollView.smoothScrollTo(0, 0);

Upvotes: 0

Fahad Rehman
Fahad Rehman

Reputation: 1199

I had the same issue I found the solution by changing the visibility of the list view in XML after setting the adapter I change the visibility to visible

listView.setAdapter(adapter);
listView.setVisibility(View.VISIBLE);

Upvotes: 0

We should never put a ListView inside a ScrollView.

Work Around : When ScrollView moves up/down because of listview's notifyDataSetChanged(), Then try,

scrollview.setEnabled(false);
listview.setFocusable(false);
adapter.notifyDataSetChanged();
scrollview.setEnabled(true);

Upvotes: 11

Aerim
Aerim

Reputation: 2036

The only solution that worked for me was to add an EditText as the first child of the layout. Ugly, I know.

<EditText
  android:layout_width="0dp"
  android:layout_height="0dp" />

Upvotes: 0

Navdeep Singh
Navdeep Singh

Reputation: 689

You may want to achieve this by using following:

 <LinearLayout 
......>
<ScrollView
    android:id="@+id/tvscrollview"
    android:layout_marginTop="8.0dip" 
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <LinearLayout
        android:id="@+id/linearLayout1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >

        <include 
            layout="@layout/a" />

        <include 
            layout="@layout/b" />

        <include 
            layout="@layout/c" />

        <include 
            layout="@layout/d" />

    </LinearLayout>
</ScrollView>
<ListView
    android:id="@+id/listview"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content" />
 </LinearLayout>

Wrap your listview outside scrollview and else inside scrollview, because You should never use a ScrollView with a ListView, because ListView takes care of its own vertical scrolling. Most importantly, doing this defeats all of the important optimizations in ListView for dealing with large lists, since it effectively forces the ListView to display its entire list of items to fill up the infinite container supplied by ScrollView.

Source Android Docs

Upvotes: 1

A.R.
A.R.

Reputation: 2641

I have made 2 or 3 changes in your xml file

<ScrollView
    android:id="@+id/tvscrollview"
    android:layout_marginTop="8.0dip" 
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <LinearLayout
        android:id="@+id/linearLayout1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" 
        android:padding="10dp">  // This will let you see the scroll bar of list view
                                    when you scroll your list view.

        <include 
            layout="@layout/a" />
        <include 
            layout="@layout/b" />
        <include 
            layout="@layout/c" />
        <include 
            layout="@layout/d" />


    <ListView
    android:id="@+id/listview"
    android:layout_width="fill_parent"
    android:layout_height="300dp" />// Instead of wraping up your list, if you wish
                                       you can give certain height to your list view.

    </LinearLayout>
</ScrollView>

Another thing you need is write the given code in your java file.

   onCreate()
  {
   ....
   ScrollView sView=(ScrollView)findViewById(R.id.tvscrollview);

   yourListView.setOnTouchListener(new OnTouchListener()
    {
        public boolean onTouch(View arg0, MotionEvent arg1) 
        {
            if(arg1.getAction() == MotionEvent.ACTION_DOWN ||   arg1.getAction() == MotionEvent.ACTION_MOVE)
            {
                sView.requestDisallowInterceptTouchEvent(true);
            }
            return false;
        }
    });

I hope this helps you.

Upvotes: 1

Ahmed Zayed
Ahmed Zayed

Reputation: 2185

It's not possible to make a scrollable view inside a scrollable view. But as a work around this, and only in case that this listviews doesn't take much memory if all views are loaded. you can use this

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ListView;

public class NonScrollableListView extends ListView {
    public NonScrollableListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // Do not use the highest two bits of Integer.MAX_VALUE because they are
        // reserved for the MeasureSpec mode
        int heightSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, heightSpec);
        getLayoutParams().height = getMeasuredHeight();
    }

}

Again, it's not good to use this workaround

Upvotes: 2

Ayoub
Ayoub

Reputation: 341

I would wrap the list and the other layouts in a RelativeLayout instead of a LinearLayout

<ScrollView
    android:id="@+id/tvscrollview"
    android:layout_marginTop="8.0dip" 
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <RelativeLayout
        android:id="@+id/linearLayout1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <LinearLayout
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >

            <include 
                layout="@layout/a" />

            <include 
                layout="@layout/b" />

            <include 
                layout="@layout/c" />

            <include 
                layout="@layout/d" />

        </LinearLayout>

        <ListView
            android:id="@+id/listview"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/container" />

    </RelativeLayout>
</ScrollView>

This way the top border of the ListView is boun to the bottom border of the LinearLayout and will always stay under everything else.

You cant put the includes directly in the RelativeLayout! See here for more details.

Upvotes: 1

nKn
nKn

Reputation: 13761

Firstly, probably you've already heard about it, but just in case: You should never put a ListView inside a ScrollView, as ListView itself already has got a ScrollView and this design goes against the whole ListView idea. If you're convinced you have to use it, probably there's a better approach to use and you may need to simplify your code somehow.

Now, even if you still want to use something like that, you may use something like this:

package your.package.name;

import android.view.View;
import android.view.ViewGroup;
import android.widget.ListAdapter;
import android.widget.ListView;

public class ScrollingListView {
  public static void setListViewHeightBasedOnChildren(ListView listView) {
    ListAdapter listAdapter = listView.getAdapter(); 
    if (listAdapter == null)
      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);
  }
}

You simply set your adapter via .setAdapter() and afterwards call ScrollingListView.setListViewHeightBasedOnChildren(your_listview). This should redimension your window accordingly to your ListView height.

---- EDIT ----

That will be probably the ugliest workaround, but try doing the following:

ScrollView sv = (ScrollView) findViewById(R.id.your_scrollview);
sv.setEnabled(false);
// Populate your ListView
sv.setEnabled(true);

Upvotes: 9

headdetect
headdetect

Reputation: 21

add:

android:transcriptMode="disabled"

in the list you don't want to scroll

From Android Docs

Upvotes: 2

Manfred Moser
Manfred Moser

Reputation: 29912

Add all the stuff on top as header to the list view.

Now that I see the code.. you can only have one ViewGroup inside a scrollview. So you would warp the two layouts into another one, BUT a ListView automatically has a scroll view in it so that wont really work.

So what you have to do is use the addHeader view in your ListActivity (of fragment) and inflate LinearLayout1 in the activity from a different xml file.

Upvotes: 2

Related Questions