Otrzymuję dziwne zachowanie przewijania, gdy dodaję RecyclerView do NestedScrollView.
Dzieje się tak, że ilekroć widok przewijania ma więcej wierszy, niż można wyświetlić na ekranie, zaraz po uruchomieniu działania NestedScrollView zaczyna się od przesunięcia od góry (obraz 1). Jeśli w widoku przewijania jest niewiele elementów, tak że można je wszystkie wyświetlić jednocześnie, tak się nie dzieje (obraz 2).
Używam wersji 23.2.0 biblioteki obsługi.
Obraz 1 : ŹLE - zaczyna się od przesunięcia od góry
Zdjęcie 2 : PRAWIDŁOWO - kilka pozycji w widoku recyklera
Wklejam poniżej mój kod układu:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="fill_vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Title:"
style="@style/TextAppearance.AppCompat.Caption"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/bodyPadding"
style="@style/TextAppearance.AppCompat.Body1"
android:text="Neque porro quisquam est qui dolorem ipsum"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Subtitle:"
style="@style/TextAppearance.AppCompat.Caption"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/TextAppearance.AppCompat.Body1"
android:padding="@dimen/bodyPadding"
android:text="Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit..."/>
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/rv"
android:focusable="false"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
Czy coś mi brakuje? Czy ktoś ma pomysł, jak to naprawić?
Zaktualizuj 1
Działa poprawnie, jeśli podczas inicjowania działania umieszczę następujący kod:
sv.post(new Runnable() {
@Override
public void run() {
sv.scrollTo(0,0);
}
});
Gdzie sv jest odwołaniem do NestedScrollView, jednak wygląda na to, że to niezły hack.
Zaktualizuj 2
Zgodnie z żądaniem, oto mój kod adaptera:
public abstract class ArrayAdapter<T, VH extends RecyclerView.ViewHolder>
extends RecyclerView.Adapter<VH> {
private List<T> mObjects;
public ArrayAdapter(final List<T> objects) {
mObjects = objects;
}
/**
* Adds the specified object at the end of the array.
*
* @param object The object to add at the end of the array.
*/
public void add(final T object) {
mObjects.add(object);
notifyItemInserted(getItemCount() - 1);
}
/**
* Remove all elements from the list.
*/
public void clear() {
final int size = getItemCount();
mObjects.clear();
notifyItemRangeRemoved(0, size);
}
@Override
public int getItemCount() {
return mObjects.size();
}
public T getItem(final int position) {
return mObjects.get(position);
}
public long getItemId(final int position) {
return position;
}
/**
* Returns the position of the specified item in the array.
*
* @param item The item to retrieve the position of.
* @return The position of the specified item.
*/
public int getPosition(final T item) {
return mObjects.indexOf(item);
}
/**
* Inserts the specified object at the specified index in the array.
*
* @param object The object to insert into the array.
* @param index The index at which the object must be inserted.
*/
public void insert(final T object, int index) {
mObjects.add(index, object);
notifyItemInserted(index);
}
/**
* Removes the specified object from the array.
*
* @param object The object to remove.
*/
public void remove(T object) {
final int position = getPosition(object);
mObjects.remove(object);
notifyItemRemoved(position);
}
/**
* Sorts the content of this adapter using the specified comparator.
*
* @param comparator The comparator used to sort the objects contained in this adapter.
*/
public void sort(Comparator<? super T> comparator) {
Collections.sort(mObjects, comparator);
notifyItemRangeChanged(0, getItemCount());
}
}
A oto mój ViewHolder:
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView txt;
public ViewHolder(View itemView) {
super(itemView);
txt = (TextView) itemView;
}
public void render(String text) {
txt.setText(text);
}
}
A oto układ każdego elementu w RecyclerView (po prostu android.R.layout.simple_spinner_item
- ten ekran służy tylko do pokazania przykładu tego błędu):
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
style="?android:attr/spinnerItemStyle"
android:singleLine="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:textAlignment="inherit"/>
android:focusableInTouchMode="false"
zaRecyclerView
? smth wyraźnie jest „zmuszanie układ, aby przejść do dołu ... (gdzie zacząć, gdy masz 1295 pozycji na dole lub po prostu mały top offsetowych jak pierwszego ekranu?)LayoutManager
Odpowiedzi:
Rozwiązałem taki problem ustawiając:
do mojego widoku powyżej RecyclerView (który został ukryty po niechcianym przewijaniu). Spróbuj ustawić tę właściwość na LinearLayout powyżej RecyclerView lub na LinearLayout, który jest kontenerem RecyclerView (pomógł mi w innym przypadku).
Jak widzę w źródle NestedScrollView, próbuje skupić się na pierwszym możliwym dziecku w onRequestFocusInDescendants i jeśli tylko RecyclerView jest aktywny, wygrywa.
Edycja (dzięki Waran): a dla płynnego przewijania nie zapomnij ustawić
yourRecyclerView.setNestedScrollingEnabled(false);
źródło
android:focusableInTouchMode="false"
recyklingu, aby nie trzeba było ustawiać wartości true dla wszystkich innych widoków.W swoim
LinearLayout
bezpośrednim PONestedScrollView
, wykorzystanieandroid:descendantFocusability
w następujący sposóbEDYTOWAĆ
Ponieważ wielu z nich uzyskało użyteczną odpowiedź, poda również wyjaśnienie.
Zastosowanie
descendantFocusability
podano tutaj . A odfocusableInTouchMode
ponad tutaj . Więc korzystającblocksDescendants
zdescendantFocusability
nie pozwala, że to dziecko, aby uzyskać ostrość podczas dotykania i stąd nieplanowane zachowanie może być zatrzymany.Jeśli chodzi o
focusInTouchMode
, zarównoAbsListView
i domyślnieRecyclerView
wywołuje metodęsetFocusableInTouchMode(true);
w ich konstruktorze, więc nie jest wymagane używanie tego atrybutu w układach XML.I
NestedScrollView
stosuje się następującą metodę:Tutaj
setFocusable()
zamiast metody używana jest metodasetFocusableInTouchMode()
. Ale według tego postu ,focusableInTouchMode
należy unikać, chyba że na pewnych warunkach, gdyż łamie zgodność z Androidem normalne zachowanie. Gra jest dobrym przykładem aplikacji, która może dobrze wykorzystać właściwość, którą można ustawić w trybie dotykowym. MapView, jeśli jest używany w trybie pełnoekranowym, tak jak w Mapach Google, jest kolejnym dobrym przykładem tego, gdzie można poprawnie używać ustawiania ostrości w trybie dotykowym.źródło
blocksDescendants
do blokowania wszystkich elementów podrzędnych . Nie jestem pewien, ale spróbujbeforeDescendants
recyclerView.setFocusable(false); nestedScrollView.requestFocus();
wewnątrz LinearLayout zadziałało dla mnie.
źródło
Miałem ten sam problem i rozwiązałem go, rozszerzając NestedScrollView i wyłączając skupianie dzieci. Z jakiegoś powodu RecyclerView zawsze żądał skupienia, nawet gdy właśnie otworzyłem i zamknąłem szufladę.
źródło
RecyclerView
posiadaczy.W moim przypadku ten kod rozwiązuje mój problem
Mój układ zawiera układ nadrzędny jako NestedScrollView, który ma element podrzędny LinearLayout. LinearLayout ma orientację „pionową” i podrzędne RecyclerView i EditText. Odniesienie
źródło
Mam dwa przypuszczenia.
Po pierwsze: spróbuj umieścić tę linię w swoim NestedScrollView
Po drugie: użyj
jak widzą twoi rodzice
Moje ostatnie możliwe rozwiązanie. Przysięgam :)
źródło
Ten problem pojawia się z powodu ostrości widoku recyklingu.
Automatycznie cały fokus przechodzi do widoku ponownego wyświetlania, jeśli jego rozmiar zwiększył rozmiar ekranu.
dodanie
android:focusableInTouchMode="true"
do pierwszego ChildView podobnegoTextView
,Button
i tak dalej (nieViewGroup
jakLinear
,Relative
i tak dalej) ma sens, aby rozwiązać problem, ale API Level 25 i powyżej nie rozwiązuje problemu.Wystarczy dodać te 2 linie w ChildView podobnego
TextView
,Button
i tak dalej (nieViewGroup
jakLinear
,Relative
i tak dalej)Właśnie napotkałem ten problem na poziomie API 25. Mam nadzieję, że inni ludzie nie marnują na to czasu.
Aby uzyskać płynne przewijanie w RecycleView, dodaj tę linię
ale dodanie tego atrybutu działa tylko z poziomem API 21 lub wyższym. Jeśli chcesz, aby płynne przewijanie działało poniżej poziomu API 25, dodaj tę linię do swojej klasy
źródło
W kodzie Java, po zainicjowaniu programu recyclinglerView i ustawieniu adaptera, dodaj następujący wiersz:
Możesz również spróbować zawinąć układ za pomocą względnego układu, aby widoki pozostały w tej samej pozycji, ale recyklerView (który przewija) jest pierwszy w hierarchii XML. Ostatnia sugestia to desperacka próba: s
źródło
Ponieważ spóźniam się z odpowiedzią, ale mogę pomóc komuś innemu. Po prostu użyj niższej lub wyższej wersji w build.gradle na poziomie aplikacji, a problem zostanie usunięty.
źródło
aby przewinąć do góry, po prostu zadzwoń
setcontentview
:źródło
Po prostu dodaj
android:descendantFocusability="blocksDescendants"
ViewGroup wewnątrz NestedScrollView.źródło