Skip to content

2.布局吸顶

donkingliang edited this page May 26, 2021 · 2 revisions

普通吸顶

要实现布局的吸顶效果,在以前,我们可能会写两个一摸一样的布局,一个隐藏在顶部,一个嵌套在ScrollView下,通过监听ScrollView的滑动来显示和隐藏顶部的布局。这种方式实现起来既麻烦也不优雅。ConsecutiveScrollerLayout内部实现了子View吸附顶部的功能,只要设置属性app:layout_isSticky="true",就可以实现吸顶功能。而且支持设置多个子View吸顶,后面的View要吸顶时会把前面的吸顶View推出屏幕。

<?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">

  <!-- 设置app:layout_isSticky="true"就可以使View吸顶 -->
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/white"
        android:padding="10dp"
        android:text="吸顶View - 1"
        android:textColor="@android:color/black"
        android:textSize="18sp"
        app:layout_isSticky="true" />

    <WebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/white"
        android:orientation="vertical"
        app:layout_isSticky="true">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:text="吸顶View - 2 我是个LinearLayout"
            android:textColor="@android:color/black"
            android:textSize="18sp" />

    </LinearLayout>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/white"
        android:padding="10dp"
        android:text="吸顶View - 3"
        android:textColor="@android:color/black"
        android:textSize="18sp"
        app:layout_isSticky="true" />

    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </androidx.core.widget.NestedScrollView>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/white"
        android:padding="10dp"
        android:text="吸顶View - 4"
        android:textColor="@android:color/black"
        android:textSize="18sp"
        app:layout_isSticky="true" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>

常驻吸顶

如果你不希望吸顶的view被后面的吸顶view顶出屏幕,而是多个吸顶view排列吸附在顶部,你可以设置常驻吸顶模式:app:isPermanent="true"

<?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"
    app:isPermanent="true"  // 常驻吸顶
    android:scrollbars="vertical">

</com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>

吸顶下沉模式

默认情况下,吸顶view在吸顶状态下,会显示在布局上层,悬浮在布局顶部,覆盖其他布局。 如果你希望吸顶View吸顶时,不是悬浮在顶部,而是像抽屉一样收起隐藏在布局下层,你可以把吸顶View设置为下沉模式。属性:app:layout_isSink="true"。设置了下沉模式,view在吸顶时会显示在下层,被其他布局覆盖,隐藏在下面。

<?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">

<!--  这个吸顶View吸顶时,会隐藏在布局下层  -->
   <FrameLayout
       android:id="@+id/fl_sink"
       android:layout_width="match_parent"
       android:layout_height="400dp"
       app:layout_isSink="true"  // 设置这个吸顶View为吸顶下沉模式
       app:layout_isTriggerScroll="true"
       app:layout_isSticky="true">

       <ImageView
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:scaleType="fitXY"
           android:src="@drawable/temp" />

       <TextView
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_marginLeft="30dp"
           android:layout_marginRight="30dp"
           android:layout_marginTop="30dp"
           android:textColor="@android:color/white"
           android:textSize="16sp"
           android:text="这是个下沉的吸顶view,它不会被推出屏幕,而是覆盖隐藏在其他布局下面。"/>

   </FrameLayout>

</com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>

stickyOffset属性

吸顶View默认是悬浮停留在ConsecutiveScrollerLayout的顶部,如果你希望吸顶View悬停的位置是距离顶部往上或者往下一定距离的位置,那么你可以使用app:stickyOffset属性设置吸顶位置的偏移。

<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"
    app:stickyOffset="50dp"> // 吸顶偏移

</com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>
// 设置吸顶View到顶部的距离,在距离顶部一定距离时开始悬停吸顶
// 使用这个方法可以动态改变吸顶View的吸顶位置
scrollerLayout.setStickyOffset(int offset);

autoAdjustHeightAtBottomView属性和adjustHeightOffset属性

子View悬浮吸顶时,会覆盖遮挡下面的布局,如果你希望下面的布局不被吸顶view覆盖,可以设置属性app:autoAdjustHeightAtBottomView="true",它能让ConsecutiveScrollerLayout的最后一个子View自动调整自己的高度,使布局内容不被吸顶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"
    app:autoAdjustHeightAtBottomView="true"> // 自动调整底部子view高度

</com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>

自动调整子view高度的时候,除了考虑吸顶View的高度以外,可能还需要考虑其他的一些额外因素。比如使用stickyOffset属性改变了吸顶View的吸顶位置,那么就有可能调整了子view高度后,吸顶View依然会遮挡布局。这时你可以通过app:adjustHeightOffset属性设置子view高度调整时的额外高度偏移,在调整子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"
    app:adjustHeightOffset="50dp"> // 调整子view高度的额外偏移量

</com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>

解析:

autoAdjustHeightAtBottomView属性和adjustHeightOffset属性只会对ConsecutiveScrollerLayout的最后一个子View期作用,也就是说,只有最后一个子View的高度会被调整。

不调整子view高度时,子View的最大高度不大于ConsecutiveScrollerLayout的高度。调整子view高度后,子View的最大高度不大于ConsecutiveScrollerLayout的高度 - 吸顶View高度 - adjustHeightOffset。adjustHeightOffset可以为负数,但是吸顶View高度 + adjustHeightOffset不能为负数,也就是后调整子view高度依然不大于ConsecutiveScrollerLayout的高度。

如果你使用了吸顶View功能,就应该设置app:autoAdjustHeightAtBottomView="true"

stickyOffset和adjustHeightOffset的区别

stickyOffset表示吸顶View的吸顶位置偏移,可以动态改变吸顶view的位置。adjustHeightOffset表示自动调整子View高度时的额外高度偏移量,不可以动态改变。当你设置了stickyOffset,就有可能需要设置adjustHeightOffset避免布局被覆盖遮挡,但不是必须的,这取决于你的布局和业务。

autoAdjustHeightAtBottomView属性和adjustHeightOffset属性虽然大部分时候是跟吸顶功能配合使用的,但是也可以单独使用。比如下面的例子中,如果Toolbar开始为透明,当ConsecutiveScrollerLayout滑动时变为不透明,就会覆盖ConsecutiveScrollerLayout的顶部,那么你就可以使用autoAdjustHeightAtBottomView和adjustHeightOffset调整ConsecutiveScrollerLayout的子View高度,使它在滑动到底部时,最后一个子View的内容不被遮挡覆盖。

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:adjustHeightOffset="48dp"
        app:autoAdjustHeightAtBottomView="true">

    </com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>

    <androidx.appcompat.widget.Toolbar
        android:layout_width="match_parent"
        android:layout_height="48dp" />

</FrameLayout>

关于吸顶功能的其他方法

// 监听吸顶变化(普通模式)
scrollerLayout.setOnStickyChangeListener(OnStickyChangeListener);
// 监听吸顶变化(常驻模式)
scrollerLayout.setOnPermanentStickyChangeListener(OnPermanentStickyChangeListener);
// 获取当前吸顶view(普通模式)
scrollerLayout.getCurrentStickyView(); 
// 获取当前吸顶view(常驻模式)
scrollerLayout.getCurrentStickyViews();
// 设置吸顶常驻模式
public void setPermanent(boolean isPermanent);
// 判断子view是否处于吸顶状态
public boolean theChildIsStick(View child);
// 判断子view是否是吸顶view
public boolean isStickyView(View child);

/**
 * 在View吸顶的状态下,是否可以触摸view来滑动ConsecutiveScrollerLayout布局。
 * 默认为false,则View吸顶的状态下,不能触摸它来滑动布局
 */
app:layout_isTriggerScroll="true"