Como desativar a rolagem do RecyclerView?


221

Não consigo desativar a rolagem no RecyclerView. Tentei ligar, rv.setEnabled(false)mas ainda posso rolar.

Como posso desativar a rolagem?


1
Qual é o sentido de usar RecyclerViewse você não deseja rolar?
— CommonsWare

16
@CommonsWare, eu só quero desativá-lo temporariamente, por exemplo, enquanto estou fazendo uma animação personalizada com um de seus filhos.
— Zsolt Safrany

1
Ah, tudo bem, isso faz sentido. Eu provavelmente colocaria um transparente Viewpor cima do RecyclerView, alternando entre VISIBLEe GONEconforme necessário, mas fora do manguito sua abordagem parece razoável.
— CommonsWare

Espero que isso ajude a encontrar uma solução melhor.
— Saiteja Parsi 24/09/2015

1
@ CommmonsWare, aqui está o que eu preciso, por exemplo. Preciso exibir imagens no RecyclerView uma de cada vez, sem imagens parcialmente visíveis, apenas uma na minha janela de exibição. E há setas à esquerda e à direita com as quais o usuário pode navegar. Dependendo da imagem exibida no momento (são de vários tipos), algumas coisas fora do RecyclerView são acionadas. É o design que nossos clientes desejam.
— Varvara Kalinina

Respostas:


368

Você deve substituir o gerenciador de layout da sua revisão de reciclagem por isso. Dessa forma, apenas desabilitará a rolagem, nenhuma das outras funcionalidades. Você ainda poderá lidar com cliques ou outros eventos de toque. Por exemplo:-

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

Aqui, usando o sinalizador "isScrollEnabled", você pode ativar / desativar a funcionalidade de rolagem da sua exibição de reciclagem temporariamente.

Além disso:

Simples substitua sua implementação existente para desativar a rolagem e permitir o clique.

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

2
Essa deve ser a resposta correta, pois desabilita a rolagem enquanto me permite clicar.
— Jared Burrows

10
Desabilitando a rolagem no LayoutMangers padrão já deve existir, um usuário da API não precisa fazer isso. Obrigado pela resposta!
— Martin O'Shea

1
E se eu quiser desativar a rolagem apenas de um determinado item? O que significa que você pode rolar para baixo (ou para cima), mas depois ficar bloqueado, até que eu o reative novamente?
— desenvolvedor android

2
Isto também desativa smoothScrollToPosition
— Mladen Rakonjac

4
você adiciona o objeto de versão do kotlin: LinearLayoutManager (this) {substitui fun canScrollVertically (): Boolean {return false}}
— amorenew

149

A resposta real é

recyclerView.setNestedScrollingEnabled(false);

Mais informações na documentação


39
Isso desativará apenas a rolagem aninhada, nem toda a rolagem. Em particular, se o RecyclerView estiver em um ScrollView, mas não preencher a tela, o ScrollView não rolará (o conteúdo cabe na tela) e você obterá a interface do usuário de rolagem no RecyclerView (efeito de fim de lista ao tentar rolar por exemplo ) mesmo que não role quando ficar maior. Estender o LayoutManager realmente faz o trabalho.
— que você

3
@ personne3000 Acredito que recyclerView.setHasFixedSize (true) faz o trabalho para isso. Edit: Eu também uso isso em conjunto com setFillViewport (true), por isso não tenho certeza de qual dos dois o corrige.
— MDroidd

5
Isso funciona bem para mim, mas meu "RecyclerView" estava dentro de um layout de Fragmento usando "ScrollView", então tive que alterar o "ScrollView" por "NestedScrollView", conforme descrito aqui: stackoverflow.com/a/38088902/618464
— educoutinho

1
Lembre-se de usar o nome da classe completo para NestedScrollView: android.support.v4.widget.NestedScrollView, como descrito aqui, por chessdork: stackoverflow.com/questions/37846245/...
— gustavoknz

Depois de passar quatro horas tentando descobrir o que está errado, isso resolveu meu problema. No meu caso, estou usando uma visão de reciclagem dentro de um MotionLayout. E adicionei apenas uma área de deslize para arrastar para cima adicionando movimento: touchAnchorId = "@ id / swipe_area". Funciona normalmente, mas também é acionado quando é tocado nas áreas vazias da reciclagem. Tenha cuidado ao usar o motionLayout com outros elementos passíveis de swip.
— O. Kumru

73

A resposta REAL REAL é: Para API 21 e superior:

Nenhum código java necessário. Você pode definir android:nestedScrollingEnabled="false" em 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
Eu acho que essa deve ser a resposta aceita. Sem nenhum esforço na aula, você pode desativar a rolagem e, no entanto, o toque é permitido no layout.
— VipiN Negi

O que para APIs abaixo de 21 ??
— Mouaad Abdelghafour AITALI

@pic para APIs mais baixas que você pode usar: recyclerView.setNestedScrollingEnabled (false);
— Milad Faridnia 30/06/19

1
Resposta aceita para mim.
— Fernando Torres

1
esta é a resposta melhor e mais direta, que também evita poluição desnecessária de código e padrões de extensão de classe sem fim.
— Raphael C

52

Esta solução é um pouco tola, mas funciona; você pode ativar / desativar a rolagem no RecyclerView.

Este é um RecyclerView.OnItemTouchListenerevento de roubo a cada toque, desabilitando o alvo 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) {

    }
}

Usando isso:

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
Isso desabilita o clique do item também?
— Jahir Fiquitiva

@JahirFiquitiva Sim. "Este é um RecyclerView.OnItemTouchListener vazio que rouba todos os eventos de toque"
— Max

1
isso também desativa o pergaminho de vista pai bem
— Jemshit Iskenderov

Isso desativa o clique do item também
— Muhammad Riyaz

1
Por que usar uma solução alternativa (com efeitos colaterais) quando existe uma solução limpa? Apenas substituir o LayoutManager (veja outras respostas)
— personne3000

37

Isso funciona para mim:

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

11
Isso desativa todos os toques.
— Jared Burrows

1
Bom truque! Para reativar novamente, basta redefinir o onTouchListener do onTouch para retornar false
— HendraWD 3/17/17

uhm, mas há um avisoCustom view 'RecyclerView' has setOnTouchListener called on it but does not override performClick
— HendraWD

Se você estiver recebendo o mesmo aviso que o @HendraWD mencionado no comentário acima, dê uma olhada nas respostas desta pergunta: stackoverflow.com/questions/46135249/…
— Nicolás Carrasco

1
Em vez de retornar true e, portanto, desativar todos os tipos de eventos de toque, apenas em return e.getAction() == MotionEvent.ACTION_MOVE;vez disso , apenas os eventos de return true;rolagem / deslize são cancelados.
— Toufic Batache

15

Você pode desativar a rolagem congelando o RecyclerView.

Congelar: recyclerView.setLayoutFrozen(true)

Para descongelar: recyclerView.setLayoutFrozen(false)


1
Observe: as visualizações filho não são atualizadas quando o RecyclerView está congelado. Preciso desativar a rolagem quando houver espaço suficiente na tela para mostrar todos os itens. Oh ... O que uma API agradável Android para esta tarefa ...
— Oleksandr Nos

1
Descontinuado na API 28
— OhhhThatVarun 14/09/19

13

Criar classe que estenda a 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();
    }
}

Isso desativará o evento de rolagem, mas não os eventos de clique

Use isso em seu XML, faça o seguinte:

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

Obrigado! Tudo isso é necessário quando queremos permitir que a exibição do reciclador tenha altura máxima de acordo com o conteúdo, sem rolagem interna. :)
— Rahul Rastogi

Mas se essa exibição do reciclador estiver em uma exibição de rolagem (por exemplo, ScrollView), o gerenciador de layout deve substituir o método canScrollVertically () retornando false a partir dela.
— Rahul Rastogi 21/0318

@RahulRastogi usar NestedScrollView vez de ScrollView
— Ashish

Hey @AshishMangave, Por favor, diga como ativá-lo novamente programaticamente
— Shruti

@ Sruti, não podemos fazer isso programaticamente. bcz que substituir diretamente recyclerview 's onMeasure method.you pode tentar para este recyclerView.setNestedScrollingEnabled (falsos) .Esta irá ajudá-lo
— Ashish

11

Se você apenas desativar a funcionalidade de rolagemRecyclerView , poderá usar o setLayoutFrozen(true);método de RecyclerView. Mas não pode ser desativado o evento de toque.

your_recyclerView.setLayoutFrozen(true);

esse método está obsoleto. você pode sugerir outro?
— Aiyaz Parmar 28/05/19

Descontinuado na API 28
— OhhhThatVarun 14/09/19

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


Isso interrompe a rolagem de smoothScrollToPosition () quando tocada.
— usar o seguinte código

Esta é a melhor solução para mim, pois permite rolar programaticamente, mas evita a rolagem do usuário.
— Miguel Sesma

7

Existe uma resposta muito simples.

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

O código acima desabilita a rolagem vertical do RecyclerView.



6

Escreveu uma versão do 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()

5

Estenda LayoutManagere substitua canScrollHorizontally()e canScrollVertically()para desativar a rolagem.

Esteja ciente de que a inserção de itens no início não voltará automaticamente para o início; para contornar isso, faça 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);
          }
        }
      }
    });
  }

1
Isso desativa também smoothScrollToPosition
— Mladen Rakonjac

5

Sei que isso já tem uma resposta aceita, mas a solução não leva em consideração um caso de uso que me deparei.

Eu precisava especificamente de um item de cabeçalho que ainda pudesse ser clicado, mas desabilitei o mecanismo de rolagem do RecyclerView. Isso pode ser feito com o seguinte 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) {

    }
});

Por algum motivo, você precisa clicar com o botão direito, sem precisar mover para que isso funcione.
— Jared Burrows

Bom ponto, @JaredBurrows. Isso ocorre porque estamos desconsiderando ACTION_MOVE. Para que o toque pareça natural, há alguns movimentos entre o toque e um leve movimento na tela. Infelizmente, não consegui resolver esse problema.
— Ryan Simon

Obrigado, exatamente o que eu precisava!
— Student

5

Como setLayoutFrozenfoi descontinuado, você pode desativar a rolagem congelando o RecyclerView usandosuppressLayout .

Congelar:

recyclerView.suppressLayout(true)

Para descongelar:

recyclerView.suppressLayout(false)

4

em XML: -

Você pode adicionar

android:nestedScrollingEnabled="false"

no arquivo XML de layout filho do RecyclerView

ou

em Java: -

childRecyclerView.setNestedScrollingEnabled(false);

ao seu RecyclerView em código Java.

Usando o ViewCompat (Java): -

childRecyclerView.setNestedScrollingEnabled(false);funcionará apenas em android_version> 21 dispositivos. para funcionar em todos os dispositivos, use o seguinte

ViewCompat.setNestedScrollingEnabled(childRecyclerView, false);


Excelentes, com todas as APIs cobertas, devem ser ligados.
— Ricard

3

Por alguma razão, a resposta do @Alejandro Gracia começa a funcionar apenas após alguns segundos. Encontrei uma solução que bloqueia o RecyclerView instantaneamente:

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

Substitua onTouchEvent () e onInterceptTouchEvent () e retorne false se você não precisar do OnItemTouchListener. Isso não desativa 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;
    }
}

3

Você pode adicionar esta linha depois de configurar seu adaptador

ViewCompat.setNestedScrollingEnabled(recyclerView, false);

Agora, a sua reciclagem funcionará com rolagem suave


2

Estou enfrentando problemas nesta questão há algumas horas. Por isso, gostaria de compartilhar minha experiência. Para a solução layoutManager, está bom, mas se você quiser reativar a rolagem, a recicladora voltará ao topo.

A melhor solução até agora (pelo menos para mim) é usar o método @Zsolt Safrany, mas adicionar getter e setter para que você não precise remover ou adicionar o OnItemTouchListener.

Como se 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

No Kotlin, se você não quiser criar uma classe extra apenas para definir um valor, poderá criar uma classe anônima no LayoutManager:

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

1

Aqui está como eu fiz isso com ligação de dados:

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

No lugar de "true", usei uma variável booleana que foi alterada com base em uma condição para que a exibição do reciclador alternasse entre ser desativado e ativado.


1

Me deparei com um fragmento que contém vários RecycleView, então eu só preciso de uma barra de rolagem em vez de uma barra de rolagem em cada RecycleView.

Então, basta colocar o ScrollView no contêiner pai que contém os 2 RecycleViews e usá-lo android:isScrollContainer="false"no 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

Adicionar

android:descendantFocusability="blocksDescendants"

em seu filho SrollView ou NestedScrollView (e pai de ListView, recyclerview e gridview qualquer um)


1

Existe uma maneira mais direta de desativar a rolagem (tecnicamente, é mais a interceptação de um evento de rolagem e a finalização quando uma condição é atendida), usando apenas a funcionalidade padrão. RecyclerViewtem o método chamado addOnScrollListener(OnScrollListener listener), e usando apenas isso, você pode impedir a rolagem, da seguinte maneira:

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: digamos que você deseja desativar a rolagem ao clicar em um dos itens dentro RecyclerViewpara poder executar algumas ações com ele, sem se distrair ao rolar acidentalmente para outro item e, quando terminar, basta clicar em o item novamente para ativar a rolagem. Para isso, você gostaria de anexar OnClickListenera todos os itens contidos nele RecyclerView; portanto, quando você clica em um item, ele alterna isItemSelectedde falsepara true. Dessa forma, quando você tentar rolar, RecyclerViewchamará automaticamente o método onScrollStateChangede, como isItemSelecteddefinido como true, será interrompido imediatamente, antes deRecyclerView ter a chance, bem ... de rolar.

Nota: para uma melhor usabilidade, tente usar em GestureListenervez de OnClickListenerimpedir accidentalcliques.


stopScroll()não é congelar o pergaminho, é apenas parar / parar a rolagem da reciclagem.
— Farid

@FARID, sim, esse método apenas interrompe qualquer rolagem em andamento e, nesse caso em particular, faz isso sempre que um usuário tenta rolar o conteúdo do RecyclerView com isItemSelecteddefinido como true. Caso contrário, você precisaria de um RecyclerView personalizado para desativar a rolagem definitiva e eu queria evitá-la, pois precisava de apenas algumas funcionalidades extras, fornecidas por esta solução.
— Tim Jorjev 02/06/19

0

Adicione isso à sua revisão de reciclagem em xml

 android:nestedScrollingEnabled="false"

como isso

<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

Para interromper a rolagem por toque, mas continue rolando através de 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;
            }
        });
    }
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.