NestedScrolling внутри Viewpager внутри BottomSheetDialog

Укороченная версия:

Как мне установить NestedScrollingChild из NestedScrollingParent с множественным номером такого ребенка.

Длинная версия

Я реализовал BottomSheetDialogFragment чей макет состоит из ViewPagerАдаптер этого видового пейджера содержит RecyclerView,

Теперь проблема в том, что с NestedScrollingParent который в это время расположение координат нижней таблицы поддерживает только один прямой NestedScrollingChildтолько первый фрагмент адаптера может быть прокручен.

Что я имею в виду, когда бы setAdapter вызывается в viewpager, первый элемент поддерживает вложенную прокрутку. Но после того, как я изменил страницу, новая страница теперь не прокручивается. Затем, когда я возвращаюсь на предыдущую страницу, он все еще поддерживает прокрутку.

Кроме того, я заметил, что если фрагмент или страница, которая может прокручиваться, уничтожается, следующая страница теперь может прокручиваться, что означает, что последняя страница становится дочерним элементом прокрутки нижнего листа. Проблема в том, что страница, которая теперь получила возможность прокрутки, является не текущим элементом, а предыдущим (мой адаптер должен поддерживать 3 фрагмента).

Резюме:

После setAdapter

  • фрагмент 0 можно прокрутить
  • затем после смены страницы на фрагмент 1 фрагмент 1 не может прокручиваться
  • но переключение на фрагмент 2, а затем возврат к фрагменту 1 позволяет фрагменту 1 прокручиваться (так как фрагмент 0 уничтожен, я думаю)

4 ответа

Решение

Покопавшись в исходном коде, я обнаружил, что проблема заключается в неисправном алгоритме, используемом при поиске NestedScrollingChild в нижней части листа (люди в Google не приняли во внимание возможность ViewPager внутри нижнего листа).

Смотрите метод здесь: findScrollingChild ()

Что этот метод делает, он вернет первый NestedScrollingChild он встречается в заданном представлении (в данном случае в нижней части таблицы), которое в случае просмотра с 3 страницами предшествует текущей странице. Кроме того, этот метод запускается на этапе макета дочерних элементов CoordinatorLayout Обертка нижнего листа.

Имея это в виду, можно придумать много решений, включая создание подкласса самого поведения.

Кроме того, можно ограничить NestedScrollingChild внутри viewpager путем добавления и удаления одного экземпляра такого потомка (удалить со старой страницы, затем добавить на текущей странице), что я и сделал. Вы можете сделать это внутри setPrimaryItem адаптера или на OnPageChangeListener, Просто обязательно позвони requestLayout на координатор макета нижнего листа. (Это решение зависит от типа компоновки / структуры адаптера пейджера, поэтому я не буду публиковать свое точное решение).

У меня были некоторые проблемы с этими предыдущими ответами. На самом деле, в моем случае @NonNull Object object в setPrimaryItem метод возвращает фрагмент и не может быть приведен к NestedScrollView,

Вот что у меня есть в моем XML:

  • Один BottomSheet содержащий ViewPager,
  • ViewPager с двумя страницами, содержащими одну Fragment каждый.
  • каждый Fragment Содержит один NestedScrollView в корне зрения.

Вот мой ViewPagerAdapter (Котлин)

class MyViewPagerAdapter(val id: String) :
    FragmentStatePagerAdapter(supportFragmentManager) {

    val titles = arrayOf(getString(R.string.title1),
        getString(R.string.title2))

    override fun getItem(position: Int) = when (position) {
        0 -> MyFragment1.newInstance(id)
        1 -> MyFragment2.newInstance(id)
        else -> Fragment()
    }

    override fun getCount() = 2

    override fun getPageTitle(position: Int): CharSequence? {
        return titles[position]
    }

    override fun setPrimaryItem(container: ViewGroup, position: Int, `object`: Any) {
        super.setPrimaryItem(container, position, `object`)

        val currentFragment = `object` as Fragment

        if (currentFragment.view != null) { //The first time the view isn't created yet
            for (i in 0 until count) {
                (container.getChildAt(i) as NestedScrollView).isNestedScrollingEnabled = false
            }

            val currentNestedScrollView: NestedScrollView = currentFragment.view as NestedScrollView
            currentNestedScrollView.isNestedScrollingEnabled = true

            container.requestLayout()
        }
    }
}

У меня была точно такая же проблема, и я нашел очень похожее решение.
Предоставление моего решения для справки:

ViewPagerAdapter.java

....
@Override
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
    super.setPrimaryItem(container, position, object);
    NestedScrollView current = ((NestedScrollView)object);
    current.setNestedScrollingEnabled(true);

    for (int i = 0; i < getCount(); i++) {
        if (i != position) {
            NestedScrollView otherScrollView = container.findViewWithTag(i);
            otherScrollView.setNestedScrollingEnabled(false);
        }
    }

    container.requestLayout();
}
...

Кроме того, я пишу сообщение в блоге на эту тему: Невозможно прокрутить прокручиваемый контент внутри ViewPager как BottomSheet из CoordinatorLayout

В дополнение к ответу @saiday эти коды отлично работают у меня (API 21 и выше);

Вот что у меня есть в моем XML:

Один BottomSheet, содержащий ViewPager.

ViewPager с двумя страницами, каждая из которых содержит по одному фрагменту.

Каждый фрагмент содержит один NestedScrollView в корневом представлении.

и мой код ViewPagerAdapter (java):

@Override
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
    super.setPrimaryItem(container, position, object);

    Fragment currentFragment = (Fragment) object;
    if (currentFragment.getView() != null) {
        for (int i = 0; i < getCount(); i++) {
            ((NestedScrollView) container.getChildAt(i)).setNestedScrollingEnabled(false);
            ((NestedScrollView) container.getChildAt(i)).getChildAt(0).setNestedScrollingEnabled(false); //this is recyclerview's nestedscroll
        }

        NestedScrollView currentNestedScrollView = ((NestedScrollView) currentFragment.getView());
        currentNestedScrollView.setNestedScrollingEnabled(true);
        currentNestedScrollView.getChildAt(0).setNestedScrollingEnabled(true); //this is recyclerview's nestedscroll, I used getChildAt(0) because I have one element in NestedScrollView.

        container.requestLayout();
    }
}

Мой первый и второй макеты (только идентификаторы разные):

<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:id="@+id/marketMembersFragmentScrollView"
    android:fillViewport="true">
    <androidx.recyclerview.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/marketMembersFragmentRecyclerView"/>
</androidx.core.widget.NestedScrollView>

Я упростила saiday's Решение немного. Важно знать, что я возвращаю ViewBindings вместо Views из instantiateItem,

public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
    super.setPrimaryItem(container, position, object);
    for (int i = 0; i < container.getChildCount(); i++) {
        //First disable nested scrolling for all instantiated child views
        ((NestedScrollView)container.getChildAt(i)).setNestedScrollingEnabled(false);
    }

    //Enable nested scrolling for the primary item
    ViewDataBinding binding = (ViewDataBinding) object;
    NestedScrollView current = ((NestedScrollView)binding.getRoot());
    current.setNestedScrollingEnabled(true);

    container.requestLayout();
}

При использовании библиотек Androidx RecyclerView реализует NestedScrollingChild вместо того NestedScrollView.

override fun setPrimaryItem(container: ViewGroup, position: Int, `object`: Any) {
        super.setPrimaryItem(container, position, `object`)

        val currentFragment = `object` as Fragment

        if (currentFragment.view != null) {
            for (i in 0 until count) {
                (container.getChildAt(i) as? NestedScrollingChild)?.isNestedScrollingEnabled = false
            }

            val currentNestedScrollView: NestedScrollingChild = currentFragment.view as NestedScrollingChild
            currentNestedScrollView.isNestedScrollingEnabled = true

            container.requestLayout()
        }
    }

Я тоже столкнулся с этой проблемой, пытаясь использовать ViewPager с прокруткой просмотров почти на каждой странице, внутри BottomSheetDialog. Я нашел это решение, сделанное laenger ([ https://github.com/laenger/ViewPagerBottomSheet][1]), и приняло его, чтобы оно соответствовало BottomSheetDialog. Надеюсь, что это может помочь тем, кто сталкивается с той же проблемой:)

Другие вопросы по тегам