Plain's Blog

休想打败我的生活🔥

滑动 Scrollview 时动态改变 Toolbar 透明度,模仿京东详情页效果

Plain's Avatar 2020-09-23 安卓笔记

  1. 1. 效果
  2. 2. 思路
  3. 3. 实现

前段时间逛JD时看到详情页滑动时 Toolbar 改变透明度的效果感觉很不错,想着自己实现一下,于是就有了这篇文章。

效果

由于GIF图过大,效果通过Demo地址查看

思路

由于滑动的是一个 Scrollview,自然而然想到通过监听其滑动,通过计算Y轴滑动的距离与触发透明效果区域的距离,得到其滑动的比例,进而求得此时的透明度

实现

首先定义一个接口,用来回调透明度的变化

1
2
3
4
5
6
7
8
interface OnAlphaChangeListener {

/**
* 透明度 [alpha]
*/
fun onAlpha(@IntRange(from = 0, to = 255) alpha: Int)

}

然后自定义 Scrollview,这里继承自 NestedScrollView,使其有更好的兼容性

1
2
3
4
5
6
7
8
9
class DynamicAlphaScrollView @JvmOverloads constructor(
context: Context,
attributeSet: AttributeSet? = null,
defStyleAttr: Int = 0
) : NestedScrollView(context, attributeSet, defStyleAttr) {

......

}

既然是监听 ScrollView 的滑动距离,就要重写其 onScrollChanged 方法

1
2
3
override fun onScrollChanged(x: Int, y: Int, oldX: Int, oldY: Int) {
super.onScrollChanged(x, y, oldX, oldY)
}

接下来就是计算不同滑动距离下的透明度,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 处理透明度变化
*
* @param [scrollY] Y轴滑动距离
*/
private fun handleAlphaChange(scrollY: Int) {
onAlphaChangeListener?.let {
// 屏幕高度
val heightPixels = resources.displayMetrics.heightPixels
// 取屏幕高度1/3当作顶部高度(模糊值)
val topHeight = heightPixels / 3f
// 根据滑动距离计算透明度
val alpha = when {
// 滑动距离为0,透明度 0
scrollY <= 0 -> 0
// 滑动距离处于顶部高度所在范围,计算透明度
scrollY in 1 until topHeight.toInt() -> calculateAlpha(scrollY, topHeight)
// 滑动距离超过顶部高度,透明度 255
else -> 255
}
// 回调透明度
it.onAlpha(alpha)
}
}

这里我们取屏幕高度1/3作为触发透明度变化的区域,主要有3种情况

  • 当滑动距离为0时,透明度为0,也就是完全透明状态

  • 当滑动距离超过触发透明度变化的区域时,透明度为255,也就是完全不透明状态

  • 当滑动距离处于触发透明度变化的区域内时,就要根据滑动距离计算出透明度,公式如下

1
2
3
4
5
6
7
8
9
/**
* 计算透明度
*
* @param [scrollY] Y轴滑动距离
* @param [topHeight] 顶部高度
*/
private fun calculateAlpha(scrollY: Int, topHeight: Float): Int {
return (scrollY / topHeight * 255).toInt()
}

代码很简单,就一句话,滑动的距离除以触发透明度变化的区域在乘以255,就是此时的透明度

比如,滑动了200px,触发透明度变化的区域高度为300px,那么此时的透明度为:

1
200 / 300 * 255 = 170

通过上面的计算,我们知道了不同滑动距离下透明度的变化值,那么现在就要将它们应用起来

首先看一下布局文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">

<FrameLayout
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">

<top.i97.scrollviewtoolbartransparentdemo.DynamicAlphaScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<ImageView
android:layout_width="match_parent"
android:layout_height="200dp"
android:scaleType="centerCrop"
android:src="@drawable/intellijidea" />

......

</LinearLayout>

</top.i97.scrollviewtoolbartransparentdemo.DynamicAlphaScrollView>

<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="55dp"
android:background="@color/colorPrimary"
app:title="Gallery"
app:titleTextColor="@android:color/white" />

</FrameLayout>

<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@color/colorAccent"
android:elevation="5dp"
android:text="Action"
android:textAllCaps="false" />

</LinearLayout>

布局非常简单,上面一个 Toolbar,下面一个 Scrollview 包裹了一些图片

下面就是 Activity 中的实现

首先实现 OnAlphaChangeListener 接口,重写 onAlpha 方法来接收透明度变化的值

1
2
3
4
5
6
7
8
9
class MainActivity : AppCompatActivity(), OnAlphaChangeListener {

......

override fun onAlpha(alpha: Int) {

}

}

然后给刚才自定义的 Scrollview 设置监听

1
scrollView.onAlphaChangeListener = this

接着在 onAlpha 方法中改变 Toolbar 背景的透明度

1
2
3
4
override fun onAlpha(alpha: Int) {
// 动态改变 Toolbar 背景透明度
toolbar.background.mutate().alpha = alpha
}

最后就是沉浸式状态栏,由于不是本文章的重点,这里借助第三方库 Immersionbar 实现

1
2
3
4
5
6
7
8
9
private fun init() {
scrollView.onAlphaChangeListener = this
toolbar.apply {
// 初始透明度为0
background.mutate().alpha = 0
// 沉浸状态栏
ImmersionBar.with(this@MainActivity).titleBar(this).init()
}
}

❤️ done

本文作者 : Plain
This blog is under a CC BY-NC-SA 3.0 Unported License
本文链接 : https://plain-dev.com/android-toolbar-becomes-transparent-with-scrollview/

本文最后更新于 天前,文中所描述的信息可能已发生改变