2010-12-13 14 views
16

Tworzę listę zdjęć przy użyciu ListView, a zdjęcia mają rozmiar, który zmieściłby od 2 do 3 zdjęć na ekranie.Widok listy Przyciągnij do pozycji

Problem, który mam, polega na tym, że chciałbym, aby użytkownik przestał przewijać, aby pierwszy element widocznej listy został przyciągnięty do górnej części ekranu, na przykład, jeśli przewijanie się zakończy i mała część pierwsze wyświetlane zdjęcie, przewijamy listę tak, aby obraz był zawsze w pełni wyświetlany, jeśli wyświetla się głównie obraz, przewijamy listę tak, aby następny obraz był w pełni widoczny.

Czy istnieje sposób osiągnięcia tego w Androidzie z listview?

Odpowiedz

26

Znalazłem sposób to po prostu słuchania przewinąć i zmienić pozycję podczas przewijania zakończył wdrożenie ListView.OnScrollListener

@Override 
public void onScrollStateChanged(AbsListView view, int scrollState) { 
    switch (scrollState) { 
    case OnScrollListener.SCROLL_STATE_IDLE: 
     if (scrolling){ 
      // get first visible item 
      View itemView = view.getChildAt(0); 
      int top = Math.abs(itemView.getTop()); // top is a negative value 
      int bottom = Math.abs(itemView.getBottom()); 
      if (top >= bottom){ 
       ((ListView)view).setSelectionFromTop(view.getFirstVisiblePosition()+1, 0); 
      } else { 
       ((ListView)view).setSelectionFromTop(view.getFirstVisiblePosition(), 0); 
      } 
     } 
     scrolling = false; 
     break; 
    case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: 
    case OnScrollListener.SCROLL_STATE_FLING: 
     Log.i("TEST", "SCROLLING"); 
     scrolling = true; 
     break; 
    } 
} 

Zmiana nie jest tak gładka, ale działa zrobienia.

+0

Jeśli chcesz, aby był płynny, spróbuj użyć widoku ((ListView)).smoothScrollToPosition (pos); – stealthcopter

+2

Dwa problemy z wykonaniem tego: 1) smoothScrollToPosition spowoduje, że powyższa logika wykryje kolejny zwój i przejdzie do niestabilnej pętli - możesz tego uniknąć, usuwając skrzynkę dla SCROLL_STATE_FLING, ale nie jestem pewien, czy to dobra poprawka. 2) smoothScrollToPosition nie wyrównuje rzeczy poprawnie, przynajmniej z moimi listami. Przewija górną część określonego elementu do 15 pikseli powyżej wierzchołka listy, zamiast dopasowywać rzeczy poprawnie, tak jak to robi setSelection/setSelectionFromTop. – benkc

+0

Jakieś płynne rozwiązanie? – benkol

0

Oprócz wypróbowania powyższego kodu, powinieneś się upewnić, że twoja listView ma wysokość, która może pasować do dokładnej liczby elementów, które chcesz wyświetlić. np. Jeśli chcesz, aby 4 elementy były wyświetlane po efektach przyciągania, a wysokość wiersza (zdefiniowana w jego układzie) powinna wynosić 1/4 całkowitej wysokości listy.

2

Stosując rozwiązanie @nininho „s,

W onScrollStateChanged gdy zmienia się stan na SCROLL_STATE_IDLE pamiętać, stanowisko, przystawki i podnieść flagę:

snapTo = view.getFirstVisiblePosition(); 
shouldSnap = true; 

Następnie zastąpić metodę computeScroll():

@Override 
public void computeScroll() { 
    super.computeScroll(); 
    if(shouldSnap){ 
     this.smoothScrollToPositionFromTop(snapTo, 0); 
     shouldSnap = false; 
    } 
} 
5

Wykorzystując kilka pomysłów z rozwiązania @ nininho, mam listę do przyciągnięcia do przedmiotu z gładkim scr oll zamiast nagle do niego trafić. Jedynym zastrzeżeniem jest to, że testowałem to rozwiązanie tylko na Moto X w podstawowym ListView z tekstem, ale działa bardzo dobrze na urządzeniu. Niemniej jednak jestem przekonany co do tego rozwiązania i zachęcam do wyrażenia opinii.

listview.setOnScrollListener(new OnScrollListener() { 
     @Override 
     public void onScrollStateChanged(AbsListView view, int scrollState) { 
      // TODO Auto-generated method stub 
      if (scrollState == SCROLL_STATE_IDLE) { 
       View itemView = view.getChildAt(0); 
       int top = Math.abs(itemView.getTop()); 
       int bottom = Math.abs(itemView.getBottom()); 
       int scrollBy = top >= bottom ? bottom : -top; 
       if (scrollBy == 0) { 
        return; 
       } 
       smoothScrollDeferred(scrollBy, (ListView)view); 
      } 
     } 

     private void smoothScrollDeferred(final int scrollByF, 
       final ListView viewF) { 
      final Handler h = new Handler(); 
      h.post(new Runnable() { 

       @Override 
       public void run() { 
        // TODO Auto-generated method stub 
        viewF.smoothScrollBy(scrollByF, 200); 
       } 
      }); 
     } 

     @Override 
     public void onScroll(AbsListView view, int firstVisibleItem, 
       int visibleItemCount, int totalItemCount) { 
      // TODO Auto-generated method stub 

     } 
    }); 

Powodem odroczenia gładkie przewijanie jest, bo w moim testów bezpośrednio dzwoniąc metoda smoothScrollBy w stanie zmienił zwrotna miał problemy faktycznie przewijania. Nie przewiduję też w pełni przetestowanego, niezawodnego rozwiązania o bardzo wysokim stanie, aw poniższym rozwiązaniu nie posiadam żadnego stanu. To rozwiązanie nie jest jeszcze dostępne w sklepie Google Play, ale powinno być dobrym punktem wyjścia.

+0

Kiedy 'top> = bottom',' scrollBy' jest równy (** N: ** cols w GridView, N = 1 dla ListView) 'view.getChildAt (view.getFirstVisiblePosition() + N) .getY()'. – samosaris

0

Należy pamiętać, że po wywołaniu smoothScrollBy(), getFirstVisiblePosition() może wskazywać na element listy nad najwyższym jeden w ListView. Jest to szczególnie ważne, gdy view.getChildAt(0).getBottom() == 0. Musiałem zadzwonić pod numer view.setSelection(view.getFirstVisiblePosition() + 1), aby zaradzić temu dziwnemu zachowaniu.