RecyclerView 在插入时滚动
RecyclerView scrolling on insert
我正在尝试使用 RecyclerView
创建一个聊天应用程序。我正在使用 LinearLayoutManager
和 setReverseLayout(true)
。
当我一直滚动到底部(即数据集开始 = 最新消息)并将新消息插入数据集时,该项目按预期出现在列表底部(视图是向上滚动以为新项目腾出空间)。
我遇到的问题是当我向上滚动查看旧消息时。当一条新消息插入到数据集的开头时,视图会向上滚动大约一个消息高度,即使该消息甚至没有呈现,因为它超出了视口的范围。
如何在视图滚动到底部时保持滚动行为,但在滚动到较旧的消息时禁用它?
更新:
我还做了一个小程序,重现了这个问题:
https://github.com/ehehhh/RecyclerViewProblem
更新 2: 我提交了对我所做的回购有效的修复。
相关代码(希望):
compile 'com.android.support:recyclerview-v7:24.2.0'
XML:
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="8dp"
android:paddingTop="8dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
RecyclerView 初始化代码:
layoutManager = new LinearLayoutManager(context);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
layoutManager.setReverseLayout(true);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setScrollContainer(true);
recyclerView.setLayoutAnimation(null);
recyclerView.setItemAnimator(null);
adapter = new ChatAdapter(...);
recyclerView.setAdapter(adapter);
适配器:
public class ChatAdapter extends RecyclerView.Adapter<ChatViewHolder> {
private List<MessageWrapper> dataset;
public ChatAdapter(List<MessageWrapper> dataset, ...) {
this.dataset = dataset;
setHasStableIds(true);
}
...
@Override
public long getItemId(int position) {
return dataset.get(position).getId();
}
@Override
public int getItemCount() {
return dataset.size();
}
public void datasetChanged(List<MessageWrapper> dataset) {
this.dataset = dataset;
notifyDataSetChanged();
}
}
当一个新项目被添加到数据集时,我只是在适配器中调用 datasetChanged
方法。
如果您知道项目已更改,则在 Recycler 视图中使用 notifyDataSetChanged 是多余的
你可以使用
notifyItemInserted(position)
在这种特殊情况下,起作用的是
notifyItemInserted(0);
或
notifyItemRangeInserted(positionStart, newItems.size() - 1)
这只会重新绑定此范围内的视图
我遇到了类似的问题。我正在制作聊天应用程序,我的 RecyclerView 总是从顶部显示行,我希望它转到底部以显示最后插入的消息。这对我有用,recyclerView.scrollToPosition(messages.size()-1)
添加到 ChildEventListener
:
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private MessageAdapter mMessageAdapter;
private List<Message> messages;
private FirebaseDatabase mFirebaseDatabase;
private DatabaseReference mMessagesDatabaseRef;
....
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mFirebaseDatabase = FirebaseDatabase.getInstance();
mMessagesDatabaseRef = mFirebaseDatabase.getReference().child("messages");
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
// Initialize message ListView and its adapter
messages = new ArrayList<>();
mMessageAdapter = new MessageAdapter(messages);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setAdapter(mMessageAdapter);
.....
mMessagesDatabaseRef.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
messages.add(dataSnapshot.getValue(Message.class));
recyclerView.scrollToPosition(messages.size()-1);
mMessageAdapter.notifyDataSetChanged();
}
......
});
}
}
我正在尝试使用 RecyclerView
创建一个聊天应用程序。我正在使用 LinearLayoutManager
和 setReverseLayout(true)
。
当我一直滚动到底部(即数据集开始 = 最新消息)并将新消息插入数据集时,该项目按预期出现在列表底部(视图是向上滚动以为新项目腾出空间)。
我遇到的问题是当我向上滚动查看旧消息时。当一条新消息插入到数据集的开头时,视图会向上滚动大约一个消息高度,即使该消息甚至没有呈现,因为它超出了视口的范围。
如何在视图滚动到底部时保持滚动行为,但在滚动到较旧的消息时禁用它?
更新: 我还做了一个小程序,重现了这个问题: https://github.com/ehehhh/RecyclerViewProblem
更新 2: 我提交了对我所做的回购有效的修复。
相关代码(希望):
compile 'com.android.support:recyclerview-v7:24.2.0'
XML:
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="8dp"
android:paddingTop="8dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
RecyclerView 初始化代码:
layoutManager = new LinearLayoutManager(context);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
layoutManager.setReverseLayout(true);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setScrollContainer(true);
recyclerView.setLayoutAnimation(null);
recyclerView.setItemAnimator(null);
adapter = new ChatAdapter(...);
recyclerView.setAdapter(adapter);
适配器:
public class ChatAdapter extends RecyclerView.Adapter<ChatViewHolder> {
private List<MessageWrapper> dataset;
public ChatAdapter(List<MessageWrapper> dataset, ...) {
this.dataset = dataset;
setHasStableIds(true);
}
...
@Override
public long getItemId(int position) {
return dataset.get(position).getId();
}
@Override
public int getItemCount() {
return dataset.size();
}
public void datasetChanged(List<MessageWrapper> dataset) {
this.dataset = dataset;
notifyDataSetChanged();
}
}
当一个新项目被添加到数据集时,我只是在适配器中调用 datasetChanged
方法。
如果您知道项目已更改,则在 Recycler 视图中使用 notifyDataSetChanged 是多余的 你可以使用
notifyItemInserted(position)
在这种特殊情况下,起作用的是
notifyItemInserted(0);
或
notifyItemRangeInserted(positionStart, newItems.size() - 1)
这只会重新绑定此范围内的视图
我遇到了类似的问题。我正在制作聊天应用程序,我的 RecyclerView 总是从顶部显示行,我希望它转到底部以显示最后插入的消息。这对我有用,recyclerView.scrollToPosition(messages.size()-1)
添加到 ChildEventListener
:
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private MessageAdapter mMessageAdapter;
private List<Message> messages;
private FirebaseDatabase mFirebaseDatabase;
private DatabaseReference mMessagesDatabaseRef;
....
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mFirebaseDatabase = FirebaseDatabase.getInstance();
mMessagesDatabaseRef = mFirebaseDatabase.getReference().child("messages");
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
// Initialize message ListView and its adapter
messages = new ArrayList<>();
mMessageAdapter = new MessageAdapter(messages);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setAdapter(mMessageAdapter);
.....
mMessagesDatabaseRef.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
messages.add(dataSnapshot.getValue(Message.class));
recyclerView.scrollToPosition(messages.size()-1);
mMessageAdapter.notifyDataSetChanged();
}
......
});
}
}