layout_scrollFlags属性不完全解析及总结
Posted ihrthk
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了layout_scrollFlags属性不完全解析及总结相关的知识,希望对你有一定的参考价值。
layout_scrollFlags属性不完全解析及总结
0.前言
分析源码com.android.support:design:27.1.1库的android.support.design.widget.AppBarLayout.java
现在只是简单分析向下滑动的情况,当dy为负数。这里的min和max就是通过setTopAndBottomOffset方法对AppBarLayout的滑动范围进行上下方向的进行限制。
第一种情况:
- 如果只有含有SCROLL_FLAG_SCROLL,不有含SCROLL_FLAG_ENTER_ALWAYS。
- 那有child.getDownNestedPreScrollRange就为0(因为(flags不含有LayoutParams.FLAG_QUICK_RETURN),即min==max。
- 也就是父View放弃优先子View进行滚动,AppBarLayout也不会优先「内容视图」滚动。
- 即先滚动『内容视图』,再滚动AppBarLayout
第二种情况:
- 如果同时含有SCROLL_FLAG_SCROLL和SCROLL_FLAG_ENTER_ALWAYS
- 则min = childHeight + lp.topMargin + lp.bottomMargin,max = mDownPreScrollRange = 0
- 使用dy和curOffset来计算curOffset
- 使用setTopAndBottomOffset(newOffset)来设置AppBarLayout,同时计算consumed = curOffset - newOffset
- 当consumed为0的时候,即放弃优先滚动AppBarLayout。
- 即先滚动AppBarLayout,再滚动『内容视图』
//AppBarLayout.Behavior类的
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
View target, int dx, int dy, int[] consumed, int type)
if (dy != 0)
int min, max;
if (dy < 0)
// We're scrolling down
min = -child.getTotalScrollRange();
max = min + child.getDownNestedPreScrollRange();
else
// We're scrolling up
min = -child.getUpNestedPreScrollRange();
max = 0;
if (min != max)
consumed[1] = scroll(coordinatorLayout, child, dy, min, max);
1.分析getTotalScrollRange()方法
//AppBarLayout类的
/**
* Returns the scroll range of all children.
*
* @return the scroll range in px
*/
public final int getTotalScrollRange()
if (mTotalScrollRange != INVALID_SCROLL_RANGE)
return mTotalScrollRange;
int range = 0;
for (int i = 0, z = getChildCount(); i < z; i++)
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int childHeight = child.getMeasuredHeight();
final int flags = lp.mScrollFlags;
if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0)
// We're set to scroll so add the child's height
range += childHeight + lp.topMargin + lp.bottomMargin;
if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0)
// For a collapsing scroll, we to take the collapsed height into account.
// We also break straight away since later views can't scroll beneath
// us
range -= ViewCompat.getMinimumHeight(child);
break;
else
// As soon as a view doesn't have the scroll flag, we end the range calculation.
// This is because views below can not scroll under a fixed view.
break;
return mTotalScrollRange = Math.max(0, range - getTopInset());
- 首先getTotalScrollRange只计算一次,除非调用invalidateScrollRanges。
- 依次取出AppBarLayout的子View,并取出对应的mScrollFlags
- 判断mScrollFlags是否含有SCROLL_FLAG_SCROLL
- 如果没有,则结束当前的滑动范围计算
- 如果含有SCROLL_FLAG_SCROLL,滑动范围就是childHeight + lp.topMargin + lp.bottomMargin
- 再次判断是否含有SCROLL_FLAG_EXIT_UNTIL_COLLAPSED,有的话滑动范围还需要减去ViewCompat.getMinimumHeight(child)
小结:
SCROLL_FLAG_SCROLL影响的是AppBarLayout最大可滑动距离,
即childHeight + lp.topMargin + lp.bottomMargin
如果再加上SCROLL_FLAG_EXIT_UNTIL_COLLAPSED,它会卡在最小高度那里。即 range -= ViewCompat.getMinimumHeight(child),因为它的可滑动距离少了minHeight。
2.分析getDownNestedPreScrollRange()方法
/**
* Return the scroll range when scrolling down from a nested pre-scroll.
*/
int getDownNestedPreScrollRange()
if (mDownPreScrollRange != INVALID_SCROLL_RANGE)
// If we already have a valid value, return it
return mDownPreScrollRange;
int range = 0;
for (int i = getChildCount() - 1; i >= 0; i--)
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int childHeight = child.getMeasuredHeight();
final int flags = lp.mScrollFlags;
if ((flags & LayoutParams.FLAG_QUICK_RETURN) == LayoutParams.FLAG_QUICK_RETURN)
// First take the margin into account
range += lp.topMargin + lp.bottomMargin;
// The view has the quick return flag combination...
if ((flags & LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED) != 0)
// If they're set to enter collapsed, use the minimum height
range += ViewCompat.getMinimumHeight(child);
else if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0)
// Only enter by the amount of the collapsed height
range += childHeight - ViewCompat.getMinimumHeight(child);
else
// Else use the full height (minus the top inset)
range += childHeight - getTopInset();
else if (range > 0)
// If we've hit an non-quick return scrollable view, and we've already hit a
// quick return view, return now
break;
return mDownPreScrollRange = Math.max(0, range);
- 首先getDownNestedPreScrollRange只计算一次,除非调用invalidateScrollRanges。
- 依次取出AppBarLayout的子View,并取出对应的mScrollFlags
- 判断mScrollFlags是否含有FLAG_QUICK_RETURN(即SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS)
- 如果没有,则结束当前的滑动范围计算
- 如果有,先计算range += lp.topMargin + lp.bottomMargin;
- 再判断是否含有SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED,有则range += ViewCompat.getMinimumHeight(child);
小结:
优先滑出AppBarLayout,然后在滑内容
保持MinHeight,优先划剩下的AppBarLayout,最优再划内容
childHeight - ViewCompat.getMinimumHeight(child);
3.总结
- layout_scrollFlags默认值为0,不起作用
- 优先滚动**内容(Content)**使用scroll
- 优先滚动AppBarLayout使用scroll|enterAlways
- 只需要卡住效果,可以在AppBarLayout里面,给CollapsingToolbarLayout添加一个或多个Sibling(AppBarLayout是一个垂直的LinearLayout)
- 折叠卡住效果需要exitUntilCollapsed和minHeight一起使用
- 需要三段折叠滑动效果,考虑是使用enterAlwaysCollapsed(即先滑minHeight,再滑内容(Content),最后滑剩余的AppBarLayout)
以上是关于layout_scrollFlags属性不完全解析及总结的主要内容,如果未能解决你的问题,请参考以下文章
如何以编程方式为工具栏设置 app:layout_scrollFlags
BottomAppBar 和 FloatingActionButton 应用程序:layout_scrollFlags 行为