Как обновить данные, отображаемые в RecyclerView
(вызывая notifyDataSetChanged
его адаптер), и убедиться, что положение прокрутки сброшено точно туда, где оно было?
В случае старого доброго ListView
все, что требуется, - это получение getChildAt(0)
, проверка getTop()
и setSelectionFromTop
последующий вызов с теми же точными данными.
Это кажется невозможным в случае RecyclerView
.
Я предполагаю, что я должен использовать его, LayoutManager
который действительно обеспечивает scrollToPositionWithOffset(int position, int offset)
, но как правильно получить позицию и смещение?
layoutManager.findFirstVisibleItemPosition()
и layoutManager.getChildAt(0).getTop()
?
Или есть более элегантный способ выполнить работу?
android
android-recyclerview
Конрад Моравски
источник
источник
Ответы:
Я использую этот. ^ _ ^
// Save state private Parcelable recyclerViewState; recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState(); // Restore state recyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);
Это проще, надеюсь, вам это поможет!
источник
У меня очень похожая проблема. И я придумал следующее решение.
Использование
notifyDataSetChanged
- плохая идея. Вы должны быть более конкретными, тогдаRecyclerView
вы сохраните состояние прокрутки.Например, если вам нужно только обновить или, другими словами, вы хотите, чтобы каждое представление было перекомпоновано, просто сделайте следующее:
adapter.notifyItemRangeChanged(0, adapter.getItemCount());
источник
notifyDataSetChanged
позиция прокрутки изменилась, показывая новые элементы. Однако, если использовать сохраняет состояние прокрутки.notifyItemRangeInserted(0, newItems.size() - 1)
RecyclerView
РЕДАКТИРОВАТЬ: Чтобы восстановить точно такое же видимое положение, как в, чтобы оно выглядело точно так, как оно было, нам нужно сделать что-то немного другое (см. Ниже, как восстановить точное значение scrollY):
Сохраните положение и смещение следующим образом:
LinearLayoutManager manager = (LinearLayoutManager) mRecycler.getLayoutManager(); int firstItem = manager.findFirstVisibleItemPosition(); View firstItemView = manager.findViewByPosition(firstItem); float topOffset = firstItemView.getTop(); outState.putInt(ARGS_SCROLL_POS, firstItem); outState.putFloat(ARGS_SCROLL_OFFSET, topOffset);
А затем восстановите свиток вот так:
LinearLayoutManager manager = (LinearLayoutManager) mRecycler.getLayoutManager(); manager.scrollToPositionWithOffset(mStatePos, (int) mStateOffset);
Это восстанавливает список в его точное видимое положение. Очевидно, потому что он будет выглядеть одинаково для пользователя, но у него не будет такого же значения scrollY (из-за возможных различий в размерах макета альбомной / книжной ориентации).
Обратите внимание, что это работает только с LinearLayoutManager.
--- Ниже показано, как восстановить точный scrollY, который, вероятно, изменит внешний вид списка ---
Примените OnScrollListener следующим образом:
private int mScrollY; private RecyclerView.OnScrollListener mTotalScrollListener = new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); mScrollY += dy; } };
При этом в mScrollY всегда будет сохраняться точное положение прокрутки.
Сохраните эту переменную в своем Bundle и восстановите ее при восстановлении состояния в другую переменную , мы назовем ее mStateScrollY.
После восстановления состояния и после того, как ваш RecyclerView сбросил все свои данные, сбросьте прокрутку следующим образом:
mRecyclerView.scrollBy(0, mStateScrollY);
Вот и все.
Помните, что вы восстанавливаете прокрутку для другой переменной, это важно, потому что OnScrollListener будет вызываться с помощью .scrollBy () и впоследствии установит mScrollY на значение, хранящееся в mStateScrollY. Если вы этого не сделаете, mScrollY будет иметь двойное значение прокрутки (потому что OnScrollListener работает с дельтами, а не с абсолютной прокруткой).
Экономия состояния в действиях может быть достигнута так:
@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt(ARGS_SCROLL_Y, mScrollY); }
И для восстановления вызовите это в своем onCreate ():
if(savedState != null){ mStateScrollY = savedState.getInt(ARGS_SCROLL_Y, 0); }
Сохранение состояния во фрагментах работает аналогичным образом, но фактическое сохранение состояния требует немного дополнительной работы, но есть много статей, посвященных этому, поэтому у вас не должно возникнуть проблем с выяснением того, как, принципы сохранения scrollY и восстановление остается прежним.
источник
Да, вы можете решить эту проблему, создав конструктор адаптера только один раз, здесь я объясняю часть кодирования:
if (appointmentListAdapter == null) { appointmentListAdapter = new AppointmentListAdapter(AppointmentsActivity.this); appointmentListAdapter.addAppointmentListData(appointmentList); appointmentListAdapter.setOnStatusChangeListener(onStatusChangeListener); appointmentRecyclerView.setAdapter(appointmentListAdapter); } else { appointmentListAdapter.addAppointmentListData(appointmentList); appointmentListAdapter.notifyDataSetChanged(); }
Теперь вы можете видеть, что я проверил, является ли адаптер нулевым или нет, и инициализируется только тогда, когда он равен нулю.
Если адаптер не равен нулю, то я уверен, что я инициализировал свой адаптер хотя бы один раз.
Поэтому я просто добавлю список в адаптер и вызову notifydatasetchanged.
RecyclerView всегда содержит последнюю прокрученную позицию, поэтому вам не нужно сохранять последнюю позицию, просто вызовите notifydatasetchanged, просмотр recycler всегда обновляет данные, не переходя наверх.
Спасибо, удачного кодирования
источник
Сохраняйте позицию прокрутки, используя ответ @DawnYu, чтобы обернуть его
notifyDataSetChanged()
следующим образом:val recyclerViewState = recyclerView.layoutManager?.onSaveInstanceState() adapter.notifyDataSetChanged() recyclerView.layoutManager?.onRestoreInstanceState(recyclerViewState)
источник
Верхний ответ от @DawnYu работает, но recyclerview сначала прокрутится вверх, а затем вернется к предполагаемой позиции прокрутки, вызывая реакцию «мерцание», что не очень приятно.
Чтобы обновить recyclerView, особенно после выхода из другого действия, без мерцания и сохранения положения прокрутки, вам необходимо сделать следующее.
Надеюсь это поможет.
источник
Я не использовал Recyclerview, но сделал это в ListView. Пример кода в Recyclerview:
setOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { rowPos = mLayoutManager.findFirstVisibleItemPosition();
Это слушатель, когда пользователь прокручивает. Накладные расходы на производительность незначительны. Таким образом, первая видимая позиция будет точной.
источник
findFirstVisibleItemPosition
возвращает «позицию адаптера первого видимого вида», но как насчет смещения.mMessageAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { @Override public void onChanged() { mLayoutManager.smoothScrollToPosition(mMessageRecycler, null, mMessageAdapter.getItemCount()); } });
Решение здесь - продолжать прокрутку recyclerview, когда приходит новое сообщение.
Метод onChanged () обнаруживает действие, выполняемое с recyclerview.
источник
У меня это работает в Котлине.
class LEDRecyclerAdapter (var currentPole: Pole): RecyclerView.Adapter<RecyclerView.ViewHolder>() { ... }
Смещение прокрутки не меняется.
источник
У меня была эта проблема со списком элементов, у каждого из которых было время в минутах, пока они не были «выполнены» и нуждались в обновлении. Я бы обновил данные, а затем позвонил
orderAdapter.notifyDataSetChanged();
и каждый раз она прокручивалась вверх. Я заменил это на
for(int i = 0; i < orderArrayList.size(); i++){ orderAdapter.notifyItemChanged(i); }
и это было нормально. Ни один из других методов в этой теме не помог мне. Однако при использовании этого метода каждый отдельный элемент мигал при его обновлении, поэтому мне также пришлось поместить это в родительский фрагмент onCreateView.
RecyclerView.ItemAnimator animator = orderRecycler.getItemAnimator(); if (animator instanceof SimpleItemAnimator) { ((SimpleItemAnimator) animator).setSupportsChangeAnimations(false); }
источник
Если у вас есть один или несколько EditTexts внутри элементов recyclerview, отключите их автофокус, поместив эту конфигурацию в родительский вид recyclerview:
android:focusable="true" android:focusableInTouchMode="true"
У меня была эта проблема, когда я начал другое действие, запущенное из элемента recyclerview, когда я вернулся и установил обновление одного поля в одном элементе с помощью notifyItemChanged (position), прокрутка RV перемещается, и я пришел к выводу, что автофокус EditText Элементы, приведенный выше код решил мою проблему.
Лучший.
источник
Просто вернитесь, если oldPosition и position совпадают;
private int oldPosition = -1; public void notifyItemSetChanged(int position, boolean hasDownloaded) { if (oldPosition == position) { return; } oldPosition = position; RLog.d(TAG, " notifyItemSetChanged :: " + position); DBMessageModel m = mMessages.get(position); m.setVideoHasDownloaded(hasDownloaded); notifyItemChanged(position, m); }
источник