Просмотр Recycler внутри NestedScrollView вызывает прокрутку для начала в середине
Я получаю странное поведение прокрутки, когда добавляю RecyclerView внутри NestedScrollView.
Случается, что всякий раз, когда у scrollview больше строк, чем может отображаться на экране, как только действие запускается, NestedScrollView начинает со смещения сверху (изображение 1). Если в представлении прокрутки есть несколько элементов, чтобы их можно было отобразить сразу, это не происходит (изображение 2).
Я использую версию 23.2.0 библиотеки поддержки.
Изображение 1: WRONG - начинается со смещения сверху
![Image 1]()
Изображение 2: CORRECT - несколько элементов в представлении ресайклера
![Image 2]()
Я вставляю ниже своего кода макета:
<?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"/>
Ответы
Ответ 1
Я решил эту проблему, установив:
<ImageView ...
android:focusableInTouchMode="true"/>
к моему представлению выше RecyclerView (который был скрыт после нежелательной прокрутки). Попробуйте установить это свойство в LinearLayout выше RecyclerView или LinearLayout, который является контейнером RecyclerView (помог мне в другом случае).
Как я вижу в источнике NestedScrollView, он пытается сфокусировать первого возможного дочернего элемента onRequestFocusInDescendants, и если только RecyclerView будет сфокусирован, он победит.
Изменить (спасибо Waran): и для плавного прокрутки не забудьте установить yourRecyclerView.setNestedScrollingEnabled(false);
Ответ 2
В LinearLayout
сразу после NestedScrollView
используйте android:descendantFocusability
следующим образом
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp"
android:descendantFocusability="blocksDescendants">
ИЗМЕНИТЬ
Так как многие из них получают этот ответ полезным, также будут объясняться.
Использование descendantFocusability
дано здесь. И focusableInTouchMode
над здесь.
Поэтому использование 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();
}
Здесь вместо setFocusableInTouchMode()
используется метод setFocusable()
. Но в соответствии с этим сообщением focusableInTouchMode
следует избегать, если только для определенных условий он не нарушает согласованность с обычным поведением Android. Игра является хорошим примером приложения, которое может эффективно использовать свойство фокусируемого в касании. MapView, если он используется в полноэкранном режиме, как в Картах Google, является еще одним хорошим примером того, где вы можете правильно использовать фокусируемый сенсорный режим.
Ответ 3
android:descendantFocusability="blocksDescendants"
внутри LinearLayout Работал для меня.
Ответ 4
У меня была одна и та же проблема, и я пропустил расширение 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;
}
}
Ответ 5
В моем случае этот код решает проблему моей проблемы
RecyclerView recyclerView = findViewById(R.id.recyclerView);
NestedScrollView nestedScrollView= findViewById(R.id.nestedScrollView);
recyclerView.setFocusable(false);
nestedScrollView.requestFocus();
//populate recyclerview here
Мой макет содержит родительский макет как NestedScrollView, у которого есть дочерний LinearLayout. LinearLayout имеет ориентацию "вертикальный" и дочерний RecyclerView и EditText. Ссылка
Ответ 6
У меня есть две догадки.
Сначала: попробуйте поместить эту строку в свой 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>
Мое последнее возможное решение. Клянусь:)
Ответ 7
В коде Java, после инициализации вашего recyclerView и установки адаптера, добавьте эту строку:
recyclerView.setNestedScrollingEnabled(false)
Вы также можете попытаться обернуть макет с помощью relativeLayout, чтобы представления оставались в одной и той же позиции, но recyclerView (который прокручивается) является первым в иерархии xml. Последнее предложение - отчаянная попытка: p
Ответ 8
Эта проблема возникает из-за перезаписи Focus.
Все фокусы автоматически перешли на повторное отображение, если его размер увеличил размер экрана.
добавление android:focusableInTouchMode="true"
к первому ChildView, например, TextView
, Button
и т.д. (не относится к ViewGroup
, например, Linear
, Relative
и т.д.), имеет смысл решить проблему, но решение API уровня 25 и выше делает не работа.
Просто добавьте эти две строки в ChildView, например, TextView
, Button
и т.д. (Не в ViewGroup
, например Linear
, Relative
и т.д.)
android:focusableInTouchMode="true"
android:focusable="true"
Я только что столкнулся с этой проблемой на уровне API 25. Я надеюсь, что другие люди не тратят время на это.
Для плавной прокрутки в RecycleView добавьте эту строку
android:nestedScrollingEnabled="false"
но добавление этих атрибутов работает только с API уровня 21 или выше. Если вы хотите, чтобы сглаживающая прокрутка работала ниже уровня API 25, добавьте эту строку в свой класс
mList = findViewById(R.id.recycle_list);
ViewCompat.setNestedScrollingEnabled(mList, false);
Ответ 9
Как я опаздываю, но могу помочь кому-то другому.
Просто используйте приведенную ниже или более высокую версию на уровне сборки build.gradle и проблема будет удалена.
compile com.android.support:recyclerview-v7:23.2.1
Ответ 10
чтобы прокрутить вверх, просто вызовите это в setcontentview
:
scrollView.SmoothScrollTo(0, 0);
Ответ 11
Просто добавьте android:descendantFocusability="blocksDescendants"
в ViewGroup внутри NestedScrollView.