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?

Zsolt Safrany
fonte
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;
 }
};
Saurabh Garg
fonte
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

Bozic Nebojsa
fonte
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">
Milad Faridnia
fonte
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 
Zsolt Safrany
fonte
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;
      }
  });
alxgarcia
fonte
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)

Virat Singh
fonte
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 
     ...
     ... 
  />
Ashish
fonte
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);
Ekta Bhawsar
fonte
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;
        }
    });

rajesh
fonte
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.

Emi Raz
fonte
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()
Eu sou um dragão sapo
fonte
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);
          }
        }
      }
    });
  }
Aidanvii
fonte
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) {

    }
});
Ryan Simon
fonte
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)
Shylendra Madda
fonte
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);

jafarbtech
fonte
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) {
            }
        });
vovahost
fonte
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;
    }
}
ypresto
fonte
3

Você pode adicionar esta linha depois de configurar seu adaptador

ViewCompat.setNestedScrollingEnabled(recyclerView, false);

Agora, a sua reciclagem funcionará com rolagem suave

Atefeh Srch
fonte
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);
Aladim
fonte
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
}
Micer
fonte
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.

bwhite
fonte
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" />
pk028382
fonte
1

Adicionar

android:descendantFocusability="blocksDescendants"

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

Pankaj Talaviya
fonte
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.

Tim Jorjev
fonte
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">
anas
fonte
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;
            }
        });
    }
Cara
fonte