2016-04-05 1 views
12

Ho implementato uno scorrimento orizzontale RecyclerView. Il mio numero LinearLayoutManager utilizza il numero LinearLayoutManager e il problema che sto affrontando è che quando provo a utilizzare scrollToPosition(position) o smoothScrollToPosition(position) o da LinearLayoutManager di scrollToPositionWithOffset(position). Né funziona per me. Una chiamata di scorrimento non scorre nella posizione desiderata o non richiama lo OnScrollListener.RecyclerView - Scorri verso la posizione non funziona ogni volta

Finora ho provato così tante diverse combinazioni di codice che non riesco a postarle tutte qui. In seguito è quello che funziona per me (ma solo parzialmente):

public void smoothUserScrollTo(final int position) { 

    if (position < 0 || position > getAdapter().getItemCount()) { 
     Log.e(TAG, "An attempt to scroll out of adapter size has been stopped."); 
     return; 
    } 

    if (getLayoutManager() == null) { 
     Log.e(TAG, "Cannot scroll to position a LayoutManager is not set. " + 
       "Call setLayoutManager with a non-null layout."); 
     return; 
    } 

    if (getChildAdapterPosition(getCenterView()) == position) { 
     return; 
    } 

    stopScroll(); 

    scrollToPosition(position); 

    if (lastScrollPosition == position) { 

     addOnLayoutChangeListener(new OnLayoutChangeListener() { 
      @Override 
      public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { 

       if (left == oldLeft && right == oldRight && top == oldTop && bottom == oldBottom) { 
        removeOnLayoutChangeListener(this); 

        updateViews(); 

        // removing the following line causes a position - 3 effect. 
        scrollToView(getChildAt(0)); 
       } 
      } 
     }); 
    } 

    lastScrollPosition = position; 
} 

@Override 
public void scrollToPosition(int position) { 
    if (position < 0 || position > getAdapter().getItemCount()) { 
     Log.e(TAG, "An attempt to scroll out of adapter size has been stopped."); 
     return; 
    } 

    if (getLayoutManager() == null) { 
     Log.e(TAG, "Cannot scroll to position a LayoutManager is not set. " + 
       "Call setLayoutManager with a non-null layout."); 
     return; 
    } 

//  stopScroll(); 

     ((LinearLayoutManager) getLayoutManager()).scrollToPositionWithOffset(position, 0); 
//  getLayoutManager().scrollToPosition(position); 
    } 

ho optato per scrollToPositionWithOffset() a causa della this ma il caso forse è diverso, come io uso un LinearLayoutManager invece di GridLayoutManager. Ma la soluzione funziona anche per me, ma come ho detto prima solo parzialmente.

  • Quando la chiamata per scorrere è dalla posizione 0 alla dimensione totale - la pergamena funziona come un incantesimo.
  • Quando lo scorrimento è da totalSize - 7 a totalSize - 3, la prima volta si scorre solo fino al settimo ultimo elemento nell'elenco. La seconda volta tuttavia posso scorrere fino
  • Quando si scorre da totalSize - 3 a totalSize, comincio a ottenere un comportamento imprevisto.

Se qualcuno ha trovato un lavoro intorno lo apprezzerei. Ecco lo gist del mio codice di ordinazione ReyclerView.

risposta

19

Ho avuto lo stesso problema alcune settimane fa e ho trovato solo una soluzione davvero pessima per risolverlo. Dovuto usare un postDelayed con 200-300 ms.

new Handler().postDelayed(new Runnable() { 
    @Override 
    public void run() { 
     yourList.scrollToPosition(position); 
    } 
}, 200); 

Se avete trovato una soluzione migliore, fatemelo sapere! In bocca al lupo!

+0

La soluzione migliore che ho trovato finora è fornita nell'interrogativo stesso (non abbastanza buono però!). Inoltre, non essere scortese, ma la tua soluzione è abbastanza cruda! Preferirei sovrascrivere il metodo 'scrollToPosition()' di LinearLayout quindi adottare questo approccio. – Abbas

+1

Anche se penso che questa soluzione sia rozza, questa è l'unica soluzione che funziona fino ad ora: \. grazie comunque! – aldok

+1

Questa è l'unica soluzione che ha funzionato. Prima ho chiamato chatAdapter.notifyDataSetChanged(); poi nuovo gestore() postDelayed (new Runnable() { @Override public void run() { se (list_chat.size()> 0) { recyclerView.smoothScrollToPosition (list_chat.size() - 1).; } } }, 200); –

1

Bene, ho trovato un lavoro in giro.

Dal scrollToPositionWithOffset(position, offset) era la migliore chiamata che ho avuto, lo sto usando. Ho testato l'output su vari set di dati e finora non ho trovato alcuna incongruenza. Spero che non ci sia (ma se qualcuno trova qualche commento per favore qui sotto).

Ma poi c'è la domanda di last 7 Items che lo scroller non riesce in qualche modo a scorrere. Per quegli articoli Unfortunate Attualmente sto usando smoothScrollBy(dx, dy). Ma dal momento che può essere usato solo se si conosce la posizione dello scrollto-item diventa un po 'complicato. Ma ecco la soluzione (nel sopra menzionato gist ho modificato solo la seguente definizione di funzione).

public void smoothUserScrollTo(final int position) { 

    if (position < 0 || position > getAdapter().getItemCount()) { 
     Log.e(TAG, "An attempt to scroll out of adapter size has been stopped."); 
     return; 
    } 

    if (getLayoutManager() == null) { 
     Log.e(TAG, "Cannot scroll to position a LayoutManager is not set. " + 
       "Call setLayoutManager with a non-null layout."); 
     return; 
    } 

    if (getChildAdapterPosition(getCenterView()) == position) { 
     scrollToView(getCenterView()); 
     return; 
    } 

    stopScroll(); 

    scrollToPosition(position); 

    if (lastScrollPosition == position || position >= getAdapter().getItemCount() - 7) { 

     addOnLayoutChangeListener(new OnLayoutChangeListener() { 
      @Override 
      public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { 

       if (left == oldLeft && right == oldRight && top == oldTop && bottom == oldBottom) { 

        if (getChildAdapterPosition(getCenterView()) == position) { 
         // Only remove the listener if/when the centered items is the item we want to scroll to. 
         removeOnLayoutChangeListener(this); 
         scrollToView(getCenterView()); 
         return; 
        } 

        if (position >= 0 && position < getAdapter().getItemCount() - 7) { 

         removeOnLayoutChangeListener(this); 
         updateViews(); 
         scrollToView(getChildAt(0)); 
        } 
        else if (position >= getAdapter().getItemCount() - 7 && position <= getAdapter().getItemCount()){ 

         // Search in the attached items of the RecyclerView for our required item. 
         // You can remove the loop and optimize your code further if you want to, 
         // but I am gonna leave it here as is. It is simple enough :-). 
         int childPosition = 0; 
         for (int i = 0; i < getChildCount(); i++) { 
          childPosition = getChildAdapterPosition(getChildAt(i)); 
          if (childPosition == position) { 
           updateViews(); 
           scrollToView(getChildAt(i)); 
           break; 
          } 

          // Since we couldn't find the item in attached items. 
          // Scroll to the last attached item (It'll dettach and attach 
          // necessary items). So we can scroll once the view is stable again. 
          if (i == getChildCount() - 2) { 
           scrollToView(getChildAt(i)); 
          } 
         } 
        } 
       } 
      } 
     }); 
    } 

    lastScrollPosition = position; 
} 

Nota: Io uso solo per smoothScrollBy(dx, dy) ultimi 7 elementi. Anche eventuali modifiche sono ben accette.

8

Risulta stavo avendo un problema simile fino a quando ho utilizzato

myRecyclerview.scrollToPosition(objectlist.size()-1) 

Sarebbe sempre rimanere al vertice, quando solo mettere nella dimensione ObjectList. Questo è stato fino a quando ho deciso di impostare la dimensione uguale a una variabile. Ancora una volta, non ha funzionato.Poi ho pensato che forse stava gestendo un'eccezionale percezione sessuale senza dirmelo. Quindi l'ho sottratto di 1. Quindi ha funzionato.

+1

Hahaha: D. È stato molto semplice Grazie! –

+0

ha funzionato per me ma non quando i record sono troppi. – MbaiMburu

0

Aveva lo stesso problema. Il mio problema era, che ho riempito la vista con i dati in un compito asincrono, dopo che ho provato a scorrere. Da onPostExecute ofc risolto questo problema. Un ritardo ha risolto anche questo problema, perché quando lo scorrimento è stato eseguito, l'elenco era già stato riempito.

0

Uso di seguito la soluzione per rendere visibile l'elemento selezionato nella vista del riciclatore dopo che la vista del riciclatore è stata ricaricata (cambio di orientamento, ecc.). Sostituisce LinearLayoutManager e utilizza onSaveInstanceState per salvare la posizione corrente del riciclatore. Quindi in onRestoreInstanceState viene ripristinata la posizione salvata. Infine, in onLayoutCompleted, scrollToPosition (mRecyclerPosition) viene utilizzato per rendere nuovamente visibile la posizione di riciclatore selezionata in precedenza, ma come ha dichiarato Robert Banyai, affinché funzioni in modo affidabile, è necessario inserire un certo ritardo. Immagino sia necessario fornire tempo sufficiente per l'adattatore per caricare i dati prima che venga chiamato scrollToPosition.

private class MyLayoutManager extends LinearLayoutManager{ 
    private boolean isRestored; 

    public MyLayoutManager(Context context) { 
     super(context); 
    } 

    public MyLayoutManager(Context context, int orientation, boolean reverseLayout) { 
     super(context, orientation, reverseLayout); 
    } 

    public MyLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 
     super(context, attrs, defStyleAttr, defStyleRes); 
    } 

    @Override 
    public void onLayoutCompleted(RecyclerView.State state) { 
     super.onLayoutCompleted(state); 
     if(isRestored && mRecyclerPosition >-1) { 
      Handler handler=new Handler(); 
      handler.postDelayed(new Runnable() { 
       @Override 
       public void run() { 
        MyLayoutManager.this.scrollToPosition(mRecyclerPosition); 
       } 
      },200); 

     } 
     isRestored=false; 
    } 

    @Override 
    public Parcelable onSaveInstanceState() { 
     Parcelable savedInstanceState = super.onSaveInstanceState(); 
     Bundle bundle=new Bundle(); 
     bundle.putParcelable("saved_state",savedInstanceState); 
     bundle.putInt("position", mRecyclerPosition); 
     return bundle; 
    } 

    @Override 
    public void onRestoreInstanceState(Parcelable state) { 
     Parcelable savedState = ((Bundle)state).getParcelable("saved_state"); 
     mRecyclerPosition = ((Bundle)state).getInt("position",-1); 
     isRestored=true; 
     super.onRestoreInstanceState(savedState); 
    } 
} 
Problemi correlati