How to scroll to top of long ScrollView layout?
Asked Answered
K

18

180

For part of my app, the user is presented with a list of names and is asked to group them as they see fit.

(Note, the ListView code was copied verbatim from the Android Views tutorial. I haven't yet customized it to my needs, I'm just trying to figure out how to make this work.)

The basic layout is a LinearLayout, containing a ScrollView (called "groupsScrollView" in the code below), containing a RelativeLayout. I have some buttons and text, and then my ListView beneath that, which displays the list of names. All this is taller than the visible screen area, so the user is allowed to scroll vertically to see it all.

This all works beautifully, except when the page loads it is always pre-scrolled to the top of my ListView - in the middle of the page. The text and buttons I've created that tell the user what to do are not visible.

I can grab the screen and scroll up, that works just fine, but I'd like the screen to load having already been scrolled to the top. The user shouldn't have to scroll UP to see the top of a freshly loaded page. But everything I've tried to programmatically scroll to the top of the screen has failed.

Here's my onCreate method:

protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.groups);

    mainScrollView = (ScrollView)findViewById(R.id.groupsScrollView);

    //get the Bundle out of the Intent...
    Bundle extras = getIntent().getExtras();
    mNames = extras.getStringArray("mNames");

    setListAdapter(new ArrayAdapter<String>(this, R.layout.list_item, mNames));

    ListView lv = getListView();
    lv.setTextFilterEnabled(true);

    //This is the line I'm having issues with
    mainScrollView.pageScroll(View.FOCUS_UP);

    lv.setOnItemClickListener(new OnItemClickListener() {
        public void onItemClick(AdapterView<?> parent, View view,
            int position, long id) {
          // When clicked, show a toast with the TextView text
          Toast.makeText(getApplicationContext(), ((TextView) view).getText(),
              Toast.LENGTH_SHORT).show();
        }
      });
}

The line "mainScrollView.pageScroll(View.FOCUS_UP); is the problem. It doesn't cause an error, but it doesn't seem to do anything. I've tried scrollTo(0,0), scrollDirection(View.FOCUS_UP), and everything else I can think of.

Since I don't get an error I have to assume that these scroll commands are actually working, but that they're scrolling to the top of the ListView, rather than the top of the ScrollView or RelativeLayout that contains it. This seems to be confirmed by Google's own description of the scrollTo(int x, int y) method where they say "This version also clamps the scrolling to the bounds of our child.".

So to make a long question even longer, how do I load a bunch of views on the screen contained within a ScrollView, and then programmatically scroll the whole thing to the top of the page so the user can interact with it?

Thanks!

Kris answered 7/11, 2010 at 20:4 Comment(0)
F
289

Try

mainScrollView.fullScroll(ScrollView.FOCUS_UP);

it should work.

Filomenafiloplume answered 23/1, 2011 at 13:15 Comment(2)
Works For me Also . :DOccasionally
I found ScrollView#fullScroll() change current focus. I tested with EditText.Deductible
E
183

Had the same issue, probably some kind of bug.

Even the fullScroll(ScrollView.FOCUS_UP) from the other answer didn't work.
Only thing that worked for me was calling scroll_view.smoothScrollTo(0,0) right after the dialog is shown.

Euonymus answered 19/5, 2012 at 20:10 Comment(6)
This worked initially, but later when I tried to do it again and again, this approach did not give satisfactory result.Didst
i solve from scroll_view.smoothScrollTo(0,0), when we have listview in side the scrollview at that time fullScroll(ScrollView.FOCUS_UP) will not work.Nematic
scroll_view.smoothScrollTo(0,0) worked better for me than mainScrollView.fullScroll(ScrollView.FOCUS_UP); THANKS!Static
On most devices fullScroll(ScrollView.FOCUS_UP) worked, but on the Nexus 5 and devices where I had the text size turned way up - the fullSscroll moved under the actionbar, smoothScrollTo(0,0) worked better across all devices.Paramatta
This also has the advantage over fullScroll() in that it keeps the selected item still selected after the move. With fullScroll(), the first item in the scrollview was reselected each time in my case, independent of the selected item before the scroll moved upwards.Ryswick
if you don't want to use scroll_view.smoothScrollTo(0,0) , try scroll_view.scrollTo(0,0) , that works tooSarnoff
D
100

I had the same problem and this fixed it.

listView.setFocusable(false);
Decahedron answered 11/7, 2014 at 18:12 Comment(5)
This is the correct solution for gridviews also. It happens when you have more than one view elements in the same view group, especially another listview on top of the list or gridview. Since adapter notifies the UI thread, view is recreated and scrolls up to the first element. Just call listView.setFocusable(false) / gridView.setFocusable(false). If you do it from xml file, it will not work though.Codee
This solution worked in my case also, I had RecyclerView inside NestedScrollView so it was auto scrolling down. Thanks, man!Sphalerite
I had to add this before setting the Adapter but after that works like a charm. ThanksPintsize
perfect explanation!!Rima
Thank you both Miguel and especially omersem for the explanation, especially the part about how it wont work if set in XML, this saved me some headacheAlleenallegation
N
59

scrollViewObject.fullScroll(ScrollView.FOCUS_UP) this works fine, but only the problem with this line is that, when data is populating in scrollViewObject, has been called immediately. You have to wait for some milliseconds until data is populated. Try this code:

scrollViewObject.postDelayed(new Runnable() {
    @Override
    public void run() {
        scroll.fullScroll(ScrollView.FOCUS_UP);
    }
}, 600);

OR

 scrollViewObject.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        scrollViewObject.getViewTreeObserver().removeOnGlobalLayoutListener(this);
        scrollViewObject.fullScroll(View.FOCUS_UP);
    }
});
Nippur answered 17/5, 2013 at 6:21 Comment(5)
I was dealing with the same issue where the scrollview was supposed to be scrolling to the bottom of the page, fixed it right up!Dualpurpose
Instead of guessing the "magic" x number of milliseconds you'd have to wait, View objects offer a post(Runnable) method you could use that will post the runnable once the widget renders on the UI.Chartography
@Ryan : post(Runnable) always doesn't work for me on Nexus 5 (Genymotion)Abduction
post(Runnable) may not work because if you fetch datas after view infloation step, as first scroll up works and then datas populate to the view. So you either you should use postDelayed(Runnable,dummyMillis) or you should use scroll.fullScroll(ScrollView.FOCUS_UP) after data populated processes.Banker
Your first solution in which you give static millis is a bad approach.Diannediannne
T
36

The main problem to all these valid solutions is that you are trying to move up the scroll when it isn't drawn on screen yet.

You need to wait until scroll view is on screen, then move it to up with any of these solutions. This is better solution than a postdelay, because you don't mind about the delay.

    // Wait until my scrollView is ready
    scrollView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            // Ready, move up
            scrollView.fullScroll(View.FOCUS_UP);
        }
    });
Trappist answered 30/10, 2013 at 8:35 Comment(1)
Nice solution - Don't forget to remove the Global Listener at the end of the onGlobalLayout method calling scrollView.getViewTreeObserver().removeOnGlobalLayoutListener(this);Fingerling
P
30

Very easy

    ScrollView scroll = (ScrollView) findViewById(R.id.addresses_scroll);
    scroll.setFocusableInTouchMode(true);
    scroll.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
Peso answered 28/1, 2016 at 20:42 Comment(5)
for me it didn't work (the 3rd line), the solution was : scroll.fullScroll(View.FOCUS_UP); (with including your first 2 lines)Palaeography
@medskill, Hi, did you write these in your xml file? I fount that this works only in onCreate().Cumae
@imknownJ.Kimu Works for me in xml fileRomeo
This worked for me. You need both the second and third line. It would not work in the XML for me.Awhirl
I would kiss you if I could! Spent 1-2 hours on this. The best one when waiting for two listviews to load and didn't want focus on them. Added it to onResume().Cheater
B
25

I faced Same Problem When i am using Scrollview inside View Flipper or Dialog that case scrollViewObject.fullScroll(ScrollView.FOCUS_UP) returns false so that case scrollViewObject.smoothScrollTo(0, 0) is Worked for me

Scroll Focus Top

Band answered 12/12, 2012 at 9:56 Comment(0)
F
22
 final ScrollView scrollview = (ScrollView)findViewById(R.id.selected_place_layout_scrollview);

         scrollview.post(new Runnable() { 
                public void run() { 
                    scrollview.scrollTo(0, 0); 
                } 
            }); 
Flapdoodle answered 2/7, 2014 at 6:56 Comment(0)
U
13

This is worked for me

scroll_view.smoothScrollTo(0,0); // scroll to top of screen
Unfamiliar answered 21/3, 2018 at 9:2 Comment(0)
R
11

I fixed my issue in Kotlin like this:

scrollview.isFocusableInTouchMode = true
scrollview.fullScroll(View.FOCUS_UP)
scrollview.smoothScrollTo(0,0)

You can also create an extension of this like:

ScrollView.moveToTop()
{
    this.isFocusableInTouchMode = true
    this.fullScroll(View.FOCUS_UP)
    this.smoothScrollTo(0,0)
}

and use it like:

scrollView.moveToTop()
Rational answered 13/2, 2020 at 9:10 Comment(0)
K
9
runOnUiThread( new Runnable(){
   @Override
   public void run(){
      mainScrollView.fullScroll(ScrollView.FOCUS_UP);
   }
}
Kandacekandahar answered 26/2, 2014 at 12:46 Comment(0)
H
6

This is force scroll for scrollview :

            scrollView.post(new Runnable() {
                @Override
                public void run() {
                    scrollView.scrollTo(0, 0);
                    scrollView.pageScroll(View.FOCUS_UP);
                    scrollView.smoothScrollTo(0,0);
                }
            });
Hereunto answered 19/9, 2017 at 6:37 Comment(0)
G
4
scrollView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        // Ready, move up
        scrollView.fullScroll(View.FOCUS_UP);
    }
});
Glendaglenden answered 27/1, 2018 at 17:19 Comment(0)
N
2

This solution also works for NestedScrollView

NestedScrollView nestedScrollView = view.findViewById(R.id.YourNestedScrollViewID);
nestedScrollView.fullScroll(ScrollView.FOCUS_UP);
Naturism answered 22/8, 2020 at 8:34 Comment(0)
M
0
@SuppressWarnings({ "deprecation", "unchecked" })
public void swipeTopToBottom(AppiumDriver<MobileElement> driver) 
                    throws InterruptedException {
       Dimension dimensions = driver.manage().window().getSize();
        Double screenHeightStart = dimensions.getHeight() * 0.30;
        int scrollStart = screenHeightStart.intValue();
        System.out.println("s="+scrollStart);
        Double screenHeightEnd = dimensions.getHeight()*0.90;
        int scrollEnd = screenHeightEnd.intValue();
        driver.swipe(0,scrollStart,0,scrollEnd,2000);
        CommonUtils.threadWait(driver, 3000);
    }
Mose answered 18/7, 2017 at 12:11 Comment(2)
this will run for sure,Mose
Please provide a brief explanation as to how this answer solves the problem.Elisaelisabet
A
0

A few years later, the only proper solution is a mix of this thread:

  • without a listener or a post delayed handler, simpler solutions will fail because the can try to scroll before the ScrollView is drawn on the screen like explained by @Pelanes
  • the most proposed answer scan_settings_sv.fullScroll(View.FOCUS_UP); will move view to the first focusable element, not necessarily the top of scroll view. So, if there is a header or some text, or disabled views greyed out, and and a TextView below them, the ScrollView will move up to the TextView and not ScrollView Header
  • Like @Nilhcem commented, we need to remove the listener at the end, else the ScrollView will keep moving up
  • I also implemented it with the global OnGlobalLayoutListener reference as method argument to fix compiler warnings

So, the final solution should implement view.smoothScrollTo(0,0);, combined with OnGlobalLayoutListener to ensure the view was properly drwan on screen:

final ScrollView my_view= dialog.findViewById(R.id.my_view_id);
scan_settings_sv.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        // Ready, move up
        //my_view.fullScroll(View.FOCUS_UP); //moves up to the to selectable/focusable element
        my_view.smoothScrollTo(0,0); // scrolls to top of view
        my_view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
    }
});

Note that it is very important to get the ScrollView just before invoking the method! Else, if the view height changed after the first final ScrollView my_view= dialog.findViewById(R.id.my_view_id); invocation, for example because a ListView below the ScrollView was expanded, the scroll will not properly reach top of the ScrollView

Annelid answered 22/7, 2022 at 11:2 Comment(0)
I
0

I found that if your ScrollView nested inside SwipeRefreshLayout, than you can do this:

swipeRefreshLayout.getChildAt(0).scrollTo(0,0);
Imbecility answered 17/2, 2023 at 12:30 Comment(0)
I
0

Becareful if you are using a RecyclerView

There is lot of chance the parent scroll view isn't used for scrolling as recycler view can scroll. By the way, just don't use scroll view in this case.

In the case you are using a recyclerView, it's very simpler:

adapter.submitList(data) {
    // Scroll back to top
    recyclerView.scrollToPosition(0)
}

Or if you want the scolling effect to be seen:

adapter.submitList(data) {
    // Scroll back to top
    recyclerView.smoothScrollToPosition(0)
}
Imaimage answered 24/7, 2023 at 9:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.