一、BottomNavigationBehavior

动画效果是会跟着屏幕一起滚动,上滑时向下滚动移除屏幕,下滑向上滚动显示,适用于底部切换部分。
效果图:
iShot2020-08-1722.14.26.gif
如图,底部很好的移除了屏幕,悬浮按钮由于有一定的间距还留有一部分,可以通过修改动画或者单独给悬浮按钮设置一个 Behavior。
BottomNavigationBehavior:

  1. /**
  2. * Created by chenxz on 2017/11/25.
  3. * 下滑一起下滑,下滑到屏幕外就不可见了,上滑一起上滑
  4. */
  5. public class BottomNavigationBehavior extends CoordinatorLayout.Behavior<View> {
  6. private ObjectAnimator outAnimator, inAnimator;
  7. public BottomNavigationBehavior(Context context, AttributeSet attrs) {
  8. super(context, attrs);
  9. }
  10. // 垂直滑动
  11. @Override
  12. public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
  13. return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;
  14. }
  15. @Override
  16. public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
  17. if (dy > 0) {// 上滑隐藏
  18. if (outAnimator == null) {
  19. outAnimator = ObjectAnimator.ofFloat(child, "translationY", 0, child.getHeight() * 2);
  20. outAnimator.setDuration(200);
  21. }
  22. if (!outAnimator.isRunning() && child.getTranslationY() <= 0) {
  23. outAnimator.start();
  24. }
  25. } else if (dy < 0) {// 下滑显示
  26. if (inAnimator == null) {
  27. inAnimator = ObjectAnimator.ofFloat(child, "translationY", child.getHeight() * 2, 0);
  28. inAnimator.setDuration(200);
  29. }
  30. if (!inAnimator.isRunning() && child.getTranslationY() >= child.getHeight()) {
  31. inAnimator.start();
  32. }
  33. }
  34. }
  35. }

使用的 xml 布局:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. xmlns:app="http://schemas.android.com/apk/res-auto">
  6. <com.google.android.material.appbar.AppBarLayout
  7. android:layout_width="match_parent"
  8. android:layout_height="wrap_content">
  9. <androidx.appcompat.widget.Toolbar
  10. android:id="@+id/tb_tb_scroll_tb"
  11. android:layout_width="match_parent"
  12. android:layout_height="wrap_content"
  13. app:navigationIcon="@mipmap/ic_navigation_back_white"
  14. app:title="toolbar滚动"
  15. app:titleTextColor="@color/white"
  16. android:background="@color/blue_74D3FF"
  17. app:layout_scrollFlags="scroll|enterAlways|snap"/>
  18. </com.google.android.material.appbar.AppBarLayout>
  19. <include layout="@layout/content_scrolling"/>
  20. <!-- <androidx.core.widget.NestedScrollView-->
  21. <!-- android:layout_width="match_parent"-->
  22. <!-- android:layout_height="match_parent"-->
  23. <!-- app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">-->
  24. <!-- <TextView-->
  25. <!-- android:layout_width="wrap_content"-->
  26. <!-- android:layout_height="wrap_content"-->
  27. <!-- android:layout_margin="@dimen/m12"-->
  28. <!-- android:text="@string/large_text" />-->
  29. <!-- </androidx.core.widget.NestedScrollView>-->
  30. <!-- 加个底部滑动隐藏,适用于底部有控件的情况,上滑显示,-->
  31. <RelativeLayout
  32. android:layout_width="match_parent"
  33. android:layout_height="50dp"
  34. android:layout_gravity="bottom"
  35. android:background="@color/yellow_FF9B52"
  36. app:layout_behavior="@string/bottom_navigation_behavior">
  37. </RelativeLayout>
  38. <!-- 悬浮按钮-->
  39. <com.google.android.material.floatingactionbutton.FloatingActionButton
  40. android:id="@+id/fab_tb_scroll_menu"
  41. android:layout_width="wrap_content"
  42. android:layout_height="wrap_content"
  43. android:layout_gravity="end|bottom"
  44. android:layout_marginEnd="@dimen/m12"
  45. android:layout_marginBottom="60dp"
  46. android:src="@drawable/ic_cloud"
  47. app:layout_behavior="@string/bottom_navigation_behavior"
  48. />
  49. </androidx.coordinatorlayout.widget.CoordinatorLayout>

strings,将文件放在strings 里,方便以后对 Behavior 修改:

  1. <string name="scale_up_show_behavior">com.kiwilss.xview.design.behavior.ScaleUpShowBehavior</string>
  2. <string name="bottom_navigation_behavior">com.kiwilss.xview.design.behavior.BottomNavigationBehavior</string>

二、ScaleDownShowBehavior

FAB 行为控制器,下滑直接消失,上滑缩放出现。效果图:
iShot2020-08-1722.22.29.gif
代码:

  1. /**
  2. * Created by chenxz on 2018/5/13.
  3. *
  4. * FAB 行为控制器,下滑直接消失,上滑缩放出现
  5. */
  6. class ScaleDownShowBehavior : FloatingActionButton.Behavior {
  7. constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
  8. override fun onStartNestedScroll(coordinatorLayout: CoordinatorLayout,
  9. child: FloatingActionButton,
  10. directTargetChild: View,
  11. target: View,
  12. axes: Int,
  13. type: Int): Boolean {
  14. return axes == ViewCompat.SCROLL_AXIS_VERTICAL ||
  15. super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type)
  16. }
  17. @SuppressLint("RestrictedApi")
  18. override fun onNestedScroll(coordinatorLayout: CoordinatorLayout, child: FloatingActionButton, target: View, dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int, type: Int) {
  19. super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type)
  20. if (dyConsumed > 0 && child.visibility == View.VISIBLE) {
  21. child.visibility = View.INVISIBLE
  22. } else if (dyConsumed < 0 && child.visibility != View.VISIBLE) {
  23. child.show()
  24. }
  25. }
  26. }

三、ScaleUpShowBehavior

FloatingActionButton上滑缩小隐藏,上滑放大显示,使用于返回顶部等
效果图:
iShot2020-08-1722.24.39.gif
代码:

  1. /**
  2. * @author : Lss Administrator
  3. * @FileName: ScaleUpShowBehavior
  4. * @e-mail : kiwilss@163.com
  5. * @time : 2020/8/17
  6. * @desc : {FloatingActionButton上滑缩小隐藏,上滑放大显示,使用于返回顶部等}
  7. */
  8. public class ScaleUpShowBehavior extends FloatingActionButton.Behavior {
  9. public ScaleUpShowBehavior(Context context, AttributeSet attrs) {
  10. super();
  11. }
  12. private boolean isAnimatingOut = false;
  13. private boolean isShow = true;
  14. @Override
  15. public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
  16. @NonNull FloatingActionButton child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
  17. return axes == ViewCompat.SCROLL_AXIS_VERTICAL ||
  18. super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type);
  19. //return super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type);
  20. }
  21. @Override
  22. 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) {
  23. super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type, consumed);
  24. // if (dyConsumed > 0 && dyUnconsumed == 0) {
  25. // System.out.println("上滑中。。。");
  26. // }
  27. // if (dyConsumed == 0 && dyUnconsumed > 0) {
  28. // System.out.println("到边界了还在上滑。。。");
  29. // }
  30. // if (dyConsumed < 0 && dyUnconsumed == 0) {
  31. // System.out.println("下滑中。。。");
  32. // }
  33. // if (dyConsumed == 0 && dyUnconsumed < 0) {
  34. // System.out.println("到边界了,还在下滑。。。");
  35. // }
  36. // 手指上滑,隐藏FAB
  37. // 手指上滑,隐藏FAB
  38. if ((dyConsumed > 0 || dyUnconsumed > 0) && !isAnimatingOut && isShow) {
  39. AnimatorUtil.INSTANCE.scaleHide(child, viewPropertyAnimatorListenerHide);
  40. } else if (dyConsumed < 0 || dyUnconsumed < 0 && !isAnimatingOut && !isShow) {
  41. // 手指下滑,显示FAB
  42. AnimatorUtil.INSTANCE.scaleShow(child,viewPropertyAnimatorListener);
  43. }
  44. }
  45. ViewPropertyAnimatorListener viewPropertyAnimatorListener = new ViewPropertyAnimatorListener() {
  46. @Override
  47. public void onAnimationStart(View view) {
  48. //view.setVisibility(View.VISIBLE);
  49. isAnimatingOut = true;
  50. }
  51. @Override
  52. public void onAnimationEnd(View view) {
  53. isAnimatingOut = false;
  54. }
  55. @Override
  56. public void onAnimationCancel(View arg0) {
  57. isAnimatingOut = false;
  58. }
  59. };
  60. ViewPropertyAnimatorListener viewPropertyAnimatorListenerHide = new ViewPropertyAnimatorListener() {
  61. @Override
  62. public void onAnimationStart(View view) {
  63. isAnimatingOut = true;
  64. }
  65. @Override
  66. public void onAnimationEnd(View view) {
  67. isAnimatingOut = false;
  68. //view.setVisibility(View.GONE);
  69. }
  70. @Override
  71. public void onAnimationCancel(View arg0) {
  72. isAnimatingOut = false;
  73. }
  74. };
  75. }

四、ScrollAwareFABBehavior

FAB 上滑移出屏幕, 下滑显示,很想知乎首页,效果图:
iShot2020-08-1722.27.27.gif
代码:

  1. /**
  2. * Created by chenxz on 2018/5/13.
  3. *
  4. * FAB 上滑隐藏 下滑显示
  5. */
  6. class ScrollAwareFABBehavior : FloatingActionButton.Behavior {
  7. /**
  8. * 是否正在动画
  9. */
  10. private var isAnimateIng = false
  11. /**
  12. * 是否已经显示
  13. */
  14. private var isShow = true
  15. constructor(context: Context, attrs: AttributeSet) : super()
  16. override fun onStartNestedScroll(coordinatorLayout: CoordinatorLayout,
  17. child: FloatingActionButton,
  18. directTargetChild: View,
  19. target: View,
  20. axes: Int,
  21. type: Int): Boolean {
  22. return axes == ViewCompat.SCROLL_AXIS_VERTICAL ||
  23. super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type)
  24. }
  25. override fun onNestedScroll(coordinatorLayout: CoordinatorLayout, child: FloatingActionButton, target: View, dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int, type: Int) {
  26. super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type)
  27. // 手指上滑,隐藏FAB
  28. if ((dyConsumed > 0 || dyUnconsumed > 0) && !isAnimateIng && isShow) {
  29. AnimatorUtil.translateHide(child, object : StateListener() {
  30. override fun onAnimationStart(view: View) {
  31. super.onAnimationStart(view)
  32. isShow = false
  33. }
  34. })
  35. } else if (dyConsumed < 0 || dyUnconsumed < 0 && !isAnimateIng && !isShow) {
  36. // 手指下滑,显示FAB
  37. AnimatorUtil.translateShow(child, object : StateListener() {
  38. override fun onAnimationStart(view: View) {
  39. super.onAnimationStart(view)
  40. isShow = true
  41. }
  42. })
  43. }
  44. }
  45. internal open inner class StateListener : ViewPropertyAnimatorListener {
  46. override fun onAnimationStart(view: View) {
  47. isAnimateIng = true
  48. }
  49. override fun onAnimationEnd(view: View) {
  50. isAnimateIng = false
  51. }
  52. override fun onAnimationCancel(view: View) {
  53. isAnimateIng = false
  54. }
  55. }
  56. }