¿Cómo deshabilitar el desplazamiento RecyclerView?

221

No puedo desactivar el desplazamiento en el RecyclerView. Intenté llamar rv.setEnabled(false)pero todavía puedo desplazarme.

¿Cómo puedo desactivar el desplazamiento?

Zsolt Safrany
fuente
1
¿Cuál es el punto de usar RecyclerViewsi no quieres desplazarte?
CommonsWare
16
@CommonsWare, solo quiero deshabilitarlo temporalmente, por ejemplo, mientras estoy haciendo una animación personalizada con uno de sus hijos.
Zsolt Safrany
1
Ah de acuerdo, eso tiene sentido. Probablemente habría puesto una capa transparente Viewsobre la parte superior RecyclerView, alternando entre VISIBLEy GONEsegún sea necesario, pero fuera de lugar, su enfoque parece razonable.
CommonsWare
Espero que esto ayude a encontrar una mejor solución.
Saiteja Parsi
1
@CommonsWare, esto es para lo que lo necesito, por ejemplo. Necesito mostrar imágenes en RecyclerView de una en una, sin imágenes parcialmente visibles, solo una en mi ventana gráfica. Y hay flechas a la izquierda y a la derecha con las que el usuario puede navegar. Dependiendo de la imagen que se muestra actualmente (son de varios tipos), se activan algunas cosas fuera de RecyclerView. Es el diseño que desean nuestros clientes.
Varvara Kalinina

Respuestas:

368

Debe anular el administrador de diseño de su vista de reciclaje para esto. De esta manera, solo deshabilitará el desplazamiento, ninguna de las otras funcionalidades. Aún podrá manejar clics o cualquier otro evento táctil. Por ejemplo:-

Original:

 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();
 }
}

Aquí, utilizando el indicador "isScrollEnabled", puede habilitar / deshabilitar la funcionalidad de desplazamiento de su vista de reciclaje temporalmente.

También:

Simplemente anule su implementación existente para deshabilitar el desplazamiento y permitir hacer clic.

 linearLayoutManager = new LinearLayoutManager(context) {
 @Override
 public boolean canScrollVertically() {
  return false;
 }
};
Saurabh Garg
fuente
2
Esta debería ser la respuesta correcta ya que deshabilita el desplazamiento mientras me permite hacer clic.
Jared Burrows el
10
Desactivar el desplazamiento en los LayoutMangers predeterminados ya debería existir, un usuario de la API no debería tener que hacer esto. Gracias por la respuesta sin embargo!
Martin O'Shea
1
¿Qué sucede si deseo desactivar el desplazamiento solo desde un determinado elemento? ¿Significa que puede desplazarse hacia abajo (o hacia arriba), pero luego bloquearse, hasta que lo vuelva a habilitar?
Desarrollador de Android
2
Esto también deshabilita smoothScrollToPosition
Mladen Rakonjac
44
agrega el objeto de versión kotlin: LinearLayoutManager (this) {override fun canScrollVertically (): Boolean {return false}}
amorenew
149

La verdadera respuesta es

recyclerView.setNestedScrollingEnabled(false);

Más información en la documentación.

Bozic Nebojsa
fuente
39
Esto solo deshabilitará el desplazamiento anidado, no todo el desplazamiento. En particular, si su RecyclerView está en un ScrollView pero no llena la pantalla, ScrollView no se desplazará (el contenido cabe en la pantalla), y obtendrá la IU de desplazamiento en su RecyclerView (efecto de fin de lista al intentar desplazarse, por ejemplo ) aunque no se desplazará cuando sea más grande. Extender el LayoutManager realmente hace el trabajo.
personne3000
3
@ personne3000 Creo que recyclerView.setHasFixedSize (verdadero) hace el trabajo para eso. Editar: también uso esto en conjunción con setFillViewport (verdadero), por lo que no estoy seguro de cuál de los dos lo soluciona.
mDroidd
55
Esto funciona bien para mí, pero mi "RecyclerView" estaba dentro de un diseño Fragment usando "ScrollView", así que tuve que cambiar el "ScrollView" por "NestedScrollView", como se describe aquí: stackoverflow.com/a/38088902/618464
educoutinho
1
Recuerde usar el nombre completo de la clase para NestedScrollView:, android.support.v4.widget.NestedScrollViewcomo se describe aquí, por chessdork: stackoverflow.com/questions/37846245/…
gustavoknz
Después de pasar 4 horas tratando de averiguar qué está mal, esto resolvió mi problema. En mi caso, estoy usando una vista de reciclador dentro de un MotionLayout. Y solo agregué un área de deslizamiento para arrastrar hacia arriba agregando movimiento: touchAnchorId = "@ id / swipe_area". Funciona normalmente pero también se activa cuando se tocan las áreas vacías de la vista de reciclado. Tenga cuidado al usar motionLayout con otros elementos que se pueden deslizar.
O. Kumru
73

La respuesta REAL REAL es: Para API 21 y superior:

No se necesita código java. Puedes configurar android:nestedScrollingEnabled="false" en 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">
Milad Faridnia
fuente
11
Creo que esta debería ser la respuesta aceptada, sin ningún esfuerzo en clase, puede desactivar el desplazamiento y, sin embargo, el toque está permitido en el diseño.
VipiN Negi
¿Qué pasa con las API inferiores a 21?
Mouaad Abdelghafour AITALI
@pic para API inferiores puede usar: recyclerView.setNestedScrollingEnabled (falso);
Milad Faridnia
1
Respuesta aceptada para mí.
Fernando Torres
1
Esta es la respuesta mejor y más directa, que también evita la contaminación innecesaria del código y patrones de extensión de clase sin fin.
Raphael C
52

Esta es una solución un poco complicada pero funciona; puede habilitar / deshabilitar el desplazamiento en el RecyclerView.

Este es un RecyclerView.OnItemTouchListenerevento de robo vacío de cada toque, lo que desactiva el objetivo 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) {

    }
}

Utilizándolo:

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 
Zsolt Safrany
fuente
8
¿Esto deshabilita el elemento haciendo clic también?
Jahir Fiquitiva
@JahirFiquitiva Sí. "Este es un RecyclerView vacío. OnItemTouchListener robando cada evento táctil"
Max
1
esto también deshabilita el desplazamiento de la vista principal también
Jemshit Iskenderov
Esto también deshabilita el clic en el elemento
Muhammad Riyaz
1
¿Por qué usar una solución alternativa (con efectos secundarios) cuando hay una solución limpia? Simplemente anule el LayoutManager (vea otras respuestas)
personne3000
37

Esto funciona para mi:

  recyclerView.setOnTouchListener(new View.OnTouchListener() {
      @Override
      public boolean onTouch(View v, MotionEvent event) {
          return true;
      }
  });
alxgarcia
fuente
11
Esto desactiva todo contacto.
Jared Burrows el
1
¡Buen truco! Para volver a habilitar de nuevo, simplemente vuelva a configurar el onTouch de onTouchListener para que devuelva falso
HendraWD
uhm, pero hay una advertenciaCustom view 'RecyclerView' has setOnTouchListener called on it but does not override performClick
HendraWD
Si recibe la misma advertencia que @HendraWD mencionó en el comentario anterior, eche un vistazo a las respuestas de esta pregunta: stackoverflow.com/questions/46135249/…
Nicolás Carrasco
1
En lugar de volver verdadero, y por lo tanto deshabilitar todo tipo de eventos táctiles, solo en return e.getAction() == MotionEvent.ACTION_MOVE;lugar de return true;tan solo se cancelan los eventos de desplazamiento / deslizamiento.
Toufic Batache
15

Puede deshabilitar el desplazamiento congelando su RecyclerView.

Congelar: recyclerView.setLayoutFrozen(true)

Para descongelar: recyclerView.setLayoutFrozen(false)

Virat Singh
fuente
1
Tome nota: las vistas secundarias no se actualizan cuando RecyclerView está congelado. Necesito desactivar el desplazamiento cuando hay suficiente espacio en la pantalla para mostrar todos los elementos. Oh ... Qué buena API de Android para esta tarea ...
Oleksandr Nos
1
Desaprobado en API 28
OhhhThatVarun
13

Crear clase que extienda la clase 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();
    }
}

Esto deshabilitará el evento de desplazamiento, pero no los eventos de clic

Use esto en su XML, haga lo siguiente:

  <com.yourpackage.xyx.NonScrollRecyclerView 
     ...
     ... 
  />
Ashish
fuente
¡Gracias! Todo esto es necesario cuando queremos permitir que la vista del reciclador tome altura completa de acuerdo con el contenido sin desplazamiento interno. :)
Rahul Rastogi
Pero si esta vista de reciclador se encuentra en una vista de desplazamiento (p. Ej., ScrollView), el administrador de diseño debe anular el método canScrollVertically () que devuelve falso.
Rahul Rastogi
@RahulRastogi usa NestedScrollView en lugar de ScrollView
Ashish
Hola @AshishMangave, por favor diga cómo habilitarlo nuevamente programáticamente
Shruti
@Shruti no podemos hacer esto programáticamente. bcz anulamos directamente el método onMeasure de recyclerview. Puede probar este recyclerView.setNestedScrollingEnabled (falso). Esto lo ayudará
Ashish
11

Si solo deshabilita la funcionalidad de desplazamiento de RecyclerView, puede usar el setLayoutFrozen(true);método de RecyclerView. Pero no se puede desactivar el evento táctil.

your_recyclerView.setLayoutFrozen(true);
Ekta Bhawsar
fuente
Este método está en desuso. puedes sugerir otro?
Aiyaz Parmar
Desaprobado en API 28
OhhhThatVarun
9
recyclerView.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener() {
        @Override
        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
            // Stop only scrolling.
            return rv.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING;
        }
    });

rajesh
fuente
Esto anula el desplazamiento de smoothScrollToPosition () cuando se toca.
ypresto
Esta es la mejor solución para mí porque permite el desplazamiento mediante programación pero evita el desplazamiento del usuario.
Miguel Sesma
7

Hay una respuesta realmente simple.

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

El código anterior deshabilita el desplazamiento vertical de RecyclerView.

Emi Raz
fuente
6

Escribió una versión de 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
}

uso:

recyclerView.layoutManager = NoScrollLinearLayoutManager(context)
(recyclerView.layoutManager as NoScrollLinearLayoutManager).disableScrolling()
Soy un dragón rana
fuente
5

Extienda LayoutManagery anule canScrollHorizontally()y canScrollVertically()desactive el desplazamiento.

Tenga en cuenta que insertar elementos al principio no se desplazará automáticamente hacia el principio, para evitar esto haga algo como:

  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);
          }
        }
      }
    });
  }
Aidanvii
fuente
1
esto desactiva también smoothScrollToPosition
Mladen Rakonjac
5

Sé que esto ya tiene una respuesta aceptada, pero la solución no tiene en cuenta un caso de uso que encontré.

Específicamente, necesitaba un elemento de encabezado en el que todavía se pudiera hacer clic, pero deshabilité el mecanismo de desplazamiento de RecyclerView. Esto se puede lograr con el siguiente código:

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) {

    }
});
Ryan Simon
fuente
Por alguna razón, debe hacer clic justo, sin moverse para que esto funcione.
Jared Burrows el
Buen punto, @JaredBurrows. Esto se debe a que no tenemos en cuenta ACTION_MOVE. Para que el tacto se sienta natural, hay algo de dar entre tocar y un ligero movimiento en la pantalla. Desafortunadamente, no pude resolver ese problema.
Ryan Simon
Gracias, exactamente lo que necesitaba!
Estudiante
5

Como setLayoutFrozenestá en desuso, puede desactivar el desplazamiento congelando su RecyclerView mediante suppressLayout.

Congelar:

recyclerView.suppressLayout(true)

Para descongelar:

recyclerView.suppressLayout(false)
Shylendra Madda
fuente
4

en XML: -

Puedes añadir

android:nestedScrollingEnabled="false"

en el archivo XML de diseño RecyclerView secundario

o

en Java: -

childRecyclerView.setNestedScrollingEnabled(false);

a su RecyclerView en código Java.

Usando ViewCompat (Java): -

childRecyclerView.setNestedScrollingEnabled(false);solo funcionará en android_version> 21 dispositivos. para trabajar en todos los dispositivos use lo siguiente

ViewCompat.setNestedScrollingEnabled(childRecyclerView, false);

jafarbtech
fuente
Excelente, todas las API cubiertas, deben estar encendidas.
Ricard
3

Por alguna razón, la respuesta de @Alejandro Gracia comienza a funcionar solo después de unos segundos. Encontré una solución que bloquea el RecyclerView instantáneamente:

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) {
            }
        });
vovahost
fuente
3

Anule onTouchEvent () y onInterceptTouchEvent () y devuelva false si no necesita OnItemTouchListener. Esto no deshabilita OnClickListeners de 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;
    }
}
ypresto
fuente
3

Puede agregar esta línea después de configurar su adaptador

ViewCompat.setNestedScrollingEnabled(recyclerView, false);

Ahora su vista de reciclaje funcionará con desplazamiento suave

Atefeh Srch
fuente
2

He estado luchando en este problema durante una hora, por lo que me gustaría compartir mi experiencia. Para la solución layoutManager está bien, pero si desea volver a habilitar el desplazamiento, el reciclador volverá al principio.

La mejor solución hasta ahora (al menos para mí) es usar el método @Zsolt Safrany pero agregar getter y setter para que no tenga que quitar o agregar OnItemTouchListener.

De la siguiente manera

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);
Aladino
fuente
2

En Kotlin, si no desea crear una clase adicional solo para establecer un valor, puede crear una clase anónima desde LayoutManager:

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

Así es como lo hice con el enlace de datos:

            <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}"/>

En lugar de "verdadero", utilicé una variable booleana que cambió en función de una condición para que la vista del reciclador cambiara entre estar deshabilitada y habilitada.

bwhite
fuente
1

Encontré un fragmento que contiene múltiples RecycleView, por lo que solo necesito una barra de desplazamiento en lugar de una barra de desplazamiento en cada RecycleView.

Así que solo puse el ScrollView en el contenedor principal que contiene las 2 RecycleViews y lo uso android:isScrollContainer="false"en el 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" />
pk028382
fuente
1

Añadir

android:descendantFocusability="blocksDescendants"

en su hijo de SrollView o NestedScrollView (y padre de ListView, recyclerview y gridview cualquiera)

Pankaj Talaviya
fuente
1

Hay una forma más directa de deshabilitar el desplazamiento (técnicamente es más bien la intercepción de un evento de desplazamiento y finalizarlo cuando se cumple una condición), utilizando solo la funcionalidad estándar. RecyclerViewtiene el método llamado addOnScrollListener(OnScrollListener listener), y usando solo esto puede evitar que se desplace, así:

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

Caso de uso: supongamos que desea deshabilitar el desplazamiento cuando hace clic en uno de los elementos dentro RecyclerViewpara poder realizar algunas acciones con él, sin distraerse desplazándose accidentalmente a otro elemento, y cuando haya terminado con él, simplemente haga clic en el elemento nuevamente para habilitar el desplazamiento. Para eso, querrá adjuntar OnClickListenera cada elemento dentro RecyclerView, por lo que cuando hace clic en un elemento, se alternaría isItemSelectedde falsea true. De esta forma, cuando intente desplazarse, RecyclerViewllamará automáticamente al método onScrollStateChangedy, puesto que está isItemSelectedconfigurado en true, se detendrá inmediatamente, antes de RecyclerViewtener la oportunidad, bueno ... de desplazarse.

Nota: para una mejor usabilidad, intente usar en GestureListenerlugar de OnClickListenerevitar accidentalclics.

Tim Jorjev
fuente
stopScroll()no es para congelar el desplazamiento, es solo para detener / detener el desplazamiento de la vista de reciclaje.
Farid
@FARID, sí, este método simplemente detiene cualquier desplazamiento en progreso, y en este caso particular, lo hace cada vez que un usuario intenta desplazar el contenido de RecyclerView con isItemSelectedset to true. De lo contrario, necesitaría un RecyclerView personalizado para deshabilitar el desplazamiento para siempre y quería evitarlo ya que solo necesitaba un poco de funcionalidad adicional, que proporciona esta solución.
Tim Jorjev
0

Simplemente agregue esto a su vista de reciclaje en xml

 android:nestedScrollingEnabled="false"

Me gusta esto

<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">
anas
fuente
0

Para detener el desplazamiento táctil, siga desplazándose mediante comandos:

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;
            }
        });
    }
Chico
fuente