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?

Distwo
fonte

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);
Niraj
fonte
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

nizammoidu
fonte
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
1
O mesmo no Kotlin => gist.github.com/Kevinrob/4f32a6b971930cdc49f06be8dce6403c
Kevin Robatel
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
52

// Pos. Item de rolagem

linearLayoutManager.scrollToPositionWithOffset(pos, 0);
HBQ
fonte
7
existe alguma maneira de fazer isso sem problemas?
Ashkan
1
@ MarkBuikema eu encontrei isso antes. thanx para sua cara atenção
Ashkan
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.

Zero
fonte
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;
    }
}
}
user2212515
fonte
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 /

user3740244
fonte
7

basta chamar esse método simplesmente:

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

ao invés de:

recyclerView.scrollToPosition(yourItemPosition);
Amir Hossein Ghasemi
fonte
"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.

tsiro
fonte
6

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

recyclerView.smoothScrollToPosition(position);

Sem erros

Cria animações

Lucem
fonte
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);
Sandeep Singh
fonte
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);**
Paras Santoki
fonte
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);

    ...
} 
...
Anatolii
fonte
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());
Phan Van Linh
fonte
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);
        }
Mohamed Nageh
fonte
-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.

feheren.fekete
fonte
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