Estou tendo um comportamento estranho de rolagem quando adiciono um RecyclerView dentro de um NestedScrollView.
O que acontece é que sempre que a rolagem tem mais linhas do que as mostradas na tela, assim que a atividade é iniciada, o NestedScrollView começa com um deslocamento da parte superior (imagem 1). Se houver poucos itens na exibição de rolagem para que todos possam ser exibidos de uma só vez, isso não acontece (imagem 2).
Estou usando a versão 23.2.0 da biblioteca de suporte.
Imagem 1 : ERRADO - começa com o deslocamento do topo
Imagem 2 : CORRETO - alguns itens na exibição do reciclador
Estou colando abaixo do meu código de layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="fill_vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Title:"
style="@style/TextAppearance.AppCompat.Caption"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/bodyPadding"
style="@style/TextAppearance.AppCompat.Body1"
android:text="Neque porro quisquam est qui dolorem ipsum"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Subtitle:"
style="@style/TextAppearance.AppCompat.Caption"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/TextAppearance.AppCompat.Body1"
android:padding="@dimen/bodyPadding"
android:text="Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit..."/>
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/rv"
android:focusable="false"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
Estou esquecendo de algo? Alguém tem alguma ideia de como resolver isto?
Atualização 1
Funciona corretamente se eu inserir o seguinte código ao inicializar minha Atividade:
sv.post(new Runnable() {
@Override
public void run() {
sv.scrollTo(0,0);
}
});
Onde sv é uma referência ao NestedScrollView, no entanto, parece um hack.
Atualização 2
Conforme solicitado, aqui está o código do meu adaptador:
public abstract class ArrayAdapter<T, VH extends RecyclerView.ViewHolder>
extends RecyclerView.Adapter<VH> {
private List<T> mObjects;
public ArrayAdapter(final List<T> objects) {
mObjects = objects;
}
/**
* Adds the specified object at the end of the array.
*
* @param object The object to add at the end of the array.
*/
public void add(final T object) {
mObjects.add(object);
notifyItemInserted(getItemCount() - 1);
}
/**
* Remove all elements from the list.
*/
public void clear() {
final int size = getItemCount();
mObjects.clear();
notifyItemRangeRemoved(0, size);
}
@Override
public int getItemCount() {
return mObjects.size();
}
public T getItem(final int position) {
return mObjects.get(position);
}
public long getItemId(final int position) {
return position;
}
/**
* Returns the position of the specified item in the array.
*
* @param item The item to retrieve the position of.
* @return The position of the specified item.
*/
public int getPosition(final T item) {
return mObjects.indexOf(item);
}
/**
* Inserts the specified object at the specified index in the array.
*
* @param object The object to insert into the array.
* @param index The index at which the object must be inserted.
*/
public void insert(final T object, int index) {
mObjects.add(index, object);
notifyItemInserted(index);
}
/**
* Removes the specified object from the array.
*
* @param object The object to remove.
*/
public void remove(T object) {
final int position = getPosition(object);
mObjects.remove(object);
notifyItemRemoved(position);
}
/**
* Sorts the content of this adapter using the specified comparator.
*
* @param comparator The comparator used to sort the objects contained in this adapter.
*/
public void sort(Comparator<? super T> comparator) {
Collections.sort(mObjects, comparator);
notifyItemRangeChanged(0, getItemCount());
}
}
E aqui está o meu ViewHolder:
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView txt;
public ViewHolder(View itemView) {
super(itemView);
txt = (TextView) itemView;
}
public void render(String text) {
txt.setText(text);
}
}
E aqui está o layout de cada item no RecyclerView (é apenas android.R.layout.simple_spinner_item
- essa tela serve apenas para mostrar um exemplo desse bug):
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
style="?android:attr/spinnerItemStyle"
android:singleLine="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:textAlignment="inherit"/>
android:focusableInTouchMode="false"
paraRecyclerView
? smth claramente está "forçando a sua disposição para ir para baixo ... (onde começar quando você tem 1295 itens de topo inferior ou apenas pequena compensação como primeira tela?)LayoutManager
Respostas:
Resolvi esse problema definindo:
na minha visão acima do RecyclerView (que estava oculto após rolagem indesejada). Tente definir essa propriedade como LinearLayout acima de RecyclerView ou LinearLayout, que é o contêiner de RecyclerView (me ajudou em outro caso).
Como vejo na fonte NestedScrollView, ele tenta focar o primeiro filho possível em onRequestFocusInDescendants e, se apenas o RecyclerView estiver focável, ele vence.
Editar (graças a Waran): e para uma rolagem suave, não se esqueça de definir
yourRecyclerView.setNestedScrollingEnabled(false);
fonte
android:focusableInTouchMode="false"
o recyclerView para não precisar definir true para todas as outras visualizações.No seu
LinearLayout
imediatoNestedScrollView
, useandroid:descendantFocusability
da seguinte maneiraEDITAR
Uma vez que muitos deles obtêm essa resposta útil, também fornecerão explicações.
O uso de
descendantFocusability
é dado aqui . E a partir defocusableInTouchMode
cima aqui . Portanto, usarblocksDescendants
indescendantFocusability
não permite que a criança obtenha foco enquanto toca e, portanto, o comportamento não planejado pode ser interrompido.Quanto a
focusInTouchMode
,AbsListView
eRecyclerView
chama o métodosetFocusableInTouchMode(true);
em seu construtor por padrão, portanto, não é necessário usar esse atributo em seus layouts XML.E para o
NestedScrollView
seguinte método é usado:Aqui, o
setFocusable()
método é usado em vez desetFocusableInTouchMode()
. Mas, de acordo com este post ,focusableInTouchMode
deve ser evitado, a menos que em determinadas condições, pois quebra a consistência com o comportamento normal do Android. Um jogo é um bom exemplo de aplicativo que pode fazer bom uso da propriedade focalizável no modo de toque. O MapView, se usado em tela cheia como no Google Maps, é outro bom exemplo de onde você pode usar o foco no modo de toque corretamente.fonte
blocksDescendants
bloquear o foco de todos os descendentes. Não tenho a certeza, mas tentebeforeDescendants
recyclerView.setFocusable(false); nestedScrollView.requestFocus();
inside LinearLayout Trabalhou para mim.
fonte
Eu tive o mesmo problema e resolvi estendendo o NestedScrollView e desativando o foco em crianças. Por alguma razão, o RecyclerView sempre solicitava foco, mesmo quando eu apenas abria e fechava a gaveta.
fonte
RecyclerView
suportes.No meu caso, esse código resolve meu problema
Meu layout contém um layout pai como NestedScrollView, que possui um LinearLayout filho. O LinearLayout tem a orientação "vertical" e os filhos RecyclerView e EditText. Referência
fonte
Eu tenho dois palpites.
Primeiro: tente colocar esta linha no seu NestedScrollView
Segundo: Uso
como seu pai vê assim
Minha última solução possível. Eu juro :)
fonte
Esse problema ocorre devido à reciclagem do foco da visualização.
Automaticamente todo o foco foi reciclado para a visualização se seu tamanho aumentasse o tamanho da tela.
adicionando
android:focusableInTouchMode="true"
a primeira ChildView comoTextView
,Button
e assim por diante (não emViewGroup
comoLinear
,Relative
e assim por diante) faz sentido para resolver o problema, mas Nível API 25 e acima solução não funciona.Basta adicionar estas 2 linhas no seu ChildView como
TextView
,Button
e assim por diante (não emViewGroup
comoLinear
,Relative
e assim por diante)Acabei de enfrentar esse problema no nível 25 da API. Espero que outras pessoas não percam tempo nisso.
Para uma rolagem suave ao reciclar, adicione esta linha
mas adicionar esses atributos só funciona com o nível 21 da API ou superior. Se você deseja que a rolagem de suavização funcione abaixo do nível 25 da API, adicione esta linha à sua classe
fonte
No código Java, após inicializar o recyclerView e configurar o adaptador, inclua esta linha:
Você também pode tentar agrupar o layout com um relativoLayout para que as visualizações permaneçam na mesma posição, mas recyclerView (qual rolagem) é o primeiro na hierarquia xml. A última sugestão, uma tentativa desesperada: p
fonte
Como estou atrasado em responder, mas pode ajudar outra pessoa. Basta usar a versão abaixo ou superior no build.gradle no nível do aplicativo e o problema será removido.
fonte
para rolar até o topo, basta chamar isso em
setcontentview
:fonte
Basta adicionar
android:descendantFocusability="blocksDescendants"
o ViewGroup dentro do NestedScrollView.fonte