android-android利用layout_behavior属性实现上滑屏幕,隐藏底部菜单栏

1
本文主要实现的功能是顶部工具栏和底部菜单栏随用户手势滑动而变化可见状态

底部导航栏的部分参考了android利用layout_behavior属性实现上滑屏幕,隐藏底部菜单栏——上官若枫

新建项目com.example.app

新建一个活动,实例代码的是MainActivity的。

它的布局简单来说就是:

1
2
3
4
5
CoordinatorLayout   //`app:layout_behavior`属性才能生效
AppBarLayout //容纳toobar(actionbar)
Toolbar //工具栏
TextView //代表主要内容
BottomNavigationView //底部导航栏

activity_main.xml的代码:

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
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.MainActivity">

<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">

<!-- app:layout_scrollFlags="scroll|enterAlways"
这个属性实现随着页面滚动标题栏(toolbar)显示隐藏-->
<androidx.appcompat.widget.Toolbar
android:id="@+id/default_action_bar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"
app:title="工具栏的标题" />

</com.google.android.material.appbar.AppBarLayout>

<!-- app:layout_behavior="@string/appbar_scrolling_view_behavior"
这个属性只有在布局是CoordinatorLayout 时才有,是让控件显示在AppBarLayout之下的-->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="假装这里是内容"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />

<!-- 底部导航栏
需要新建一个菜单`bottom_navigation_menu`
`com.example.app.behavior.BottomNavigationBehavior`就是我们自定义的类
-->
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_navigation_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:labelVisibilityMode="labeled"
app:layout_behavior="com.example.app.behavior.BottomNavigationBehavior"
app:layout_scrollFlags="scroll|enterAlways"
app:menu="@menu/bottom_navigation_menu" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

自定义Behavior类

网上常见的app:layout_behavior的用法是app:layout_behavior="@string/appbar_scrolling_view_behavior"。虽然表面上看是一个字符串,其实在里面调用的也是一个view类。

这次我们通过自定义这个behavior类,实现底部菜单栏的显隐性。

原文章使用的是Java代码,这里贴一段我改写的Kotlin的代码。
我在项目中新建了一个名为behaviorPackage,并在这个Package中新建了文件BottomNavigationBehavior.kt

被注释的部分是被override的函数默认的代码。

BottomNavigationBehavior.kt的代码

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package com.example.app.behavior

import android.animation.ObjectAnimator
import android.content.Context
import android.util.AttributeSet
import android.view.View
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.ViewCompat

class BottomNavigationBehavior(context: Context, attrs: AttributeSet) :
CoordinatorLayout.Behavior<View>(context, attrs) {
private var outAnimator: ObjectAnimator? = null
private var inAnimator: ObjectAnimator? = null

override fun onStartNestedScroll(
coordinatorLayout: CoordinatorLayout,
child: View,
directTargetChild: View,
target: View,
axes: Int,
type: Int
): Boolean {
// return super.onStartNestedScroll(
// coordinatorLayout,
// child,
// directTargetChild,
// target,
// axes,
// type
// )
/**
* 滚动轴是不是竖直滚动轴。如果是的话,就返回true
*/
return axes == ViewCompat.SCROLL_AXIS_VERTICAL
}

override fun onNestedPreScroll(
coordinatorLayout: CoordinatorLayout,
child: View,
target: View,
dx: Int,
dy: Int,
consumed: IntArray,
type: Int
) {
// super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type)
/**
* 上滑隐藏
*/
if (dy > 0) {
if (outAnimator == null) {
outAnimator =
ObjectAnimator.ofFloat(child, "translationY", 0f, child.height.toFloat())
outAnimator?.duration = 200
}
if (!outAnimator!!.isRunning && child.translationY <= 0) {
outAnimator!!.start()
}

/**
* 下滑显示
*/
} else if (dy < 0) {
if (inAnimator == null) {
inAnimator =
ObjectAnimator.ofFloat(child, "translationY", child.height.toFloat(), 0f)
inAnimator!!.duration = 200
}
if (!inAnimator!!.isRunning && child.translationY >= child.height) {
inAnimator!!.start()
}
}
}
}

onStartNestedScroll
这个方法主要用于监听协调布局(CoordinatorLayout)的子view的滚动事件,当此方法返回true,表示要消耗此动作,继而执行下面的onNestedPreScroll方法,我们的代码中,如果滚动轴是竖直滚动轴,就返回true

onNestedPreScroll
这个方法就比较简单了,当用户上滑的时候,隐藏底部菜单栏,这里使用了ObjectAnimator动画的ObjectAnimator.ofFloat方法,第一个参数是view对象,指的就是bottom_navigation_view,第二个是Y轴的变化,第三个是Y轴变化的多少,接下来设置动画秒数。(注意Kotlin中要将参数转换为浮点数,否则不匹配函数签名,即代码中用了.toFloat0f的原因。)视觉效果为:bottom_navigation_view向Y轴下滑等于自身高度的量,由于其android:layout_gravity="bottom",它将会滑出可视区域。

(完)