Jak przewinąć do góry długiego układu ScrollView?

162

W przypadku części mojej aplikacji użytkownikowi wyświetla się lista nazwisk i jest proszona o pogrupowanie ich według własnego uznania.

(Uwaga: kod ListView został dosłownie skopiowany z samouczka Android Views. Nie dostosowałem go jeszcze do moich potrzeb, po prostu próbuję dowiedzieć się, jak to działa).

Podstawowy układ to LinearLayout, zawierający ScrollView (o nazwie „groupsScrollView” w poniższym kodzie), zawierający RelativeLayout. Mam kilka przycisków i tekst, a następnie mój ListView poniżej, który wyświetla listę nazw. Wszystko to jest wyższe niż widoczny obszar ekranu, więc użytkownik może przewijać w pionie, aby zobaczyć wszystko.

To wszystko działa pięknie, z wyjątkiem tego, że kiedy strona się ładuje, jest zawsze przewijana wstępnie na górę mojego ListView - na środku strony. Utworzony przeze mnie tekst i przyciski, które mówią użytkownikowi, co należy zrobić, nie są widoczne.

Mogę chwycić ekran i przewinąć w górę, to działa dobrze, ale chciałbym, aby ekran załadował się po przewinięciu do góry. Użytkownik nie powinien przewijać W GÓRĘ, aby zobaczyć górę świeżo załadowanej strony. Ale wszystko, co próbowałem programowo przewinąć do góry ekranu, zawiodło.

Oto moja metoda onCreate:

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

Problem stanowi wiersz "mainScrollView.pageScroll (View.FOCUS_UP);. Nie powoduje on błędu, ale wydaje się, że nic nie robi. Próbowałem scrollTo (0,0), scrollDirection (View.FOCUS_UP ) i wszystko inne, co przychodzi mi do głowy.

Ponieważ nie pojawia się błąd, muszę założyć, że te polecenia przewijania faktycznie działają, ale przewijają się do góry ListView, a nie do góry ScrollView lub RelativeLayout, który je zawiera. Wydaje się to potwierdzać własny opis metody scrollTo (int x, int y) firmy Google, w którym mówi się: „Ta wersja ogranicza również przewijanie do granic naszego dziecka”.

Aby więc długie pytanie było jeszcze dłuższe, w jaki sposób załadować kilka widoków na ekranie zawartym w ScrollView, a następnie programowo przewijać całość na górę strony, aby użytkownik mógł z nią wchodzić w interakcję?

Dzięki!

Glenn
źródło

Odpowiedzi:

264

Próbować

mainScrollView.fullScroll(ScrollView.FOCUS_UP);

powinno działać.

roomtek
źródło
U mnie też działa. : D
Muntasir Aonik
1
Znalazłem ScrollView # fullScroll () zmienia bieżący fokus. Testowałem z EditText.
iluzja
175

Miałem ten sam problem, prawdopodobnie jakiś błąd.

Nawet odpowiedź fullScroll(ScrollView.FOCUS_UP)z drugiej odpowiedzi nie działała.
Jedyną rzeczą, która zadziałała, było dzwonienie scroll_view.smoothScrollTo(0,0)zaraz po wyświetleniu okna dialogowego.

Mohamed Hafez
źródło
Początkowo to działało, ale później, kiedy próbowałem to robić wielokrotnie, takie podejście nie przyniosło zadowalających rezultatów.
Jaydeep W
16
rozwiązuję z scroll_view.smoothScrollTo (0,0), gdy mamy widok listy z boku widoku scrollview w tym czasie fullScroll (ScrollView.FOCUS_UP) nie będzie działać.
Kirtikumar A.
1
scroll_view.smoothScrollTo (0,0) działało lepiej dla mnie niż mainScrollView.fullScroll (ScrollView.FOCUS_UP); DZIĘKI!
marienke
2
Na większości urządzeń fullScroll (ScrollView.FOCUS_UP) działało, ale na Nexusie 5 i urządzeniach, na których rozmiar tekstu był większy - fullSscroll przesunął się pod paskiem akcji, smoothScrollTo (0,0) działało lepiej na wszystkich urządzeniach.
jt-gilkeson
Ma to również tę zaletę fullScroll(), że utrzymuje wybrany element nadal wybrany po przeniesieniu. W fullScroll()moim przypadku pierwszy element w widoku przewijania był wybierany ponownie za każdym razem, niezależnie od wybranego elementu przed przesunięciem przewijania w górę.
GeertVc
97

Miałem ten sam problem i to go naprawiło. Mam nadzieję, że ci to pomoże.

listView.setFocusable(false);
Miguel Palacios
źródło
3
Jest to również poprawne rozwiązanie dla widoków siatki. Dzieje się tak, gdy masz więcej niż jeden element widoku w tej samej grupie widoków, zwłaszcza inny widok listy na górze listy lub widoku siatki. Ponieważ adapter powiadamia wątek interfejsu użytkownika, widok jest ponownie tworzony i przewija się do pierwszego elementu. Po prostu wywołaj listView.setFocusable (false) / gridView.setFocusable (false). Jeśli zrobisz to z pliku xml, to jednak nie zadziała.
omersem
To rozwiązanie zadziałało również w moim przypadku, miałem RecyclerView wewnątrz NestedScrollView, więc automatycznie przewijał się w dół. Dzięki!
Nilesh Rathore
Musiałem to dodać przed ustawieniem adaptera, ale potem działa jak urok. Dzięki
Społeczność
doskonałe wyjaśnienie !!
Borja López
Dziękuję Miguelowi, a zwłaszcza omersemowi za wyjaśnienie, zwłaszcza część o tym, jak to nie zadziała, jeśli zostanie ustawione w XML, zaoszczędziło mi to trochę bólu głowy
buradd
53

scrollViewObject.fullScroll(ScrollView.FOCUS_UP)to działa dobrze, ale tylko problem z tą linią polega na tym, że kiedy dane są zapełniane w scrollViewObject, został wywołany natychmiast. Musisz poczekać kilka milisekund, aż dane zostaną wypełnione. Wypróbuj ten kod:

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

LUB

 scrollViewObject.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        scrollViewObject.getViewTreeObserver().removeOnGlobalLayoutListener(this);
        scrollViewObject.fullScroll(View.FOCUS_UP);
    }
});
Mohammad Imran
źródło
Miałem do czynienia z tym samym problemem, w którym widok przewijania miał przewijać się do dołu strony, naprawiłem to od razu!
Machine Tribe
1
Zamiast zgadywać „magiczną” liczbę milisekund, które będziesz musiał czekać, View objects oferuje metodę post (Runnable), której możesz użyć, która opublikuje element wykonawczy, gdy widżet wyrenderuje się w interfejsie użytkownika.
Ryan
@Ryan: post (Runnable) zawsze nie działa dla mnie na Nexusie 5 (Genymotion)
Dika
post (Runnable) może nie działać, ponieważ jeśli pobierasz dane po kroku infloacji widoku, najpierw działa przewijanie w górę, a następnie dane są wypełniane w widoku. Więc albo powinieneś użyć postDelayed (Runnable, dummyMillis), albo powinieneś użyć scroll.fullScroll (ScrollView.FOCUS_UP) po procesach zapełnionych danymi.
oguzhan
Twoje pierwsze rozwiązanie, w którym podajesz milisach statycznych, jest złym podejściem.
Malwinder Singh
35

Głównym problemem wszystkich tych prawidłowych rozwiązań jest to, że próbujesz przesunąć zwój w górę, gdy nie jest jeszcze narysowany na ekranie.

Musisz poczekać, aż widok przewijania pojawi się na ekranie, a następnie przenieś go do góry za pomocą dowolnego z tych rozwiązań. To lepsze rozwiązanie niż opóźnienie po terminie, ponieważ nie przeszkadza ci opóźnienie.

    // Wait until my scrollView is ready
    scrollView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            // Ready, move up
            scrollView.fullScroll(View.FOCUS_UP);
        }
    });
Pelanes
źródło
7
Niezłe rozwiązanie - nie zapomnij usunąć Global Listener na końcu onGlobalLayoutwywołania metody scrollView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
Nilhcem
30

Bardzo łatwe

    ScrollView scroll = (ScrollView) findViewById(R.id.addresses_scroll);
    scroll.setFocusableInTouchMode(true);
    scroll.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
CommonSenseCode
źródło
3
u mnie to nie zadziałało (trzecia linia), rozwiązaniem było: scroll.fullScroll (View.FOCUS_UP); (z włączeniem pierwszych 2 linii)
medskill
@medskill, Cześć, czy zapisałeś je w swoim pliku xml? Odkryłem, że działa to tylko w onCreate().
imknown
@ imknownJ.Kimu działa dla mnie w pliku xml
Paglian
To zadziałało dla mnie. Potrzebujesz zarówno drugiej, jak i trzeciej linii. Dla mnie to nie zadziała w XML.
ET
2
Pocałowałbym cię, gdybym mógł! Spędziłem nad tym 1-2 godziny. Najlepszy, gdy czekasz na załadowanie dwóch list i nie chcesz się na nich skupiać. Dodano go do onResume ().
John T
24

Napotkałem ten sam problem Kiedy używam Scrollview w View Flipper lub Dialog, sprawa scrollViewObject.fullScroll(ScrollView.FOCUS_UP)zwraca fałsz, więc sprawa scrollViewObject.smoothScrollTo(0, 0)działa dla mnie

Przewiń Focus Top

sravan
źródło
19
 final ScrollView scrollview = (ScrollView)findViewById(R.id.selected_place_layout_scrollview);

         scrollview.post(new Runnable() { 
                public void run() { 
                    scrollview.scrollTo(0, 0); 
                } 
            }); 
Mladen Rakonjac
źródło
9
runOnUiThread( new Runnable(){
   @Override
   public void run(){
      mainScrollView.fullScroll(ScrollView.FOCUS_UP);
   }
}
traeper
źródło
9

To działa dla mnie

scroll_view.smoothScrollTo(0,0); // scroll to top of screen
Pankaj Talaviya
źródło
6

To jest wymuszone przewijanie w widoku przewijania :

            scrollView.post(new Runnable() {
                @Override
                public void run() {
                    scrollView.scrollTo(0, 0);
                    scrollView.pageScroll(View.FOCUS_UP);
                    scrollView.smoothScrollTo(0,0);
                }
            });
Uwaga Hadi
źródło
4
scrollView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        // Ready, move up
        scrollView.fullScroll(View.FOCUS_UP);
    }
});

źródło
1

Naprawiłem swój problem w Kotlinie w ten sposób:

scrollview.isFocusableInTouchMode = true
scrollview.fullScroll(View.FOCUS_UP)
scrollview.smoothScrollTo(0,0)
MaK
źródło
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);
    }
Ajeet Gour
źródło
to na pewno będzie działać,
Ajeet Gour
Proszę podać krótkie wyjaśnienie, w jaki sposób ta odpowiedź rozwiązuje problem.
Ayberk Özgür