首先看效果图:
gradle 关联
implementation 'com.google.android.material:material:1.0.0'
下面介绍示例用到的几个布局:
CoordinatorLayout
CoordinatorLayout 是一个 “加强版” FrameLayout, 它主要有两个用途:
- 用作应用的顶层布局管理器,也就是作为用户界面中所有 UI 控件的容器;
- 用作相互之间具有特定交互行为的 UI 控件的容器,通过为 CoordinatorLayout 的子 View 指定 Behavior, 就可以实现它们之间的交互行为。 Behavior 可以用来实现一系列的交互行为和布局变化,比如说侧滑菜单、可滑动删除的 UI 元素,以及跟随着其他 UI 控件移动的按钮等。
AppBarLayout
实际上我们在应用中有 CoordinatorLayout 的地方通常都会有 AppBarLayout 的联用。
AppBarLayout 是一个垂直的 LinearLayout,实现了 Material Design 中 App bar 的 Scrolling Gestures 特性。AppBarLayout 的子 View 应该声明想要具有的“滚动行为”,这可以通过 layout_scrollFlags 属性或是 setScrollFlags() 方法来指定。
AppBarLayout 只有作为 CoordinatorLayout 的直接子 View 时才能正常工作,为了让 AppBarLayout 能够知道何时滚动其子 View,我们还应该在 CoordinatorLayout 布局中提供一个可滚动 View,我们称之为 Scrolling View。
Scrolling View 和 AppBarLayout 之间的关联,通过将 Scrolling View 的 Behavior 设为 AppBarLayout.ScrollingViewBehavior 来建立。
AppBarLayout 主要给子布局配置属性 app:layout_scrollFlags
,layout_scrollFlags 的取值可以为以下几种。
-
scroll
:设置滚动效果,没有设置这个属性的view将被固定在屏幕顶部 -
enterAlways
:与 scroll 类似(scroll|enterAlways),只不过向下滚动先显示 AppBarLayout 到完全,再滚动 Scrolling View -
enterAlwaysCollapsed
:需要和 enterAlways 一起使用(scroll|enterAlways|enterAlwaysCollapsed),和 enterAlways 不一样的是,不会显示 AppBarLayout 到完全再滚动 Scrolling View,而是先滚动 AppBarLayout 到最小高度,再滚动 Scrolling View,最后再滚动 AppBarLayout 到完全显示。
注意:需要定义 View 的最小高度(minHeight)才有效果:android:minHeight="10dp" app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
-
exitUntilCollapsed
:当本 View 离开屏幕时,会被“折叠”直到达到其最小高度。我们可以这样理解这个效果:当我们开始向上滚动 Scrolling view 时,本 View 会先接管滚动事件,这样本 View 会先进行滚动,直到滚动到了最小高度(折叠了),Scrolling view 才开始实际滚动。而当本 View 已完全折叠后,再向下滚动 Scrolling view,直到 Scrolling view 顶部的内容完全显示后,本 View 才会开始向下滚动以显现出来。android:minHeight="10dp" app:layout_scrollFlags="scroll|exitUntilCollapsed"
-
snap
:在一次滚动结束时,本 View 很可能只处于“部分显示”的状态,加上这个标记能够达到“要么完全隐藏,要么完全显示”的效果。例如,如果视图只有底部25%显示,它将折叠。相反,如果它的底部75%可见,那么它将完全展开。
CollapsingToolbarLayout
CollapsingToolbarLayout 继承自 FrameLayout,它是用来实现 Toolbar 的折叠效果,一般它的直接子 View 是 Toolbar,当然也可以是其它类型的 View。
collapsingToolbarLayout 可以作为 AppBarLayout 的子 view,可以控制包含在其中的控件在滚动时的响应事件,子 view 可以是个可折叠的 Toolbar,app:layout_collapseMode 设置折叠模式。
CollapsingToolbarLayout 常用xml属性介绍
- contentScrim:当Toolbar收缩到一定程度时的所展现的主体颜色。即Toolbar的颜色。
- title:当titleEnable设置为true的时候,在toolbar展开的时候,显示大标题,toolbar收缩时,显示为toolbar上面的小标题。
- scrimAnimationDuration:该属性控制 toolbar 收缩时,颜色变化的动画持续时间。即颜色变为 contentScrim 所指定的颜色进行的动画所需要的时间。
- expandedTitleGravity:指定 toolbar 展开时,title 所在的位置。类似的还有expandedTitleMargin、collapsedTitleGravity 这些属性。
- collapsedTitleTextAppearance:指定 toolbar 收缩时,标题字体的样式,类似的还有 expandedTitleTextAppearance。
app:layout_collapseMode 折叠模式:
-
pin
:折叠后,此布局将固定在顶部。 -
parallax
:折叠时,此布局也会有视差折叠效果。
当其子布局设置了parallax模式时,我们还可以通过app:layout_collapseParallaxMultiplier 设置视差滚动因子,值为:0~1。
实现过程
1、 coordinatorLayout 嵌套 appBarLayout 。
2、appBarLayout 的子 view collapsingToolbarLayout 设置属性app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
让头部随着内容下拉而展开,随着内容上拉而收缩。
3、collapsingToolbarLayout 的子布局有两种,展开时显示的布局和 Toolbar,其中 Toolbar 又包含了两种布局,展开时的和收缩时的。 展开时,(扫一扫、付钱)的布局:
<include
layout="@layout/include_open"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.7" />
layout_marginTop="50dp" 预留出 toolbar 的高度,避免布局重叠。
toolbar里的两种布局:
<!--pin:折叠后,此布局将固定在顶部。 -->
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="50dp"
app:layout_collapseMode="pin">
<include
android:id="@+id/toolbar_open"
ayout="@layout/include_toolbar_open"/>
<include
android:id="@+id/toolbar_close"
layout="@layout/include_toolbar_close"/>
</androidx.appcompat.widget.Toolbar>
toolbar 里的两个布局,可以通过监听 AppBarLayout 的移动控制显示和隐藏。
4、滑动过程中,各控件的透明度会有渐变的效果,这里采用类似遮罩的效果,每个 include 布局里都有个遮罩的 view,在滑动过程中监听 appBarLayout
的addOnOffsetChangedListener
,通过计算滑动的距离,逐渐改变透明度。
/**
* 通过计算滑动的距离,逐渐改变透明度。
*/
override fun onOffsetChanged(appBarLayout: AppBarLayout, verticalOffset: Int) {
//垂直方向偏移量
val offset = Math.abs(verticalOffset)
//最大偏移距离
val scrollRange = appBarLayout.totalScrollRange
//当滑动没超过一半,展开状态下 toolbar 显示内容,更具收缩位置,改变透明值
if (offset <= scrollRange / 2) {
toolbar_open.visibility = View.VISIBLE
toolbar_close.visibility = View.GONE
//根据偏移百分比 计算透明值
val scale: Float = offset.toFloat() / (scrollRange / 2)
val alpha: Int = (255 * scale).toInt()
bg_toolbar_open.setBackgroundColor(Color.argb(alpha, 25, 131, 209))
}
//当滑动超过一半,收缩状态下 toolbar 显示内容,根据收缩位置,改变透明值
else {
toolbar_open.visibility = View.GONE
toolbar_close.visibility = View.VISIBLE
val scale = (scrollRange - offset).toFloat() / (scrollRange / 2)
val alpha = (255 * scale).toInt()
bg_toolbar_close.setBackgroundColor(Color.argb(alpha, 25, 131, 209))
}
//根据百分比计算扫一扫布局透明值
val scale = offset.toFloat() / scrollRange
val alpha = (255 * scale).toInt()
bg_content.setBackgroundColor(Color.argb(alpha, 25, 131, 209))
}
详细代码见 :
github地址:https://github.com/wuchao226/AliHome