Skip to content

4.滑动子view的下级view

donkingliang edited this page May 25, 2021 · 1 revision

ConsecutiveScrollerLayout默认情况下只会处理它的直接子view的滑动,但有时候需要滑动的布局可能不是ConsecutiveScrollerLayout的直接子view,而是子view所嵌套的下级view。比如ConsecutiveScrollerLayout嵌套FrameLayout,FrameLayout嵌套RecyclerView,我们希望ConsecutiveScrollerLayout也能正常处理RecyclerView的滑动,那么就需要让ConsecutiveScrollerLayout知道FrameLayout下的RecyclerView是需要由ConsecutiveScrollerLayout处理滑动事件的滑动控件。为了支持这种需求,ConsecutiveScroller提供了两种实现方式:app:layout_scrollChild属性和IConsecutiveScroller接口。

layout_scrollChild属性

子View可以通过app:layout_scrollChild属性指定需要处理滑动事件的下级View.

<com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/scrollerLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scrollbars="vertical">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_scrollChild="@+id/recyclerView"> // 指定recyclerView为需要滑动的view

        <!-- 滑动控件的高度应该是占满父布局的 -->
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </FrameLayout>

</com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>

这种方式使用简单,但是只能指定一个滑动View,而且无法适用于滑动View需要根据不同状态动态变化的复杂场景。

IConsecutiveScroller接口

子view实现IConsecutiveScroller接口,并通过实现接口方法告诉ConsecutiveScrollerLayout需要滑动的下级view,ConsecutiveScrollerLayout就能正确地处理它的滑动事件。IConsecutiveScroller需要实现两个方法:

    /**
     * 返回当前需要滑动的下级view。在一个时间点里只能有一个view可以滑动。
     */
    View getCurrentScrollerView();

    /**
     * 返回所有可以滑动的子view。由于ConsecutiveScrollerLayout允许它的子view包含多个可滑动的子view,所以返回一个view列表。
     */
    List<View> getScrolledViews();

在前面提到的例子中,我们可以这样实现:

public class MyFrameLayout extends FrameLayout implements IConsecutiveScroller {

    @Override
    public View getCurrentScrollerView() {
        // 返回需要滑动的ScrollView
        return getChildAt(0);
    }

    @Override
    public List<View> getScrolledViews() {
        // 返回需要滑动的ScrollView
        List<View> views = new ArrayList<>();
        views.add(getChildAt(0));
        return views;
    }
}
<com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/scrollerLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scrollbars="vertical">
    
    <com.donkingliang.consecutivescrollerdemo.view.MyFrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!-- 滑动控件的高度应该是占满父布局的 -->
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

    </com.donkingliang.consecutivescrollerdemo.view.MyFrameLayout>

</com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>

这样ConsecutiveScrollerLayout就能正确地处理ScrollView的滑动。这是一个简单的例子,在实际的需求中,我们一般不需要这样做。

使用IConsecutiveScroller接口,可以同时支持多个滑动子view,可以根据不同的业务场景和状态,返回不同的滑动view。

注意:

1、getCurrentScrollerView()和getScrolledViews()必须正确地返回需要滑动的view,这些view可以是经过多层嵌套的,不一定是直接子view。所以使用者应该按照自己的实际场景去实现者两个方法。

2、滑动的控件应该跟嵌套它的子view的高度保持一致,也就是说滑动的控件高度应该是match_parent,并且包裹它的子view和它本身都不要设置上下边距(margin和ppadding)。宽度没有这个限制。

对ViewPager的支持

如果你的ViewPager承载的子布局(或Fragment)不是可以垂直滑动的,那么使用普通的ViewPager即可。如果是可以垂直滑动的,那么你的ViewPager需要实现IConsecutiveScroller接口,并返回需要滑动的view对象。框架里提供了一个实现了IConsecutiveScroller接口的自定义控件:ConsecutiveViewPager。你应该使用这个控件,并且你的ConsecutiveViewPager的子view(或Fragment的根布局)是可垂直滑动的view,如:RecyclerView、NestedScrollView、ConsecutiveScrollerLayout等。这样你的ViewPager就能正确地跟ConsecutiveScrollerLayout一起滑动了。

<?xml version="1.0" encoding="utf-8"?>
<com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/scrollerLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scrollbars="vertical">

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tabLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/white"
        app:tabGravity="fill"
        app:tabIndicatorColor="@color/colorPrimary"
        app:tabIndicatorHeight="3dp"
        app:tabMode="scrollable"
        app:tabSelectedTextColor="@color/colorPrimary" />

    <com.donkingliang.consecutivescroller.ConsecutiveViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>

布局吸顶时会覆盖在下面的布局的上面,有时候我们希望TabLayout吸顶悬浮在顶部,但是不希望它覆盖遮挡ViewPager的内容。那么你可以使用autoAdjustHeightAtBottomView属性和adjustHeightOffset属性

对ViewPager2的支持

从4.4.0版本开始,支持在ConsecutiveScrollerLayout中使用ViewPager2。跟ViewPager一样,框架里专门提供了一个ViewPage2的自定义控件:ConsecutiveViewPager2。你必须使用它,而不能直接使用Androidx里的ViewPager2。不过你要使用它,依然需要引入ViewPager2依赖。

// xxx:viewpager2版本号
implementation 'androidx.viewpager2:viewpager2:xxx'
<?xml version="1.0" encoding="utf-8"?>
<com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/scrollerLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scrollbars="vertical">

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tabLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/white"
        app:tabGravity="fill"
        app:tabIndicatorColor="@color/colorPrimary"
        app:tabIndicatorHeight="3dp"
        app:tabMode="scrollable"
        app:tabSelectedTextColor="@color/colorPrimary" />

    <com.donkingliang.consecutivescroller.ConsecutiveViewPager2
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>

ConsecutiveViewPager2提供了跟ConsecutiveViewPager一样的功能。ConsecutiveViewPager2并不是ViewPager2的子类(ViewPager2不允许继承),而是一个包含ViewPager2的控件,所以你不能把ConsecutiveViewPager2当作ViewPager2。但是ConsecutiveViewPager2提供了跟ViewPager2一样的常用方法,而且提供了获取ViewPager2对象的方法:

public ViewPager2 getViewPager2();

所以你完全可以像使用ViewPager2一样使用它。

注意事项:

1、ConsecutiveViewPager2只能作为ConsecutiveScrollerLayout的子view,中间不能嵌套其他层级。

2、不要给ConsecutiveViewPager2设置padding。

3、ConsecutiveViewPager2不支持垂直翻页。也就是说,它只能跟ViewPager一样水平翻页和支持item垂直滑动。