18 | * 下滑时 19 | * 当AppBarLayout全部展开时(即未到顶部时),会依次调用onStartNestedScroll()->onNestedScrollAccepted()->onNestedPreScroll()->onNestedScroll()->onStopNestedScroll() 20 | * 当AppBarLayout全部展开时(即到顶部时),继续向下滑动屏幕,会依次调用onStartNestedScroll()->onNestedScrollAccepted()->onNestedPreScroll()->onNestedScroll()->onStopNestedScroll() 21 | *
22 | *
23 | * 当有快速滑动时会在onStopNestedScroll()前依次调用onNestedPreFling()->onNestedFling()
24 | * 所以要修改AppBarLayout的越界行为可以重写onNestedPreScroll()或onNestedScroll(),因为AppBarLayout收起时不会调用onNestedScroll(),所以只能选择重写onNestedPreScroll(),具体原因下面会有说明。
25 | */
26 | public class AppBarLayoutOverScrollViewBehavior extends AppBarLayout.Behavior {
27 | private int mAppBarHeight;
28 | private View mCardView;
29 | private boolean isAnimate;
30 | private float mTotalDy;
31 | private float mLastScale;
32 | private int mLastBottom;
33 | private int mCardViewHeight;
34 | private int mLimitHeight;
35 | private View mToolBar;
36 | private float scaleValue = 2f / 3;// 显示卡片的三分之一 所以抛出三分之二
37 | private View mNameTitle;
38 |
39 | public AppBarLayoutOverScrollViewBehavior() {
40 | }
41 |
42 | public AppBarLayoutOverScrollViewBehavior(Context context, AttributeSet attrs) {
43 | super(context, attrs);
44 | }
45 |
46 |
47 | @Override
48 | public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout abl, int layoutDirection) {
49 | boolean handled = super.onLayoutChild(parent, abl, layoutDirection);
50 | if (null == mCardView) {
51 | mCardView = parent.findViewById(R.id.cardview);
52 | }
53 | if (null == mToolBar) {
54 | mToolBar = parent.findViewById(R.id.toolbar);
55 | }
56 | if (null == mNameTitle) {
57 | mNameTitle = parent.findViewById(R.id.name);
58 | }
59 |
60 | init(abl);
61 | return handled;
62 | }
63 |
64 |
65 | @Override
66 | public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY) {
67 | if (velocityY > 100) {
68 | isAnimate = false;
69 | }
70 | return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
71 | }
72 |
73 |
74 | @Override
75 | public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl, View target, int type) {
76 | super.onStopNestedScroll(coordinatorLayout, abl, target, type);
77 | //恢复位置
78 | if (abl.getBottom() > mLimitHeight) {
79 | recovery(abl);
80 | }
81 | }
82 |
83 |
84 | @Override
85 | public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes, int type) {
86 | //开始滚动了 就动画归位
87 | isAnimate = true;
88 | return super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes, type);
89 | }
90 |
91 |
92 | @Override
93 | public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed, int type) {
94 | if (mCardView != null && ((dy <= 0 && child.getBottom() >= mLimitHeight) || (dy > 0 && child.getBottom() > mLimitHeight))) {
95 | scrollY(child, target, dy);
96 | } else {
97 | setViewAlpha(child, dy);
98 | super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
99 | }
100 | }
101 |
102 |
103 | /**
104 | * 初始化数据
105 | *
106 | * @param appBarLayout
107 | */
108 | private void init(final AppBarLayout appBarLayout) {
109 | appBarLayout.setClipChildren(false);
110 | //整个AppbarLayout高度
111 | mAppBarHeight = appBarLayout.getMeasuredHeight();
112 | //卡片的高度
113 | mCardViewHeight = mCardView.getMeasuredHeight();
114 | //折叠正常的高度
115 | mLimitHeight = mAppBarHeight - (int) (mCardViewHeight * scaleValue);
116 |
117 | //默认1s折叠
118 | appBarLayout.postDelayed(new Runnable() {
119 | @Override
120 | public void run() {
121 | ValueAnimator anim = ValueAnimator.ofFloat(0, 1f).setDuration(200);
122 | anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
123 | @Override
124 | public void onAnimationUpdate(ValueAnimator animation) {
125 | float value = (float) animation.getAnimatedValue();
126 | appBarLayout.setBottom((int) (mAppBarHeight - value * mCardViewHeight * scaleValue));
127 | }
128 | });
129 | anim.start();
130 | }
131 | }, 1000);
132 |
133 | }
134 |
135 |
136 | /**
137 | * 混动
138 | *
139 | * @param child
140 | * @param target
141 | * @param dy
142 | */
143 | private void scrollY(AppBarLayout child, View target, int dy) {
144 | mTotalDy += -dy;
145 | mTotalDy = Math.min(mTotalDy, mLimitHeight);
146 | mLastScale = Math.max(1f, 1f + (mTotalDy / mLimitHeight));
147 | mLastBottom = mLimitHeight + (int) (mCardViewHeight * scaleValue * (mLastScale - 1));
148 | child.setBottom(mLastBottom);
149 | target.setScrollY(0);
150 | }
151 |
152 |
153 | /**
154 | * 根据滑动设置 toolbar 名字显示效果
155 | *
156 | * @param target
157 | * @param dy
158 | */
159 | private void setViewAlpha(View target, int dy) {
160 | float percent = Math.abs(target.getY() / mLimitHeight);
161 | if (percent >= 1) {
162 | percent = 1f;
163 | }
164 | //设置toolbar的透明度
165 | mToolBar.setAlpha(percent);
166 |
167 | //设置名字缩放
168 | mNameTitle.setScaleX(Math.max(0.8f, 1 - percent));
169 | mNameTitle.setScaleY(Math.max(0.8f, 1 - percent));
170 |
171 | //设置名字平移
172 | int offset = mNameTitle.getTop() - mToolBar.getTop();
173 | mNameTitle.setTranslationY(-offset * percent);
174 | }
175 |
176 | /**
177 | * 恢复位置
178 | *
179 | * @param abl
180 | */
181 | private void recovery(final AppBarLayout abl) {
182 | if (mTotalDy >= 0) {
183 | mTotalDy = 0;
184 | if (isAnimate) {
185 | ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1f).setDuration(200);
186 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
187 | @Override
188 | public void onAnimationUpdate(ValueAnimator animation) {
189 | float value = (float) animation.getAnimatedValue();
190 | int offsetY = abl.getBottom() - mLimitHeight;
191 | abl.setBottom((int) (abl.getBottom() - (value * offsetY)));
192 | abl.setScrollY(0);
193 | }
194 | });
195 | valueAnimator.start();
196 | } else {
197 | abl.setBottom(mLimitHeight);
198 | abl.setScrollY(0);
199 | }
200 | }
201 | }
202 |
203 |
204 | }
205 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zwl/mybossdemo/note:
--------------------------------------------------------------------------------
1 | 设置显示时的高度问题
2 | 打开 BottomSheetDialog 的 setContentView() ->wrapInBottomSheet()
3 |
4 | 可以看到 BottomSheetDialog 中用到了 BottomSheetBehavior ,并且该 Behavior 是通过
5 | id 为 design_bottom_sheet 的 View 来获取的。也就是说我们只要获取到这个 View 就可以调用
6 | BottomSheetBehavior 的 setPeekHeight() 方法来设置 BottomSheetDialog 显示时的高度了。
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |