web-dev-qa-db-ja.com

挿入時のRecyclerViewのスクロール

RecyclerViewを使用してチャットアプリケーションを作成しようとしています。 LinearLayoutManagersetReverseLayout(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メソッドを呼び出すだけです。

18
ehehhh

使用可能なアイテムが変更されていることがわかっている場合、通知データセット変更を使用したリサイクラービューでは冗長です。

notifyItemInserted(position) 

この特定のケースでは、うまくいったのは

 notifyItemInserted(0);

または

 notifyItemRangeInserted(positionStart, newItems.size() - 1)

これにより、この範囲のビューのみが再バインドされます

チェック https://developer.Android.com/reference/Android/support/v7/widget/RecyclerView.Adapter.html#notifyItemInserted(int)

4
Hala.M

同様の問題がありました。私はチャットアプリケーションを作成していて、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();

            }

            ......


        });

    }



}
0
Nemus