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的滑动范围进行上下方向的进行限制。

第一种情况:

  1. 如果只有含有SCROLL_FLAG_SCROLL,不有含SCROLL_FLAG_ENTER_ALWAYS。
  2. 那有child.getDownNestedPreScrollRange就为0(因为(flags不含有LayoutParams.FLAG_QUICK_RETURN),即min==max。
  3. 也就是父View放弃优先子View进行滚动,AppBarLayout也不会优先「内容视图」滚动。
  4. 即先滚动『内容视图』,再滚动AppBarLayout

第二种情况:

  1. 如果同时含有SCROLL_FLAG_SCROLL和SCROLL_FLAG_ENTER_ALWAYS
  2. 则min = childHeight + lp.topMargin + lp.bottomMargin,max = mDownPreScrollRange = 0
  3. 使用dy和curOffset来计算curOffset
  4. 使用setTopAndBottomOffset(newOffset)来设置AppBarLayout,同时计算consumed = curOffset - newOffset
  5. 当consumed为0的时候,即放弃优先滚动AppBarLayout。
  6. 即先滚动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());

  1. 首先getTotalScrollRange只计算一次,除非调用invalidateScrollRanges。
  2. 依次取出AppBarLayout的子View,并取出对应的mScrollFlags
  3. 判断mScrollFlags是否含有SCROLL_FLAG_SCROLL
  4. 如果没有,则结束当前的滑动范围计算
  5. 如果含有SCROLL_FLAG_SCROLL,滑动范围就是childHeight + lp.topMargin + lp.bottomMargin
  6. 再次判断是否含有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);

  1. 首先getDownNestedPreScrollRange只计算一次,除非调用invalidateScrollRanges。
  2. 依次取出AppBarLayout的子View,并取出对应的mScrollFlags
  3. 判断mScrollFlags是否含有FLAG_QUICK_RETURN(即SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS)
  4. 如果没有,则结束当前的滑动范围计算
  5. 如果有,先计算range += lp.topMargin + lp.bottomMargin;
  6. 再判断是否含有SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED,有则range += ViewCompat.getMinimumHeight(child);

小结:
优先滑出AppBarLayout,然后在滑内容

保持MinHeight,优先划剩下的AppBarLayout,最优再划内容
childHeight - ViewCompat.getMinimumHeight(child);

3.总结

  1. layout_scrollFlags默认值为0,不起作用
  2. 优先滚动**内容(Content)**使用scroll
  3. 优先滚动AppBarLayout使用scroll|enterAlways
  4. 只需要卡住效果,可以在AppBarLayout里面,给CollapsingToolbarLayout添加一个或多个Sibling(AppBarLayout是一个垂直的LinearLayout)
  5. 折叠卡住效果需要exitUntilCollapsed和minHeight一起使用
  6. 需要三段折叠滑动效果,考虑是使用enterAlwaysCollapsed(即先滑minHeight,再滑内容(Content),最后滑剩余的AppBarLayout)

以上是关于layout_scrollFlags属性不完全解析及总结的主要内容,如果未能解决你的问题,请参考以下文章

layout_scrollFlags属性不完全解析及总结

如何以编程方式为工具栏设置 app:layout_scrollFlags

材料设计 layout_scrollFlags 含义

BottomAppBar 和 FloatingActionButton 应用程序:layout_scrollFlags 行为

如何填充属性树?

JVM-class文件完全解析-属性表集合