RecyclerView
を使用してチャットアプリケーションを作成しようとしています。 LinearLayoutManager
とsetReverseLayout(true)
を使用しています。
一番下までスクロールすると(データセットの開始=最新のメッセージです)、新しいメッセージがデータセットに挿入されると、アイテムがリストの一番下に期待どおりに表示されます(ビューを上にスクロールしてスペースを確保します)新しいアイテムの場合)。
私が抱えている問題は、古いメッセージを表示するために上にスクロールしたときです。新しいメッセージがデータセットの先頭に挿入されると、ビューポートの範囲外であるためメッセージがレンダリングされない場合でも、ビューはメッセージの高さ約1だけ上にスクロールされます。
ビューを一番下までスクロールしたときにスクロール動作を維持し、古いメッセージまでスクロールしたときにそれを無効にするにはどうすればよいですか?
UPDATE:この問題が再現される小さなアプリも作成しました: https://github.com/ehehhh/RecyclerViewProblem =
UPDATE 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
メソッドを呼び出すだけです。
使用可能なアイテムが変更されていることがわかっている場合、通知データセット変更を使用したリサイクラービューでは冗長です。
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();
}
......
});
}
}