42

Próbuję płynnie przewinąć do ostatniego elementu listy po dodaniu elementu do tablicy tablicowej skojarzonej z listview. Problemem jest to, że tylko przewija się losowoandroid: smoothScrollToPosition() nie działa poprawnie

arrayadapter.add(item); 
//DOES NOT WORK CORRECTLY: 
listview.smoothScrollToPosition(arrayadapter.getCount()-1); 

//WORKS JUST FINE: 
listview.setSelection(arrayadapter.getCount()-1); 
+1

i ta metoda nigdy nie zadziałała u mnie. –

+2

Znany błąd: Zobacz odpowiedź https://stackoverflow.com/questions/14479078/smoothscrolltopositionfromtop-is-not-always-working-like-it-should/20997828#20997828 –

Odpowiedz

1

nazywasz arrayadapter.notifyDataSetChanged() po nazywany arrayadapter.add()? Należy również pamiętać, że smoothScrollToPosition i setSelection są metodami dostępnymi w ListView, a nie arrayadapter, jak wspomniano powyżej.

W każdym przypadku, czy to pomaga: smoothScrollToPosition after notifyDataSetChanged not working in android

+0

tak .. Popełniłem błąd na forum, to oczywiście listview.smoothScrollToPosition (pos) i listview.setSelection (pos). i notifyDataSetChanged jest wywoływany automatycznie podczas wywoływania arrayadapter.add() .., inaczej setSelection nie działałby – user1515520

+1

to po prostu dziwne, że setSelection (pos) działa idealnie, ale smoothScrollToPosition (pos) nie, prawda? – user1515520

5

metodę setSelection() należy użyć.

+2

To niekoniecznie to samo, możesz nie chcieć, aby element został wybrany, lub możesz po prostu szukać gładkiego efektu – mdelolmo

+0

To mi naprawdę pomogło, zamiast nic się nie dzieje, nadal jest przewijane na sam koniec listy. – Slickelito

+1

'setSelection' rzeczywiście działa tam, gdzie' smoothScrollToPosition' nie, ale byłoby pomocne, gdybyś mógł nas oświecić, dlaczego tak jest. –

51

Prawdopodobnie chcesz powiedzieć ListView, aby opublikował zwój, gdy wątek interfejsu użytkownika może go obsłużyć (dlatego twój nie przewija się poprawnie). SmoothScroll musi wykonać dużo pracy, zamiast po prostu iść do pozycji ignorując prędkość/czas/etc. (wymagane w przypadku "animacji").

Dlatego należy zrobić coś takiego:

getListView().post(new Runnable() { 
     @Override 
     public void run() { 
      getListView().smoothScrollToPosition(pos); 
     } 
    }); 
+13

+1 dla idei 'post'. Jednak natknąłem się na przypadki, w których funkcja "smoothScrollToPosition" nadal nie działa, pozostawiając małą przestrzeń odsuniętą między górą widżetu a pozycją w pozycji do przewijania. W tych przypadkach 'setSelection' (w połączeniu z' post' oczywiście) działa bezbłędnie. –

+0

Tak, ja też. ListViews są znacznie gorsze niż UITableView i takie. To smutne, ponieważ Listy są podstawowym pojęciem w urządzeniach mobilnych, w którym naruszono rozmiar ekranu, więc musimy przewijać rzeczy. W każdym razie, po prostu wiesz, że przewijanie działa, po prostu musisz zrozumieć zasady, układ musiał zostać ułożony, musisz to zrobić w wątku UI, musisz go opublikować, aby lista mogła kolejkować operację itp. Musi się wydarzyć dużo magii, aby mógł się pojawić zwój. Jestem pewien, że @RomanianGuy może rzucić więcej światła na to. Nadal implementacja iSO działa bez zarzutu w przeważającej części. –

+5

Imię tego gościa to @RomainGuy BTW. –

11

(skopiowane z moją odpowiedź: smoothScrollToPositionFromTop() is not always working like it should)

To znany błąd. Zobacz https://code.google.com/p/android/issues/detail?id=36062

Jednak realizowane tego obejścia, która zajmuje się wszystkich przypadkach krawędzi, które mogą wystąpić:

najpierw wywołać smothScrollToPositionFromTop(position) a potem, kiedy zakończy przewijanie, zadzwoń setSelection(position). To ostatnie wywołanie koryguje niekompletne przewijanie, przeskakując bezpośrednio do pożądanej pozycji. W ten sposób użytkownik nadal ma wrażenie, że jest przewijany w animacji do tej pozycji.

I wdrożone tego obejścia w ciągu dwóch metod pomocniczych:

smoothScrollToPosition()

public static void smoothScrollToPosition(final AbsListView view, final int position) { 
    View child = getChildAtPosition(view, position); 
    // There's no need to scroll if child is already at top or view is already scrolled to its end 
    if ((child != null) && ((child.getTop() == 0) || ((child.getTop() > 0) && !view.canScrollVertically(1)))) { 
     return; 
    } 

    view.setOnScrollListener(new AbsListView.OnScrollListener() { 
     @Override 
     public void onScrollStateChanged(final AbsListView view, final int scrollState) { 
      if (scrollState == SCROLL_STATE_IDLE) { 
       view.setOnScrollListener(null); 

       // Fix for scrolling bug 
       new Handler().post(new Runnable() { 
        @Override 
        public void run() { 
         view.setSelection(position); 
        } 
       }); 
      } 
     } 

     @Override 
     public void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount, 
           final int totalItemCount) { } 
    }); 

    // Perform scrolling to position 
    new Handler().post(new Runnable() { 
     @Override 
     public void run() { 
      view.smoothScrollToPositionFromTop(position, 0); 
     } 
    }); 
} 

getChildAtPosition()

public static View getChildAtPosition(final AdapterView view, final int position) { 
    final int index = position - view.getFirstVisiblePosition(); 
    if ((index >= 0) && (index < view.getChildCount())) { 
     return view.getChildAt(index); 
    } else { 
     return null; 
    } 
} 
+0

To jest poprawna odpowiedź, dzięki! – FireZenk

2

Sposób wyboru zestaw wspomina Lars działa ale animacja była zbyt skąpa dla naszych celów jak pomija to, co zostało. Innym rozwiązaniem jest wielokrotne przywoływanie tej metody, dopóki pierwszą widoczną pozycją nie będzie twój indeks. Najlepiej to zrobić szybko i z ograniczeniem, ponieważ będzie on walczył z przewijaniem widoku przez użytkownika w inny sposób.

private void DeterminedScrollTo(Android.Widget.ListView listView, int index, int attempts = 0) { 
    if (listView.FirstVisiblePosition != index && attempts < 10) { 
     attempts++; 
     listView.SmoothScrollToPositionFromTop (index, 1, 100); 
     listView.PostDelayed (() => { 
      DeterminedScrollTo (listView, index, attempts); 
     }, 100); 
    } 
} 

Rozwiązanie jest w języku C#. Xamarin, ale powinien łatwo przetłumaczyć na język Java.

1
final Handler handler = new Handler(); 
//100ms wait to scroll to item after applying changes 
handler.postDelayed(new Runnable() { 
@Override 
public void run() { 
    listView.smoothScrollToPosition(selectedPosition); 
}}, 100);