Recycler view внутри NestedScrollView вызывает прокрутку, чтобы начать в середине
Я получаю странное поведение прокрутки, когда я добавляю RecyclerView внутри NestedScrollView.
что происходит, когда scrollview имеет больше строк, чем может быть показано на экране, как только действие запускается, NestedScrollView начинается со смещения сверху (изображение 1). Если в виде прокрутки есть несколько элементов, чтобы их можно было показать сразу, этого не происходит (Рисунок 2).
Я использую версию 23.2.0 поддержки библиотека.
Фото 1: неправильно-начинается со смещения сверху
Рисунок 2: правильно-несколько элементов в представлении recycler
я вставляю ниже моего кода макета:
<?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>
Я что-то пропустила? Кто-нибудь знает, как это исправить?
обновление 1
он работает правильно, если я помещаю следующий код при инициализации моего Активность:
sv.post(new Runnable() {
@Override
public void run() {
sv.scrollTo(0,0);
}
});
где sv является ссылкой на NestedScrollView, однако это выглядит довольно Хак.
обновление 2
как и просили, вот мой адаптер код:
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());
}
}
и вот мой 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);
}
}
и вот макет каждого элемента в RecyclerView (это просто android.R.layout.simple_spinner_item
- этот экран предназначен только для показа примера этой ошибки):
<?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"/>
9 ответов:
Я решил такую проблему путем установления:
<ImageView ... android:focusableInTouchMode="true"/>
на мой взгляд выше RecyclerView (который был скрыт после нежелательной прокрутки). Попробуйте установить это свойство в свой LinearLayout выше RecyclerView или в LinearLayout, который является контейнером RecyclerView (помог мне в другом случае).
Как я вижу в источнике NestedScrollView он пытается сфокусировать первый возможный дочерний элемент в onRequestFocusInDescendants, и если только RecyclerView фокусируется, он выигрывает.
изменить (спасибо Waran): и для гладкой прокрутки не забудьте установить
yourRecyclerView.setNestedScrollingEnabled(false);
в своем
LinearLayout
сразу послеNestedScrollView
используйтеandroid:descendantFocusability
следующим образом<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="10dp" android:descendantFocusability="blocksDescendants">
EDIT
так как многие из них получают этот ответ полезным, также даст объяснение.
использование
descendantFocusability
дано здесь. И по состояниюfocusableInTouchMode
over здесь. Так что с помощьюblocksDescendants
наdescendantFocusability
не позволяет это ребенок, чтобы получить Фокус при прикосновении и, следовательно, незапланированное поведение может быть остановившийся.как
focusInTouchMode
, иAbsListView
иRecyclerView
вызывает методsetFocusableInTouchMode(true);
в их конструкторе по умолчанию, поэтому не требуется использовать этот атрибут в ваших XML-макетах.и
NestedScrollView
используется следующий метод:private void initScrollView() { mScroller = ScrollerCompat.create(getContext(), null); setFocusable(true); setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); setWillNotDraw(false); final ViewConfiguration configuration = ViewConfiguration.get(getContext()); mTouchSlop = configuration.getScaledTouchSlop(); mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); }
здесь
setFocusable()
метод используется вместоsetFocusableInTouchMode()
. Но согласно этому post,focusableInTouchMode
следует избегать, если для определенных условий, как это нарушает согласованность с Android normal поведение. Игра является хорошим примером приложения, которое может эффективно использовать свойство focusable In touch mode. MapView, если он используется в полноэкранном режиме, как в Google Maps, является еще одним хорошим примером того, где вы можете правильно использовать focusable в сенсорном режиме.
У меня была та же проблема и sovled путем расширения NestedScrollView и отключения фокусировки детей. По какой-то причине RecyclerView всегда просил сосредоточиться, даже когда я просто открывал и закрывал ящик.
public class DummyNestedScrollView extends NestedScrollView { public DummyNestedScrollView(Context context) { super(context); } public DummyNestedScrollView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public DummyNestedScrollView(Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } /** * Fixind problem with recyclerView in nested scrollview requesting focus * http://stackoverflow.com/questions/36314836/recycler-view-inside-nestedscrollview-causes-scroll-to-start-in-the-middle * @param child * @param focused */ @Override public void requestChildFocus(View child, View focused) { Log.d(getClass().getSimpleName(), "Request focus"); //super.requestChildFocus(child, focused); } /** * http://stackoverflow.com/questions/36314836/recycler-view-inside-nestedscrollview-causes-scroll-to-start-in-the-middle * @param direction * @param previouslyFocusedRect * @return */ @Override protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { Log.d(getClass().getSimpleName(), "Request focus descendants"); //return super.onRequestFocusInDescendants(direction, previouslyFocusedRect); return false; } }
в моем случае этот код решает мой вопрос
RecyclerView recyclerView = findViewById(R.id.recyclerView); NestedScrollView nestedScrollView= findViewById(R.id.nestedScrollView); recyclerView.setFocusable(false); nestedScrollView.requestFocus(); //populate recyclerview here
мой макет содержит Родительский макет как NestedScrollView, который имеет дочерний LinearLayout. LinearLayout имеет ориентацию "вертикаль" и childs RecyclerView и EditText. ссылка
у меня есть два догадки.
во-первых: попробуйте поставить эту строку на свой NestedScrollView
app:layout_behavior="@string/appbar_scrolling_view_behavior"
второй: Используйте
<android.support.design.widget.CoordinatorLayout
как ваш родительский вид Вот так
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v4.widget.NestedScrollView 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:orientation="vertical" android:padding="16dp"> <TextView style="@style/TextAppearance.AppCompat.Caption" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Title:"/> <TextView style="@style/TextAppearance.AppCompat.Body1" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="@dimen/bodyPadding" android:text="Neque porro quisquam est qui dolorem ipsum"/> <TextView style="@style/TextAppearance.AppCompat.Caption" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Subtitle:"/> <TextView style="@style/TextAppearance.AppCompat.Body1" android:layout_width="match_parent" android:layout_height="wrap_content" 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:layout_width="match_parent" android:layout_height="wrap_content" android:focusable="false"/> </LinearLayout> </android.support.v4.widget.NestedScrollView>
мое последнее возможное решение. Клянусь :)
в коде Java, после инициализации recyclerView и установки адаптера, добавьте эту строку:
recyclerView.setNestedScrollingEnabled(false)
вы также можете попытаться обернуть макет с помощью relativeLayout, чтобы представления оставались в том же положении, но recyclerView (который прокручивается) является первым в иерархии xml. Последнее предложение-отчаянная попытка: p