一、BottomNavigationBehavior
动画效果是会跟着屏幕一起滚动,上滑时向下滚动移除屏幕,下滑向上滚动显示,适用于底部切换部分。
效果图:
如图,底部很好的移除了屏幕,悬浮按钮由于有一定的间距还留有一部分,可以通过修改动画或者单独给悬浮按钮设置一个 Behavior。
BottomNavigationBehavior:
/**
* Created by chenxz on 2017/11/25.
* 下滑一起下滑,下滑到屏幕外就不可见了,上滑一起上滑
*/
public class BottomNavigationBehavior extends CoordinatorLayout.Behavior<View> {
private ObjectAnimator outAnimator, inAnimator;
public BottomNavigationBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
// 垂直滑动
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;
}
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
if (dy > 0) {// 上滑隐藏
if (outAnimator == null) {
outAnimator = ObjectAnimator.ofFloat(child, "translationY", 0, child.getHeight() * 2);
outAnimator.setDuration(200);
}
if (!outAnimator.isRunning() && child.getTranslationY() <= 0) {
outAnimator.start();
}
} else if (dy < 0) {// 下滑显示
if (inAnimator == null) {
inAnimator = ObjectAnimator.ofFloat(child, "translationY", child.getHeight() * 2, 0);
inAnimator.setDuration(200);
}
if (!inAnimator.isRunning() && child.getTranslationY() >= child.getHeight()) {
inAnimator.start();
}
}
}
}
使用的 xml 布局:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/tb_tb_scroll_tb"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:navigationIcon="@mipmap/ic_navigation_back_white"
app:title="toolbar滚动"
app:titleTextColor="@color/white"
android:background="@color/blue_74D3FF"
app:layout_scrollFlags="scroll|enterAlways|snap"/>
</com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/content_scrolling"/>
<!-- <androidx.core.widget.NestedScrollView-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="match_parent"-->
<!-- app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">-->
<!-- <TextView-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_margin="@dimen/m12"-->
<!-- android:text="@string/large_text" />-->
<!-- </androidx.core.widget.NestedScrollView>-->
<!-- 加个底部滑动隐藏,适用于底部有控件的情况,上滑显示,-->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_gravity="bottom"
android:background="@color/yellow_FF9B52"
app:layout_behavior="@string/bottom_navigation_behavior">
</RelativeLayout>
<!-- 悬浮按钮-->
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab_tb_scroll_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_marginEnd="@dimen/m12"
android:layout_marginBottom="60dp"
android:src="@drawable/ic_cloud"
app:layout_behavior="@string/bottom_navigation_behavior"
/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
strings,将文件放在strings 里,方便以后对 Behavior 修改:
<string name="scale_up_show_behavior">com.kiwilss.xview.design.behavior.ScaleUpShowBehavior</string>
<string name="bottom_navigation_behavior">com.kiwilss.xview.design.behavior.BottomNavigationBehavior</string>
二、ScaleDownShowBehavior
FAB 行为控制器,下滑直接消失,上滑缩放出现。效果图:
代码:
/**
* Created by chenxz on 2018/5/13.
*
* FAB 行为控制器,下滑直接消失,上滑缩放出现
*/
class ScaleDownShowBehavior : FloatingActionButton.Behavior {
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
override fun onStartNestedScroll(coordinatorLayout: CoordinatorLayout,
child: FloatingActionButton,
directTargetChild: View,
target: View,
axes: Int,
type: Int): Boolean {
return axes == ViewCompat.SCROLL_AXIS_VERTICAL ||
super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type)
}
@SuppressLint("RestrictedApi")
override fun onNestedScroll(coordinatorLayout: CoordinatorLayout, child: FloatingActionButton, target: View, dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int, type: Int) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type)
if (dyConsumed > 0 && child.visibility == View.VISIBLE) {
child.visibility = View.INVISIBLE
} else if (dyConsumed < 0 && child.visibility != View.VISIBLE) {
child.show()
}
}
}
三、ScaleUpShowBehavior
FloatingActionButton上滑缩小隐藏,上滑放大显示,使用于返回顶部等
效果图:
代码:
/**
* @author : Lss Administrator
* @FileName: ScaleUpShowBehavior
* @e-mail : kiwilss@163.com
* @time : 2020/8/17
* @desc : {FloatingActionButton上滑缩小隐藏,上滑放大显示,使用于返回顶部等}
*/
public class ScaleUpShowBehavior extends FloatingActionButton.Behavior {
public ScaleUpShowBehavior(Context context, AttributeSet attrs) {
super();
}
private boolean isAnimatingOut = false;
private boolean isShow = true;
@Override
public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
@NonNull FloatingActionButton child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
return axes == ViewCompat.SCROLL_AXIS_VERTICAL ||
super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type);
//return super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type);
}
@Override
public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull FloatingActionButton child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type, @NonNull int[] consumed) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type, consumed);
// if (dyConsumed > 0 && dyUnconsumed == 0) {
// System.out.println("上滑中。。。");
// }
// if (dyConsumed == 0 && dyUnconsumed > 0) {
// System.out.println("到边界了还在上滑。。。");
// }
// if (dyConsumed < 0 && dyUnconsumed == 0) {
// System.out.println("下滑中。。。");
// }
// if (dyConsumed == 0 && dyUnconsumed < 0) {
// System.out.println("到边界了,还在下滑。。。");
// }
// 手指上滑,隐藏FAB
// 手指上滑,隐藏FAB
if ((dyConsumed > 0 || dyUnconsumed > 0) && !isAnimatingOut && isShow) {
AnimatorUtil.INSTANCE.scaleHide(child, viewPropertyAnimatorListenerHide);
} else if (dyConsumed < 0 || dyUnconsumed < 0 && !isAnimatingOut && !isShow) {
// 手指下滑,显示FAB
AnimatorUtil.INSTANCE.scaleShow(child,viewPropertyAnimatorListener);
}
}
ViewPropertyAnimatorListener viewPropertyAnimatorListener = new ViewPropertyAnimatorListener() {
@Override
public void onAnimationStart(View view) {
//view.setVisibility(View.VISIBLE);
isAnimatingOut = true;
}
@Override
public void onAnimationEnd(View view) {
isAnimatingOut = false;
}
@Override
public void onAnimationCancel(View arg0) {
isAnimatingOut = false;
}
};
ViewPropertyAnimatorListener viewPropertyAnimatorListenerHide = new ViewPropertyAnimatorListener() {
@Override
public void onAnimationStart(View view) {
isAnimatingOut = true;
}
@Override
public void onAnimationEnd(View view) {
isAnimatingOut = false;
//view.setVisibility(View.GONE);
}
@Override
public void onAnimationCancel(View arg0) {
isAnimatingOut = false;
}
};
}
四、ScrollAwareFABBehavior
FAB 上滑移出屏幕, 下滑显示,很想知乎首页,效果图:
代码:
/**
* Created by chenxz on 2018/5/13.
*
* FAB 上滑隐藏 下滑显示
*/
class ScrollAwareFABBehavior : FloatingActionButton.Behavior {
/**
* 是否正在动画
*/
private var isAnimateIng = false
/**
* 是否已经显示
*/
private var isShow = true
constructor(context: Context, attrs: AttributeSet) : super()
override fun onStartNestedScroll(coordinatorLayout: CoordinatorLayout,
child: FloatingActionButton,
directTargetChild: View,
target: View,
axes: Int,
type: Int): Boolean {
return axes == ViewCompat.SCROLL_AXIS_VERTICAL ||
super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type)
}
override fun onNestedScroll(coordinatorLayout: CoordinatorLayout, child: FloatingActionButton, target: View, dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int, type: Int) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type)
// 手指上滑,隐藏FAB
if ((dyConsumed > 0 || dyUnconsumed > 0) && !isAnimateIng && isShow) {
AnimatorUtil.translateHide(child, object : StateListener() {
override fun onAnimationStart(view: View) {
super.onAnimationStart(view)
isShow = false
}
})
} else if (dyConsumed < 0 || dyUnconsumed < 0 && !isAnimateIng && !isShow) {
// 手指下滑,显示FAB
AnimatorUtil.translateShow(child, object : StateListener() {
override fun onAnimationStart(view: View) {
super.onAnimationStart(view)
isShow = true
}
})
}
}
internal open inner class StateListener : ViewPropertyAnimatorListener {
override fun onAnimationStart(view: View) {
isAnimateIng = true
}
override fun onAnimationEnd(view: View) {
isAnimateIng = false
}
override fun onAnimationCancel(view: View) {
isAnimateIng = false
}
}
}