Role o RecyclerView para mostrar o item selecionado no topo


215

Estou procurando uma maneira de rolar RecyclerViewpara mostrar o item selecionado no topo.

De uma maneira ListVieweu pude fazer isso usando scrollTo(x,y)e obtendo o topo do elemento que precisa ser centralizado.

Algo como:

@Override
public void onItemClick(View v, int pos){
    mylistView.scrollTo(0, v.getTop());
}

O problema é que RecyclerViewretorna um erro ao usar seu scrollTométodo dizendo

O RecyclerView não suporta rolagem para uma posição absoluta

Como posso rolar RecyclerViewpara colocar o item selecionado na parte superior da visualização?

Respostas:


405

Se você estiver usando o LinearLayoutManagerou Staggered GridLayoutManager, cada um deles terá um método scrollToPositionWithOffset que assume a posição e também o deslocamento do início do item desde o início do RecyclerView, o que parece realizar o que você precisa (configurando o deslocamento como 0 deve alinhar com o topo).

Por exemplo:

//Scroll item 2 to 20 pixels from the top
linearLayoutManager.scrollToPositionWithOffset(2, 20);

34
Se alguém precisar disso para restaurar a posição de rolagem de um RecyclerView, é assim que salvar as posições de rolagem (os dois argumentos para o método scrollToPositionWithOffset): int index = linearLayoutManager.findFirstVisibleItemPosition (); Visualizar v = linearLayoutManager.getChildAt (0); int top = (v == nulo)? 0: (v.getTop () - linearLayoutManager.getPaddingTop ());
DominicM 17/02

O que eu não entendo sobre isso é quando chamar scrollToPosition? se o ouvinte de seleção estiver nas visualizações individuais, como o objeto RecyclerView (que tem acesso ao seu gerenciador de layout que pode chamar scrolToPosition) será notificado de que um item está selecionado?
Nonos

@Nonos, você pode primeiro armazenar o LayoutManager usado em RecylerView.setLayoutManager () e depois acessá-lo diretamente. Assim: LinearLayoutManager llm = new LinearLayoutManager (this); mRecyclerView.setLayoutManager (llm); ... (posteriormente no código) ... llm.scrollToPositionWithOffset (2, 20);
Niraj

19
E se eu quiser rolar para o topo "sem problemas"?
Tiago

@Tiago, eu não tenho jogado ao redor com ele, mas esta série de 3 porções de artigos podem ajudar: blog.stylingandroid.com/scrolling-recyclerview-part-1
Niraj

93

Se você estiver procurando pelo LinearLayout Manager vertical, poderá obter uma rolagem suave usando um costume LinearSmoothScroller:

import android.content.Context;
import android.graphics.PointF;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.LinearSmoothScroller;
import android.support.v7.widget.RecyclerView;

public class SnappingLinearLayoutManager extends LinearLayoutManager {

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

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
                                       int position) {
        RecyclerView.SmoothScroller smoothScroller = new TopSnappedSmoothScroller(recyclerView.getContext());
        smoothScroller.setTargetPosition(position);
        startSmoothScroll(smoothScroller);
    }

    private class TopSnappedSmoothScroller extends LinearSmoothScroller {
        public TopSnappedSmoothScroller(Context context) {
            super(context);

        }

        @Override
        public PointF computeScrollVectorForPosition(int targetPosition) {
            return SnappingLinearLayoutManager.this
                    .computeScrollVectorForPosition(targetPosition);
        }

        @Override
        protected int getVerticalSnapPreference() {
            return SNAP_TO_START;
        }
    }
}

use uma instância do gerenciador de layout na visualização de reciclagem e, em seguida, a chamada recyclerView.smoothScrollToPosition(pos);suavizará a rolagem para a posição selecionada até o topo da visualização do reciclador


1
Resposta realmente útil! Também substituo getHorizontalSnapPreference()as listas TopSnappedSmoothScroller for Horizontal.
Juan Saravia

6
Estou certo ao observar que isso funciona apenas para itens que ainda têm o suficiente depois / abaixo deles para preencher o restante RecyclerView? O que significa dizer: Isto não parece trabalho para o último item - independentemente dos SNAP_TO_STARTos RecyclerViewúnicos pergaminhos até que o item se torna visível na parte inferior, mas não movê-lo todo o caminho até. Alguma idéia de como conseguir isso? (Eu tenho trabalhado em torno isso definindo estofamento personalizado no RecyclerView, mas que não parece muito certo ...)
david.mihola


em algum momento em que o item é focado, estou tendo um foco que pode ser desenhado para a exibição do item.
YLS 30/03

1
@ david.mihola Você encontrou o caminho 'certo'? Eu configurei estofamento personalizado para minha recyclerview mas eu estou tendo alguns problemas estranhos ...
bernardo.g


28

Você só precisa ligar recyclerview.scrollToPosition(position). Isso é bom!

Se você quiser chamá-lo no adaptador, deixe seu adaptador ter a instância de recyclerview ou a atividade ou fragmento que contém recyclerview, do que implementa o método getRecyclerview()neles.

Espero que possa ajudá-lo.


2
Isso funcionou para mim - recyclerView.scrollToPosition (0); me coloque no topo.
Raio Hunter

2
que não está funcionando, você deve usar o linearLayoutManager.scrollToPositionWithOffset (pos, 0);
Anil Ugale

@ Anil Ugale Isso é um absurdo. Claro que funciona. Você pode usar qualquer LayoutManager para um RecyclerView. Por que você deveria se preocupar mais com isso quando deseja rolar? Eu acho que você estava fazendo algo errado. Recyclerview.scrollToPosition (position) é um método de conveniência que chama LayoutManager.scrollToPosition (position).
A incrível Jan

1
@TheincredibleJan ele não funciona em todos os casos, o layoutManager geralmente é responsável pela rolagem de qualquer maneira apenas pelas suas informações.
Mohammed

11

mesmo com regulador de velocidade

public class SmoothScrollLinearLayoutManager extends LinearLayoutManager {
private static final float MILLISECONDS_PER_INCH = 110f;
private Context mContext;

public SmoothScrollLinearLayoutManager(Context context,int orientation, boolean reverseLayout) {
    super(context,orientation,reverseLayout);
    mContext = context;
}

@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
                                   int position) {
    RecyclerView.SmoothScroller smoothScroller = new TopSnappedSmoothScroller(recyclerView.getContext()){
        //This controls the direction in which smoothScroll looks for your view
        @Override
        public PointF computeScrollVectorForPosition(int targetPosition) {
            return new PointF(0, 1);
        }

        //This returns the milliseconds it takes to scroll one pixel.
        @Override
        protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
            return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
        }
    };
    smoothScroller.setTargetPosition(position);
    startSmoothScroll(smoothScroller);
}


private class TopSnappedSmoothScroller extends LinearSmoothScroller {
    public TopSnappedSmoothScroller(Context context) {
        super(context);

    }

    @Override
    public PointF computeScrollVectorForPosition(int targetPosition) {
        return SmoothScrollLinearLayoutManager.this
                .computeScrollVectorForPosition(targetPosition);
    }

    @Override
    protected int getVerticalSnapPreference() {
        return SNAP_TO_START;
    }
}
}

8

Experimente o que funcionou para mim legal!

Crie uma variável private static int displayedposition = 0;

Agora, para a posição do seu RecyclerView em sua atividade.

myRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);

                LinearLayoutManager llm = (LinearLayoutManager) myRecyclerView.getLayoutManager();


                displayedposition = llm.findFirstVisibleItemPosition();


            }
        });

Coloque essa declaração no local em que deseja que o site anterior seja exibido em sua exibição.

LinearLayoutManager llm = (LinearLayoutManager) mRecyclerView.getLayoutManager();
        llm.scrollToPositionWithOffset(displayedposition , youList.size());

Bem, é isso, funcionou bem para mim \ o /


7

basta chamar esse método simplesmente:

((LinearLayoutManager)recyclerView.getLayoutManager()).scrollToPositionWithOffset(yourItemPosition,0);

ao invés de:

recyclerView.scrollToPosition(yourItemPosition);

"ao invés de"? Distwo não mencionou "scrollToPosition". Se ele o tivesse usado, não haveria nenhum problema. scrollToPosition () como pretendido.
A incrível Jan

1
Estranhamente, o uso de scrollToPositionWithOffset funciona para mim e recyclerview.scrollToPosition não. Embora, eu pediria ao Amir para alterar o RecyclerView para recyclerview, porque dá a sensação de que os métodos são estáticos e não são.
Mohit 21/10

6

o que eu fiz para restaurar a posição de rolagem após atualizar o RecyclerView no botão clicou:

if (linearLayoutManager != null) {

    index = linearLayoutManager.findFirstVisibleItemPosition();
    View v = linearLayoutManager.getChildAt(0);
    top = (v == null) ? 0 : (v.getTop() - linearLayoutManager.getPaddingTop());
    Log.d("TAG", "visible position " + " " + index);
}

else{
    index = 0;
}

linearLayoutManager = new LinearLayoutManager(getApplicationContext());
linearLayoutManager.scrollToPositionWithOffset(index, top);

obtendo o deslocamento do primeiro item visível a partir do topo antes de criar o objeto linearLayoutManager e depois de instancia-lo, o scrollToPositionWithOffset do objeto LinearLayoutManager foi chamado.


6

Não sei por que não encontrei a melhor resposta, mas é realmente simples.

recyclerView.smoothScrollToPosition(position);

Sem erros

Cria animações


onde você colocará isso em atividade ou adaptador?
Arnold Brown

3

rolar na posição específica
e isso me ajudou muito. pelo ouvinte de clique, você pode obter a posição no seu adaptador

layoutmanager.scrollToPosition(int position);

3
If you want to scroll automatic without show scroll motion then you need to write following code:

**=> mRecyclerView.getLayoutManager().scrollToPosition(position);**

If you want to display scroll motion then you need to add following code.
=>Step 1: You need to declare SmoothScroller.

RecyclerView.SmoothScroller smoothScroller = new
                LinearSmoothScroller(this.getApplicationContext()) {
                    @Override
                    protected int getVerticalSnapPreference() {
                        return LinearSmoothScroller.SNAP_TO_START;
                    }
                };

=>step 2: You need to add this code any event you want to perform scroll to specific position.
=>First you need to set target position to SmoothScroller.

**smoothScroller.setTargetPosition(position);**

=>Then you need to set SmoothScroller to LayoutManager.
                        **mRecyclerView.getLayoutManager().startSmoothScroll(smoothScroller);**

3

Nenhuma das respostas explica como mostrar os últimos itens na parte superior. Portanto, as respostas funcionam apenas para itens que ainda possuem itens suficientes acima ou abaixo deles para preencher o restante RecyclerView. Por exemplo, se houver 59 elementos e um 56º elemento for selecionado, ele deverá estar no topo, como na figura abaixo:

RecyclerView - o elemento 56 está selecionado Nós poderíamos lidar com esses casos usando linearLayoutManager.scrollToPositionWithOffset(pos, 0)e uma lógica adicional no Adapterde RecyclerView- adicionando uma margem de costume abaixo do último artigo (se o último item não é visível, então isso significa que há espaço suficiente preenchimento RecyclerView). A margem personalizada pode ser uma diferença entre a altura da vista raiz e a altura do item. Portanto, seu Adapterpara RecyclerViewseria da seguinte maneira:

...
@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
    ...

    int bottomHeight = 0;
    int itemHeight = holder.itemView.getMeasuredHeight();
    // if it's the last item then add a bottom margin that is enough to bring it to the top
    if (position == mDataSet.length - 1) {
        bottomHeight = Math.max(0, mRootView.getMeasuredHeight() - itemHeight);
    }
    RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)holder.itemView.getLayoutParams();
    params.setMargins(0, 0, params.rightMargin, bottomHeight);
    holder.itemView.setLayoutParams(params);

    ...
} 
...

2

No meu caso, RecyclerViewtenho um top de estofamento como este

<android.support.v7.widget.RecyclerView
     ...
     android:paddingTop="100dp"
     android:clipToPadding="false"
/>

Então, para rolar um item para o topo, preciso

recyclerViewLinearLayoutManager.scrollToPositionWithOffset(position, -yourRecyclerView.getPaddingTop());

1

Se a sua LayoutManageré LinearLayoutManagervocê usa scrollToPositionWithOffset(position,0);nele e ele vai fazer o seu item o primeiro item visível na lista. Caso contrário, você pode usar smoothScrollToPositionno RecyclerViewdiretamente.

Acabei usando o código abaixo.

 RecyclerView.LayoutManager layoutManager = mainList.getLayoutManager();
        if (layoutManager instanceof LinearLayoutManager) {
            // Scroll to item and make it the first visible item of the list.
            ((LinearLayoutManager) layoutManager).scrollToPositionWithOffset(position, 0);
        } else {
            mainList.smoothScrollToPosition(position);
        }

-2

Eu uso o código abaixo para rolar suavemente um item (thisView) até o topo.
Também funciona para o GridLayoutManager com visualizações de diferentes alturas:

View firstView = mRecyclerView.getChildAt(0);
int toY = firstView.getTop();
int firstPosition = mRecyclerView.getChildAdapterPosition(firstView);
View thisView = mRecyclerView.getChildAt(thisPosition - firstPosition);
int fromY = thisView.getTop();

mRecyclerView.smoothScrollBy(0, fromY - toY);

Parece funcionar suficientemente bem para uma solução rápida.


1
Qual é o valor thisPosition?
23416 pRaNaY

é a posição que pretende suavizar rolagem para
Jan Rabe

Por que não usar simplesmente smoothScrollToPosition (int position) sem qualquer cálculo? Não importa de onde você começa, não é?
A incrível Jan
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.