2011-01-18 16 views
44

Mam HorizontalScrollView zawierający LinearLayout. Na ekranie mam przycisk, który doda nowe widoki do LinearLayout w czasie wykonywania i chciałbym, aby przewijany widok przewinął do końca listy po dodaniu nowego widoku.HorizontalScrollView: automatyczne przewijanie do końca po dodaniu nowych widoków?

Mam prawie wszystko działa - oprócz tego, że zawsze przewija jeden widok krótszy od ostatniego widoku. Wygląda na to, że przewija się bez uprzedniego obliczenia włączenia nowego widoku.

W mojej aplikacji używam niestandardowego obiektu View, ale zrobiłem małą aplikację testową, która używa ImageView i ma ten sam symptom. Próbowałem różnych rzeczy, takich jak requestLayout() zarówno na Layout i ScrollView, spróbowałem scrollTo (Integer.MAX_VALUE) i przewinął do netherverse :) Czy naruszam problem z wątkiem UI czy coś?

  • Rick

======

public class Main extends Activity { 
     /** Called when the activity is first created. */ 
     @Override 
     public void onCreate(Bundle savedInstanceState) { 
      super.onCreate(savedInstanceState); 
      setContentView(R.layout.main); 

      Button b = (Button) findViewById(R.id.addButton); 
      b.setOnClickListener(new AddListener()); 

      add(); 
     } 

     private void add() { 
      LinearLayout l = (LinearLayout) findViewById(R.id.list); 
      HorizontalScrollView s = 
       (HorizontalScrollView) findViewById(R.id.scroller); 

      ImageView i = new ImageView(getApplicationContext()); 
      i.setImageResource(android.R.drawable.btn_star_big_on); 
      l.addView(i); 

      s.fullScroll(HorizontalScrollView.FOCUS_RIGHT); 
     } 

     private class AddListener implements View.OnClickListener { 
      @Override 
      public void onClick(View v) { 
       add(); 
      } 
     } 
    } 

Układ XML:

<?xml version="1.0" encoding="utf-8"?> 
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
     android:orientation="vertical" 
     android:layout_width="fill_parent" 
     android:layout_height="fill_parent"> 

     <HorizontalScrollView 
      android:id="@+id/scroller" 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:scrollbarSize="50px"> 
      <LinearLayout 
       android:id="@+id/list" 
       android:layout_width="wrap_content" 
       android:layout_height="wrap_content" 
       android:padding="4px"/> 
     </HorizontalScrollView> 

     <LinearLayout 
      android:layout_width="fill_parent" 
      android:layout_height="fill_parent"    
      android:layout_gravity="center"> 
      <Button 
       android:id="@+id/addButton" 
       android:layout_width="wrap_content" 
       android:layout_height="wrap_content" 
       android:layout_gravity="center" 
       android:paddingLeft="80px" 
       android:paddingRight="80px" 
       android:paddingTop="40px" 
       android:paddingBottom="40px" 
       android:text="Add"/> 
     </LinearLayout> 

    </LinearLayout> 

Odpowiedz

113

myślę, że to kwestia rozrządu. Układ nie jest tworzony po dodaniu widoku. Jest to wymagane i zrobione niedługo później. Kiedy wywołasz fullScroll natychmiast po dodaniu widoku, szerokość linelayout nie miała szansy na rozwinięcie.

spróbować wymienić:

s.fullScroll(HorizontalScrollView.FOCUS_RIGHT); 

z:

s.postDelayed(new Runnable() { 
    public void run() { 
     s.fullScroll(HorizontalScrollView.FOCUS_RIGHT); 
    } 
}, 100L); 

Krótkie opóźnienie powinno dać systemowi wystarczającej ilości czasu na aklimatyzację.

P.S. Wystarczy opóźnić przewijanie aż do bieżącej iteracji pętli interfejsu użytkownika. Nie testowałem tej teorii, ale jeśli to prawda, to byłoby wystarczające, aby wykonać następujące czynności:

s.post(new Runnable() { 
    public void run() { 
     s.fullScroll(HorizontalScrollView.FOCUS_RIGHT); 
    } 
}); 

Lubię to lepiej, bo wprowadzając dowolną opóźnienie wydaje hacky do mnie (chociaż to była moja sugestia).

+5

Że to zrobił dokładnie Ted, dzięki bardzo, że został podsłuch mnie przez kilka dni! :) –

+0

Dzięki Ted hopp – praveenb

+1

+1 To pomogło mi :) –

12

Kolejna sugestia, ponieważ to pytanie bardzo mi pomogło :).

Możesz wstawić słuchacza, gdy widok zakończy fazę układania, a zaraz po wykonaniu polecenia fullScroll, musisz rozszerzyć klasę o to.

Zrobiłem to tylko dlatego, że chciałem przewinąć do sekcji tuż po onCreate(), aby uniknąć migotania od punktu początkowego do punktu przewijania.

Coś jak:

public class PagerView extends HorizontalScrollView { 

    private OnLayoutListener mListener; 
    ///... 
    private interface OnLayoutListener { 
     void onLayout(); 
    } 

    public void fullScrollOnLayout(final int direction) { 
     mListener = new OnLayoutListener() {    
      @Override 
      public void onLayout() { 
       fullScroll(direction) 
       mListener = null; 
      } 
     }; 
    } 

    @Override 
    protected void onLayout(boolean changed, int l, int t, int r, int b) { 
     super.onLayout(changed, l, t, r, b); 
     if(mListener != null) 
      mListener.onLayout(); 
    } 
} 
4

Użyj widoku metodę smoothScrollTo

postDelayed(new Runnable() { 
    public void run() { 
     scrollView.smoothScrollTo(newView.getLeft(), newView.getTop()); 
} 
}, 100L); 

należy umieścić w sposób runnable więc dać czas do procesu układu, aby ustawić nowy widok

+0

Cześć .. dzięki. ten kod jest bardzo przydatny dla mnie .. wielkie dzięki. – RVG

1

Myślę, że najlepszym podejściem jest ten opublikowany przez monxalo, ponieważ nie możesz wiedzieć, ile czasu zajmie, dopóki układ nie zostanie ukończony. Wypróbowałem to i działa idealnie. Jedyną różnicą jest to, że dodałem tylko jeden wiersz do mojego niestandardowego poziomego przewijania widoku:

@Override 
protected void onLayout(boolean changed, int l, int t, int r, int b) { 
    super.onLayout(changed, l, t, r, b); 
    this.fullScroll(HorizontalScrollView.FOCUS_RIGHT); 
} 
+0

Nie sądzę, że ilość czasu jest istotna. Problem polega na tym, że przewijanie nie powinno się odbywać w bieżącej iteracji pętli wątków interfejsu użytkownika. Po zakończeniu bieżącej iteracji hierarchia widoków powinna być aktualna i rozłożona. –

2

skorzystać z poniższego kodu do poziomego Scrollview {przewinąć do prawej}

view.post(new Runnable() { 
       @Override 
       public void run() { 
        ((HorizontalScrollView) Iview 
          .findViewById(R.id.hr_scroll)) 
          .fullScroll(HorizontalScrollView.FOCUS_RIGHT); 
       } 
      }); 
3

To co zrobiłem .

//Observe for a layout change  
ViewTreeObserver viewTreeObserver = yourLayout.getViewTreeObserver(); 
    if (viewTreeObserver.isAlive()) { 
    viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 
      @Override 
      public void onGlobalLayout() { 
       yourHorizontalScrollView.fullScroll(HorizontalScrollView.FOCUS_RIGHT); 
      } 
    }); 
    } 
10

zgadzam się z @monxalo i @ granko87, że lepszym rozwiązaniem jest z detektorem zamiast dokonywania założenie, że układ będzie kompletna, jeśli pozwolimy jakąś dowolną ilość czasu przejazdu.

W przypadku, podobnie jak ja, nie trzeba używać podklasy zwyczaj HorizontalScrollView można po prostu dodać OnLayoutChangeListener zachować rzeczy proste:

mTagsScroller.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { 
    @Override 
    public void onLayoutChange(View view, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { 
     mTagsScroller.removeOnLayoutChangeListener(this); 
     mTagsScroller.fullScroll(View.FOCUS_RIGHT); 
    } 
}); 
+0

To jest rozwiązanie, które również wymyśliłem. Mniej kodu, wykonał pracę. –

+0

Jest to zdecydowanie poprawna odpowiedź na problem. Oczekiwanie na dowolny losowy czas jest nonsensem. Oczekiwanie, że silnik układu graficznego wykona swoją pracę, jest właściwe. – DrAL3X

0

Dziecko View nie można od razu dodać w ViewGroup . Dlatego wymagane jest opublikowanie Runnable, które będzie uruchamiane po zakończeniu innych oczekujących zadań.

Więc po dodaniu View, użyj:

horizontalScrollView.post(new Runnable() { 
    @Override 
    public void run() { 
     horizontalScrollView.fullScroll(View.FOCUS_RIGHT); 
    } 
}); 
Powiązane problemy