Come disabilitare lo scorrimento di RecyclerView?


221

Non riesco a disabilitare lo scorrimento in RecyclerView. Ho provato a chiamare rv.setEnabled(false)ma posso ancora scorrere.

Come posso disabilitare lo scorrimento?


1
Qual è lo scopo di utilizzare RecyclerViewse non si desidera scorrere?
CommonsWare,

16
@CommonsWare, voglio solo disabilitarlo temporaneamente, ad esempio, mentre sto facendo un'animazione personalizzata con uno dei suoi figli.
Zsolt Safrany,

1
Ah, va bene, ha senso. Avrei probabilmente messo un trasparente Viewsopra RecyclerView, alternando tra VISIBLEe GONEsecondo necessità, ma al di fuori del bracciale il tuo approccio sembra ragionevole.
CommonsWare,

Spero che questo aiuti a trovare una soluzione migliore.
Saiteja Parsi,

1
@CommonsWare, ecco a cosa mi serve, ad esempio. Devo visualizzare le immagini in RecyclerView una alla volta, senza immagini parzialmente visibili, solo una nella mia vista. E ci sono frecce a sinistra ea destra con cui l'utente può navigare. A seconda dell'immagine attualmente visualizzata (sono di vari tipi), vengono attivate alcune cose all'esterno di RecyclerView. È il design che i nostri clienti desiderano.
Varvara Kalinina,

Risposte:


368

Per questo, dovresti sovrascrivere il layoutmanager del tuo recycleview. In questo modo disabiliterà solo lo scorrimento, nessuna delle altre funzionalità. Sarai comunque in grado di gestire i clic o qualsiasi altro evento touch. Per esempio:-

Originale:

 public class CustomGridLayoutManager extends LinearLayoutManager {
 private boolean isScrollEnabled = true;

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

 public void setScrollEnabled(boolean flag) {
  this.isScrollEnabled = flag;
 }

 @Override
 public boolean canScrollVertically() {
  //Similarly you can customize "canScrollHorizontally()" for managing horizontal scroll
  return isScrollEnabled && super.canScrollVertically();
 }
}

Qui usando il flag "isScrollEnabled" è possibile abilitare / disabilitare temporaneamente la funzionalità di scorrimento della vista di riciclo.

Anche:

Sostituisci semplicemente l'implementazione esistente per disabilitare lo scorrimento e consentire il clic.

 linearLayoutManager = new LinearLayoutManager(context) {
 @Override
 public boolean canScrollVertically() {
  return false;
 }
};

2
Questa dovrebbe essere la risposta corretta in quanto disabilita lo scorrimento e mi consente di fare clic.
Jared Burrows,

10
La disabilitazione dello scorrimento sui LayoutManger predefiniti dovrebbe già esistere, un utente dell'API non dovrebbe farlo. Grazie per la risposta però!
Martin O'Shea,

1
Cosa succede se desidero disabilitare lo scorrimento solo da un determinato elemento? Significa che puoi scorrere verso il basso (o verso l'alto), ma poi bloccarti, fino a quando non lo riattivo nuovamente?
sviluppatore Android il

2
Questo disabilita anche smoothScrollToPosition
Mladen Rakonjac il

4
aggiungi oggetto versione kotlin: LinearLayoutManager (this) {ignora divertimento canScrollVertically (): Booleano {return false}}
amorenew

149

La vera risposta è

recyclerView.setNestedScrollingEnabled(false);

Maggiori informazioni nella documentazione


39
Questo disabiliterà solo lo scorrimento nidificato, non tutto lo scorrimento. In particolare, se RecyclerView si trova in uno ScrollView ma non riempie lo schermo, ScrollView non scorrerà (il contenuto si adatta allo schermo) e otterrai l'interfaccia utente di scorrimento in RecyclerView (effetto di fine elenco quando si tenta di scorrere ad esempio ) anche se non scorrerà quando diventa più grande. L'estensione del LayoutManager fa davvero il lavoro.
personne3000,

3
@ personne3000 Credo che recyclerView.setHasFixedSize (true) faccia il suo lavoro. Modifica: lo uso anche in combinazione con setFillViewport (true), quindi non sono sicuro di quale delle due correzioni.
mDroidd,

5
Questo funziona bene per me, ma il mio "RecyclerView" era all'interno di un layout mediante Frammento "ScrollView", così ho dovuto cambiare il "ScrollView" con "NestedScrollView", come descritto qui: stackoverflow.com/a/38088902/618464
educoutinho

1
Ricordate di usare il nome completo della classe per NestedScrollView: android.support.v4.widget.NestedScrollView, come descritto qui, per chessdork: stackoverflow.com/questions/37846245/...
gustavoknz

Dopo aver trascorso 4 ore a cercare di capire cosa c'è di sbagliato, questo ha risolto il mio problema. Nel mio caso sto usando un recyclerview all'interno di un MotionLayout. E ho aggiunto solo un'area di scorrimento per trascinare verso l'alto aggiungendo movimento: touchAnchorId = "@ id / swipe_area". Funziona normalmente ma si attiva anche quando si toccano le aree vuote di recyclerview. Fai attenzione quando usi motionLayout con altri elementi scorrevoli.
O. Kumru,

73

La risposta REAL REAL è: per API 21 e successive:

Nessun codice java necessario. È possibile impostare android:nestedScrollingEnabled="false" in XML:

<android.support.v7.widget.RecyclerView
     android:id="@+id/recycler"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:clipToPadding="true"
     android:nestedScrollingEnabled="false"
     tools:listitem="@layout/adapter_favorite_place">

11
Penso che questa dovrebbe essere la risposta accettata, senza alcuno sforzo in classe è possibile disabilitare lo scorrimento e tuttavia il tocco è consentito nel layout.
VipiN Negi,

Cosa per le API inferiori a 21 ??
Mouaad Abdelghafour AITALI,

@pic per le API inferiori è possibile utilizzare: recyclerView.setNestedScrollingEnabled (false);
Milad Faridnia,

1
Risposta accettata per me.
Fernando Torres,

1
questa è la risposta migliore e più semplice, che evita anche l'inquinamento del codice non necessario e modelli di estensione della classe senza fine.
Raffaello C,

52

Questo è un po 'una soluzione alternativa, ma funziona; è possibile abilitare / disabilitare lo scorrimento inRecyclerView .

Questo è un vuoto che RecyclerView.OnItemTouchListenerruba ogni evento touch disabilitando così il bersaglio RecyclerView.

public class RecyclerViewDisabler implements RecyclerView.OnItemTouchListener {

    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        return true;
    }

    @Override
    public void onTouchEvent(RecyclerView rv, MotionEvent e) {

    }

    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

    }
}

Usandolo:

RecyclerView rv = ...
RecyclerView.OnItemTouchListener disabler = new RecyclerViewDisabler();

rv.addOnItemTouchListener(disabler);        // disables scolling
// do stuff while scrolling is disabled
rv.removeOnItemTouchListener(disabler);     // scrolling is enabled again 

8
Questo disabilita anche il clic dell'elemento?
Jahir Fiquitiva,

@JahirFiquitiva Sì. "Questo è un RecyclerView.OnItemTouchListener vuoto che ruba ogni evento touch"
Max

1
questo disabilita anche lo scorrimento della vista padre
Jemshit Iskenderov,

Questo disabilita anche il clic dell'oggetto
Muhammad Riyaz il

1
Perché usare una soluzione alternativa (con effetti collaterali) quando esiste una soluzione pulita? Sostituisci semplicemente LayoutManager (vedi altre risposte)
personne3000

37

Questo funziona per me:

  recyclerView.setOnTouchListener(new View.OnTouchListener() {
      @Override
      public boolean onTouch(View v, MotionEvent event) {
          return true;
      }
  });

11
Questo disabilita tutto il tocco.
Jared Burrows,

1
Bel trucco! Per riattivare nuovamente, basta reimpostare onTouch di onTouchListener per restituire false
HendraWD

uhm, ma c'è un avvertimentoCustom view 'RecyclerView' has setOnTouchListener called on it but does not override performClick
HendraWD,

Se ricevi lo stesso avvertimento che @HendraWD ha menzionato nel commento sopra, dai un'occhiata alle risposte a questa domanda: stackoverflow.com/questions/46135249/…
Nicolás Carrasco,

1
Invece di restituire vero, e quindi disabilitare tutti i tipi di eventi touch, proprio return e.getAction() == MotionEvent.ACTION_MOVE;invece di return true;cancellare solo gli eventi scroll / swipe.
Toufic Batache,

15

È possibile disabilitare lo scorrimento bloccando RecyclerView.

Congelare: recyclerView.setLayoutFrozen(true)

Per sbloccare: recyclerView.setLayoutFrozen(false)


1
Prendi nota: le viste figlio non vengono aggiornate quando RecyclerView è bloccato. Devo disabilitare lo scorrimento quando c'è abbastanza spazio sullo schermo per mostrare tutti gli elementi. Oh ... Che bella API Android per questo compito ...
Oleksandr nn,

1
Deprecato in API 28
Ohhh ThatVarun

13

Crea una classe che estenda la classe RecyclerView

public class NonScrollRecyclerView extends RecyclerView {

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

    public NonScrollRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public NonScrollRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int heightMeasureSpec_custom = MeasureSpec.makeMeasureSpec(
                Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec_custom);
        ViewGroup.LayoutParams params = getLayoutParams();
        params.height = getMeasuredHeight();
    }
}

Ciò disabiliterà l'evento scroll, ma non gli eventi click

Usa questo nel tuo XML e procedi come segue:

  <com.yourpackage.xyx.NonScrollRecyclerView 
     ...
     ... 
  />

Grazie! Tutto ciò è necessario quando vogliamo consentire alla vista del riciclatore di assumere la massima altezza in base al contenuto senza scorrimento interno. :)
Rahul Rastogi,

Ma se questa vista del riciclatore è in una vista a scorrimento (ad esempio ScollView), il gestore del layout dovrebbe sovrascrivere il metodo canScrollVertically () restituendo false da esso.
Rahul Rastogi,

@RahulRastogi usa NestedScrollView invece di ScrollView
Ashish,

Ehi, @AshishMangave, per favore, dimmi come riattivarlo programmaticamente
Shruti,

@Shruti non possiamo farlo programmaticamente. perché sovrascriviamo direttamente il metodo onMeasure di recyclerview. puoi provare per questo recyclerView.setNestedScrollingEnabled (false) .questo ti aiuterà
Ashish,

11

Se disabiliti solo la funzionalità di scorrimento di RecyclerViewallora puoi usare il setLayoutFrozen(true);metodo di RecyclerView. Ma non può essere disabilitato l'evento touch.

your_recyclerView.setLayoutFrozen(true);

questo metodo è obsoleto. puoi suggerirne un altro?
Aiyaz Parmar,

Deprecato in API 28
Ohhh ThatVarun

9
recyclerView.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener() {
        @Override
        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
            // Stop only scrolling.
            return rv.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING;
        }
    });


Questo interrompe lo scorrimento di smoothScrollToPosition () quando viene toccato.
ypresto,

Questa è la soluzione migliore per me perché consente lo scorrimento programmatico ma evita lo scorrimento dell'utente.
Miguel Sesma,

7

C'è una risposta davvero semplice.

LinearLayoutManager lm = new LinearLayoutManager(getContext()) {
                @Override
                public boolean canScrollVertically() {
                    return false;
                }
            };

Il codice sopra disabilita lo scorrimento verticale di RecyclerView.



6

Ha scritto una versione di Kotlin:

class NoScrollLinearLayoutManager(context: Context?) : LinearLayoutManager(context) {
    private var scrollable = true

    fun enableScrolling() {
        scrollable = true
    }

    fun disableScrolling() {
        scrollable = false
    }

    override fun canScrollVertically() =
            super.canScrollVertically() && scrollable


    override fun canScrollHorizontally() =
            super.canScrollVertically()

 && scrollable
}

utilizzo:

recyclerView.layoutManager = NoScrollLinearLayoutManager(context)
(recyclerView.layoutManager as NoScrollLinearLayoutManager).disableScrolling()

5

Estendi LayoutManagere sovrascrivi canScrollHorizontally()e canScrollVertically()disabilita lo scorrimento.

Tieni presente che l'inserimento di elementi all'inizio non tornerà automaticamente all'inizio, per aggirare il problema fai qualcosa del tipo:

  private void clampRecyclerViewScroll(final RecyclerView recyclerView)
  {
    recyclerView.getAdapter().registerAdapterDataObserver(new RecyclerView.AdapterDataObserver()
    {
      @Override
      public void onItemRangeInserted(int positionStart, int itemCount)
      {
        super.onItemRangeInserted(positionStart, itemCount);
        // maintain scroll position at top
        if (positionStart == 0)
        {
          RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
          if (layoutManager instanceof GridLayoutManager)
          {
            ((GridLayoutManager) layoutManager).scrollToPositionWithOffset(0, 0);
          }else if(layoutManager instanceof LinearLayoutManager)
          {
            ((LinearLayoutManager) layoutManager).scrollToPositionWithOffset(0, 0);
          }
        }
      }
    });
  }

1
questo disabilita anche smoothScrollToPosition
Mladen Rakonjac il

5

So che questa ha già una risposta accettata, ma la soluzione non tiene conto di un caso d'uso che ho riscontrato.

In particolare avevo bisogno di un elemento di intestazione che fosse ancora cliccabile, ma disabilitavo il meccanismo di scorrimento di RecyclerView. Questo può essere realizzato con il seguente codice:

recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
                            @Override
     public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
         return e.getAction() == MotionEvent.ACTION_MOVE;
     }

     @Override
     public void onTouchEvent(RecyclerView rv, MotionEvent e) {

     }

     @Override
     public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

    }
});

Per qualche motivo, devi fare clic giusto, senza muoverti per farlo funzionare.
Jared Burrows,

Buon punto, @JaredBurrows. Questo perché ignoriamo ACTION_MOVE. Affinché il tocco sia naturale, c'è qualcosa in più tra il tocco e un leggero movimento sullo schermo. Sfortunatamente, non sono stato in grado di risolvere questo problema.
Ryan Simon,

Grazie, esattamente quello di cui avevo bisogno!
Studente

5

Come setLayoutFrozenè deprecato, è possibile disabilitare lo scorrimento bloccando RecyclerView utilizzando suppressLayout.

Congelare:

recyclerView.suppressLayout(true)

Per sbloccare:

recyclerView.suppressLayout(false)

4

in XML: -

Puoi aggiungere

android:nestedScrollingEnabled="false"

nel file XML di layout figlio di RecyclerView

o

in Java: -

childRecyclerView.setNestedScrollingEnabled(false);

al tuo RecyclerView nel codice Java.

Utilizzando ViewCompat (Java): -

childRecyclerView.setNestedScrollingEnabled(false);funzionerà solo con dispositivi android_version> 21 . per funzionare in tutti i dispositivi utilizzare quanto segue

ViewCompat.setNestedScrollingEnabled(childRecyclerView, false);


Eccellente, tutte le API coperte, dovrebbero essere accese.
Ricard, il

3

Per qualche motivo la risposta di @Alejandro Gracia inizia a funzionare solo dopo pochi secondi. Ho trovato una soluzione che blocca istantaneamente RecyclerView:

recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
            @Override
            public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
                return true;
            }
            @Override
            public void onTouchEvent(RecyclerView rv, MotionEvent e) {
            }
            @Override
            public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
            }
        });

3

Sovrascrivi onTouchEvent () e onInterceptTouchEvent () e restituisce false se non hai bisogno di OnItemTouchListener. Questo non disabilita OnClickListeners di ViewHolders.

public class ScrollDisabledRecyclerView extends RecyclerView {
    public ScrollDisabledRecyclerView(Context context) {
        super(context);
    }

    public ScrollDisabledRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public ScrollDisabledRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean onTouchEvent(MotionEvent e) {
        return false;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent e) {
        return false;
    }
}

3

È possibile aggiungere questa riga dopo aver impostato l'adattatore

ViewCompat.setNestedScrollingEnabled(recyclerView, false);

Ora il tuo recyclerview funzionerà con scorrimento regolare


2

Ho lottato su questo problema per qualche ora, quindi vorrei condividere la mia esperienza, per la soluzione layoutManager va bene, ma se vuoi riattivare lo scorrimento il riciclatore tornerà all'inizio.

La migliore soluzione finora (almeno per me) sta usando il metodo @Zsolt Safrany ma aggiungendo getter e setter in modo da non dover rimuovere o aggiungere OnItemTouchListener.

Come segue

public class RecyclerViewDisabler implements RecyclerView.OnItemTouchListener {

    boolean isEnable = true;

    public RecyclerViewDisabler(boolean isEnable) {
        this.isEnable = isEnable;
    }

    public boolean isEnable() {
        return isEnable;
    }

    public void setEnable(boolean enable) {
        isEnable = enable;
    }

    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        return !isEnable;
    }

    @Override
    public void onTouchEvent(RecyclerView rv, MotionEvent e) {}

   @Override
   public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept){}
 }

uso

RecyclerViewDisabler disabler = new RecyclerViewDisabler(true);
feedsRecycler.addOnItemTouchListener(disabler);

// TO ENABLE/DISABLE JUST USE THIS
disabler.setEnable(enable);

2

In Kotlin, se non si desidera creare una classe aggiuntiva solo per l'impostazione di un valore, è possibile creare una classe anonima da LayoutManager:

recyclerView.layoutManager = object : LinearLayoutManager(context) {
    override fun canScrollVertically(): Boolean = false
}

1

Ecco come l'ho fatto con l'associazione dei dati:

            <android.support.v7.widget.RecyclerView
                android:id="@+id/recycler_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:clipChildren="false"
                android:onTouch="@{(v,e) -> true}"/>

Al posto del "vero" ho usato una variabile booleana che è cambiata in base a una condizione in modo che la vista del riciclatore passasse da disabilitato a abilitato.


1

Sono arrivato con un frammento che contiene più RecycleView, quindi ho bisogno solo di una barra di scorrimento invece di una barra di scorrimento in ogni RecycleView.

Quindi ho appena inserito ScrollView nel contenitore padre che contiene 2 RecycleViews e lo uso android:isScrollContainer="false"in RecycleView

<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layoutManager="LinearLayoutManager"
    android:isScrollContainer="false" />

1

Inserisci

android:descendantFocusability="blocksDescendants"

in tuo figlio di SrollView o NestedScrollView (e padre di ListView, recyclerview e gridview nessuno)


1

Esiste un modo più semplice per disabilitare lo scorrimento (tecnicamente si tratta piuttosto dell'intercettazione di un evento di scorrimento e della sua conclusione quando viene soddisfatta una condizione), utilizzando solo la funzionalità standard. RecyclerViewha il metodo chiamato addOnScrollListener(OnScrollListener listener), e usando solo questo puoi fermarlo dallo scorrimento, proprio così:

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);
        if (viewModel.isItemSelected) {
            recyclerView.stopScroll();
        }
    }
});

Caso d'uso: supponiamo che tu voglia disabilitare lo scorrimento quando fai clic su uno degli elementi all'interno in RecyclerViewmodo da poter eseguire alcune azioni con esso, senza essere distratto scorrendo accidentalmente verso un altro elemento e, quando hai finito, fai semplicemente clic su l'elemento di nuovo per abilitare lo scorrimento. Per questo, vorresti collegarti OnClickListenera ogni elemento all'interno RecyclerView, quindi quando fai clic su un elemento, si passa isItemSelectedda falsea true. In questo modo quando provi a scorrere, RecyclerViewchiamerà automaticamente il metodo onScrollStateChangede poiché isItemSelectedimpostato su true, si fermerà immediatamente, prima di RecyclerViewavere la possibilità, beh ... di scorrere.

Nota: per una migliore usabilità, prova a utilizzare GestureListenerinvece di OnClickListenerimpedire i accidentalclic.


stopScroll()non è per bloccare la pergamena, è solo per fermare / fermare la vista di riciclo.
Farid,

@FARID, sì, questo metodo interrompe qualsiasi scorrimento in corso e, in questo caso particolare, lo fa ogni volta che un utente tenta di scorrere il contenuto di RecyclerView con isItemSelectedimpostato su true. Altrimenti, avresti bisogno di un RecyclerView personalizzato per disabilitare lo scorrimento per sempre e volevo evitarlo poiché avevo bisogno di solo un po 'di funzionalità extra, che questa soluzione offre.
Tim Jorjev,

0

Aggiungilo al tuo recycleview in xml

 android:nestedScrollingEnabled="false"

come questo

<android.support.v7.widget.RecyclerView
                    android:background="#ffffff"
                    android:id="@+id/myrecycle"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:nestedScrollingEnabled="false">

0

Per interrompere lo scorrimento al tocco, ma continua a scorrere tramite i comandi:

if (appTopBarMessagesRV == null) {appTopBarMessagesRV = findViewById (R.id.mainBarScrollMessagesRV);

        appTopBarMessagesRV.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener() {
            @Override
            public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {

                if ( rv.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING)
                {
                     // Stop  scrolling by touch

                    return false;
                }
                return  true;
            }
        });
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.