Прокрутка RecyclerView на вставке

Я пытаюсь использовать RecyclerView для создания приложения чата. Я использую LinearLayoutManager с setReverseLayout(true).

Когда я прокручиваюсь до конца (это начало набора данных = новое сообщение), и новое сообщение вставляется в набор данных, элемент отображается в нижней части списка, как и ожидалось (просмотр прокручивается вверх чтобы освободить место для нового предмета).

Проблема у меня есть, когда я прокручиваю вверх, чтобы увидеть старые сообщения. Когда новое сообщение вставлено в начало набора данных, представление прокручивается примерно на одну высоту сообщения, даже если это сообщение даже не отображается, поскольку оно выходит за пределы области просмотра.

Как сохранить поведение прокрутки, когда представление прокручивается вниз, но отключите его, когда я прокрутил старые сообщения?

UPDATE: Я также сделал небольшое приложение, в котором эта проблема воспроизводится: 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);

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 в адаптере.

Ответы

Ответ 1

в представлении Recycler с использованием notifyDataSetChanged является избыточным, если вы знаете, что элементы изменены вы можете использовать

notifyItemInserted(position) 

в этом конкретном случае работала

 notifyItemInserted(0);

или

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

это приведет только к повторному просмотру представлений в этом диапазоне

проверить https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#notifyItemInserted(int)

Ответ 2

У меня была аналогичная проблема. Я делал чат-приложение, и мой 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();

            }

            ......


        });

    }



}