W jaki sposób można odświeżyć dane wyświetlane w programie RecyclerView
(wywołując notifyDataSetChanged
jego adapter) i upewnić się, że pozycja przewijania jest resetowana dokładnie do miejsca, w którym się znajdowała?
W przypadku starego dobrego ListView
wystarczy pobrać getChildAt(0)
, sprawdzić getTop()
i wywołać setSelectionFromTop
później te same dokładne dane.
Wydaje się, że nie jest to możliwe w przypadku RecyclerView
.
Myślę, że powinienem użyć tego, LayoutManager
co rzeczywiście zapewnia scrollToPositionWithOffset(int position, int offset)
, ale jaki jest właściwy sposób na odzyskanie pozycji i przesunięcia?
layoutManager.findFirstVisibleItemPosition()
i layoutManager.getChildAt(0).getTop()
?
A może istnieje bardziej elegancki sposób wykonania pracy?
źródło
Odpowiedzi:
Używam tego. ^ _ ^
// Save state private Parcelable recyclerViewState; recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState(); // Restore state recyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);
To jest prostsze, mam nadzieję, że ci pomoże!
źródło
Mam dość podobny problem. I wymyśliłem następujące rozwiązanie.
Używanie
notifyDataSetChanged
to zły pomysł. Powinieneś być bardziej szczegółowy, a następnieRecyclerView
zapiszesz stan przewijania.Na przykład, jeśli potrzebujesz tylko odświeżyć, lub innymi słowy, chcesz, aby każdy widok został ponownie powiązany, po prostu zrób to:
adapter.notifyItemRangeChanged(0, adapter.getItemCount());
źródło
notifyDataSetChanged
z pozycją przewijania zmieniłem się, pokazując nowe elementy. Jednak, jeśli używać utrzymuje stan przewijania.notifyItemRangeInserted(0, newItems.size() - 1)
RecyclerView
EDYCJA: Aby przywrócić dokładnie tę samą widoczną pozycję, jak w programie, aby wyglądała dokładnie tak, jak była, musimy zrobić coś innego (zobacz poniżej, jak przywrócić dokładną wartość scrollY):
Zapisz pozycję i przesunięcie w następujący sposób:
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);
A następnie przywróć zwój w ten sposób:
LinearLayoutManager manager = (LinearLayoutManager) mRecycler.getLayoutManager(); manager.scrollToPositionWithOffset(mStatePos, (int) mStateOffset);
Spowoduje to przywrócenie listy do dokładnej widocznej pozycji. Pozorny, ponieważ będzie wyglądał tak samo dla użytkownika, ale nie będzie miał takiej samej wartości scrollY (z powodu możliwych różnic w wymiarach układu poziomego / pionowego).
Zauważ, że działa to tylko z LinearLayoutManager.
--- Poniżej jak przywrócić dokładne scrollY, co prawdopodobnie sprawi, że lista będzie wyglądać inaczej ---
Zastosuj OnScrollListener w następujący sposób:
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; } };
Spowoduje to zapisanie dokładnej pozycji przewijania przez cały czas w mScrollY.
Przechowuj tę zmienną w swoim pakiecie i przywróć ją podczas przywracania stanu do innej zmiennej , nazwiemy ją mStateScrollY.
Po przywróceniu stanu i po zresetowaniu wszystkich danych przez RecyclerView zresetuj przewijanie za pomocą tego:
mRecyclerView.scrollBy(0, mStateScrollY);
Otóż to.
Uważaj, przywracasz przewijanie do innej zmiennej, jest to ważne, ponieważ OnScrollListener zostanie wywołany z .scrollBy (), a następnie ustawi mScrollY na wartość przechowywaną w mStateScrollY. Jeśli tego nie zrobisz, mScrollY będzie miał podwójną wartość przewijania (ponieważ OnScrollListener działa z deltami, a nie bezwzględnymi przewijaniami).
Oszczędności państwa w działaniach można osiągnąć w następujący sposób:
@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt(ARGS_SCROLL_Y, mScrollY); }
Aby przywrócić, wywołaj to w swoim onCreate ():
if(savedState != null){ mStateScrollY = savedState.getInt(ARGS_SCROLL_Y, 0); }
Zapisywanie stanu we fragmentach działa w podobny sposób, ale faktyczne zapisywanie stanu wymaga trochę dodatkowej pracy, ale jest wiele artykułów na ten temat, więc nie powinieneś mieć problemu z ustaleniem jak, zasady zapisywania scrollY a przywrócenie pozostaje takie samo.
źródło
Tak, możesz rozwiązać ten problem, tworząc konstruktor adaptera tylko raz, wyjaśniam część dotyczącą kodowania tutaj:
if (appointmentListAdapter == null) { appointmentListAdapter = new AppointmentListAdapter(AppointmentsActivity.this); appointmentListAdapter.addAppointmentListData(appointmentList); appointmentListAdapter.setOnStatusChangeListener(onStatusChangeListener); appointmentRecyclerView.setAdapter(appointmentListAdapter); } else { appointmentListAdapter.addAppointmentListData(appointmentList); appointmentListAdapter.notifyDataSetChanged(); }
Teraz widać, że sprawdziłem, czy adapter jest pusty, czy nie, i inicjuję tylko wtedy, gdy jest pusty.
Jeśli karta nie jest zerowa, mam pewność, że zainicjowałem swój adapter przynajmniej raz.
Więc po prostu dodam listę do adaptera i wywołam notifydatasetchanged.
RecyclerView zawsze przechowuje przewijaną ostatnią pozycję, dlatego nie musisz przechowywać ostatniej pozycji, po prostu wywołaj notifydatasetchanged, widok recyklera zawsze odświeża dane bez przechodzenia do góry.
Dzięki, szczęśliwego kodowania
źródło
Zachowaj pozycję przewijania, używając odpowiedzi @DawnYu, aby zawijać w
notifyDataSetChanged()
ten sposób:val recyclerViewState = recyclerView.layoutManager?.onSaveInstanceState() adapter.notifyDataSetChanged() recyclerView.layoutManager?.onRestoreInstanceState(recyclerViewState)
źródło
Najlepsza odpowiedź od @DawnYu działa, ale widok recyklingu najpierw przewinie się do góry, a następnie wróci do zamierzonej pozycji przewijania, powodując reakcję „migotania”, która nie jest przyjemna.
Aby odświeżyć widok recyklingu, szczególnie po przejściu z innego działania, bez migotania i utrzymywania pozycji przewijania, należy wykonać następujące czynności.
Mam nadzieję że to pomoże.
źródło
Nie korzystałem z Recyclerview, ale zrobiłem to na ListView. Przykładowy kod w Recyclerview:
setOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { rowPos = mLayoutManager.findFirstVisibleItemPosition();
Jest to słuchacz, gdy użytkownik przewija. Narzut wydajności nie jest znaczący. I w ten sposób pierwsza widoczna pozycja jest dokładna.
źródło
findFirstVisibleItemPosition
zwraca „pozycję adaptera pierwszego widocznego widoku”, ale co z przesunięciem.mMessageAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { @Override public void onChanged() { mLayoutManager.smoothScrollToPosition(mMessageRecycler, null, mMessageAdapter.getItemCount()); } });
Rozwiązaniem jest tutaj przewijanie widoku recyklingu, gdy nadejdzie nowa wiadomość.
Metoda onChanged () wykrywa akcję wykonywaną na recyclinglerview.
źródło
To działa dla mnie w Kotlinie.
class LEDRecyclerAdapter (var currentPole: Pole): RecyclerView.Adapter<RecyclerView.ViewHolder>() { ... }
Przesunięcie przewijania nie zmienia się.
źródło
Miałem ten problem z listą pozycji, z których każda miała czas w minutach, aż były „należne” i wymagały aktualizacji. Zaktualizowałem dane, a potem zadzwoniłem
orderAdapter.notifyDataSetChanged();
i za każdym razem przewijał się do góry. Zastąpiłem to
for(int i = 0; i < orderArrayList.size(); i++){ orderAdapter.notifyItemChanged(i); }
i było dobrze. Żadna z innych metod w tym wątku nie zadziałała. Korzystając z tej metody, sprawiał jednak, że każdy pojedynczy element migał po aktualizacji, więc musiałem również umieścić to w onCreateView fragmentu nadrzędnego
RecyclerView.ItemAnimator animator = orderRecycler.getItemAnimator(); if (animator instanceof SimpleItemAnimator) { ((SimpleItemAnimator) animator).setSupportsChangeAnimations(false); }
źródło
Jeśli masz jeden lub więcej EditTextów wewnątrz elementu widoku recyklingu , wyłącz ich autofokus, umieszczając tę konfigurację w widoku nadrzędnym widoku recyklingu:
android:focusable="true" android:focusableInTouchMode="true"
Miałem ten problem, gdy zacząłem inną czynność uruchomioną z przedmiotu z recyklingu, kiedy wróciłem i ustawiłem aktualizację jednego pola w jednym elemencie z notifyItemChanged (pozycja) przewijanie ruchu RV, a mój wniosek był taki, że autofokus EditText Pozycje, powyższy kod rozwiązał mój problem.
Najlepsza.
źródło
Po prostu wróć, jeśli stara pozycja i pozycja są takie same;
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); }
źródło