8282import  java .lang .annotation .Retention ;
8383import  java .lang .annotation .RetentionPolicy ;
8484import  java .lang .ref .WeakReference ;
85+ import  java .util .ArrayDeque ;
8586import  java .util .ArrayList ;
8687import  java .util .LinkedHashSet ;
8788import  java .util .List ;
89+ import  java .util .Queue ;
8890
8991/** 
9092 * AppBarLayout is a vertical {@link LinearLayout} which implements many of the features of material 
@@ -227,7 +229,7 @@ public abstract void onUpdate(
227229  private  boolean  liftOnScroll ;
228230  @ Nullable  private  ColorStateList  liftOnScrollColor ;
229231  @ IdRes  private  int  liftOnScrollTargetViewId ;
230-   @ Nullable  private  WeakReference <View > liftOnScrollTargetView ;
232+   @ Nullable  private  WeakReference <View > liftOnScrollTargetViewRef ;
231233  @ Nullable  private  ValueAnimator  liftOnScrollColorAnimator ;
232234  @ Nullable  private  AnimatorUpdateListener  liftOnScrollColorUpdateListener ;
233235  private  final  List <LiftOnScrollListener > liftOnScrollListeners  = new  ArrayList <>();
@@ -841,7 +843,7 @@ protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
841843  protected  void  onDetachedFromWindow () {
842844    super .onDetachedFromWindow ();
843845
844-     clearLiftOnScrollTargetView ();
846+     clearLiftOnScrollTargetViewRef ();
845847  }
846848
847849  boolean  hasChildWithInterpolator () {
@@ -1171,9 +1173,9 @@ public boolean isLiftOnScroll() {
11711173  public  void  setLiftOnScrollTargetView (@ Nullable  View  liftOnScrollTargetView ) {
11721174    this .liftOnScrollTargetViewId  = View .NO_ID ;
11731175    if  (liftOnScrollTargetView  == null ) {
1174-       clearLiftOnScrollTargetView ();
1176+       clearLiftOnScrollTargetViewRef ();
11751177    } else  {
1176-       this .liftOnScrollTargetView  = new  WeakReference <>(liftOnScrollTargetView );
1178+       this .liftOnScrollTargetViewRef  = new  WeakReference <>(liftOnScrollTargetView );
11771179    }
11781180  }
11791181
@@ -1184,7 +1186,7 @@ public void setLiftOnScrollTargetView(@Nullable View liftOnScrollTargetView) {
11841186  public  void  setLiftOnScrollTargetViewId (@ IdRes  int  liftOnScrollTargetViewId ) {
11851187    this .liftOnScrollTargetViewId  = liftOnScrollTargetViewId ;
11861188    // Invalidate cached target view so it will be looked up on next scroll. 
1187-     clearLiftOnScrollTargetView ();
1189+     clearLiftOnScrollTargetViewRef ();
11881190  }
11891191
11901192  /** Sets the color of the {@link AppBarLayout} when it is fully lifted. */ 
@@ -1207,39 +1209,88 @@ public int getLiftOnScrollTargetViewId() {
12071209    return  liftOnScrollTargetViewId ;
12081210  }
12091211
1210-   boolean  shouldLift (@ Nullable  View  defaultScrollingView ) {
1211-     View  scrollingView  = findLiftOnScrollTargetView (defaultScrollingView );
1212-     if  (scrollingView  == null ) {
1213-       scrollingView  = defaultScrollingView ;
1214-     }
1212+   boolean  shouldBeLifted () {
1213+     final  View  scrollingView  = findLiftOnScrollTargetView ();
12151214    return  scrollingView  != null 
12161215        && (scrollingView .canScrollVertically (-1 ) || scrollingView .getScrollY () > 0 );
12171216  }
12181217
12191218  @ Nullable 
1220-   private  View  findLiftOnScrollTargetView (@ Nullable  View  defaultScrollingView ) {
1219+   private  View  findLiftOnScrollTargetView () {
1220+     View  liftOnScrollTargetView  = liftOnScrollTargetViewRef  != null 
1221+         ? liftOnScrollTargetViewRef .get ()
1222+         : null ;
1223+ 
1224+     final  ViewGroup  parent  = (ViewGroup ) getParent ();
1225+ 
12211226    if  (liftOnScrollTargetView  == null  && liftOnScrollTargetViewId  != View .NO_ID ) {
1222-       View  targetView  = null ;
1223-       if  (defaultScrollingView  != null ) {
1224-         targetView  = defaultScrollingView .findViewById (liftOnScrollTargetViewId );
1227+       liftOnScrollTargetView  = parent .findViewById (liftOnScrollTargetViewId );
1228+       if  (liftOnScrollTargetView  != null ) {
1229+         clearLiftOnScrollTargetViewRef ();
1230+         liftOnScrollTargetViewRef  = new  WeakReference <>(liftOnScrollTargetView );
12251231      }
1226-       if  (targetView  == null  && getParent () instanceof  ViewGroup ) {
1227-         // Assumes the scrolling view is a child of the AppBarLayout's parent, 
1228-         // which should be true due to the CoordinatorLayout pattern. 
1229-         targetView  = ((ViewGroup ) getParent ()).findViewById (liftOnScrollTargetViewId );
1232+     }
1233+ 
1234+     return  liftOnScrollTargetView  != null 
1235+         ? liftOnScrollTargetView 
1236+         : getDefaultLiftOnScrollTargetView (parent );
1237+   }
1238+ 
1239+   private  View  getDefaultLiftOnScrollTargetView (@ NonNull  ViewGroup  parent ) {
1240+     for  (int  i  = 0 , z  = parent .getChildCount (); i  < z ; i ++) {
1241+       final  View  child  = parent .getChildAt (i );
1242+       if  (hasScrollingBehavior (child )) {
1243+         final  View  scrollableView  = findClosestScrollableView (child );
1244+         if  (scrollableView  != null ) {
1245+           return  scrollableView ;
1246+         }
12301247      }
1231-       if  (targetView  != null ) {
1232-         liftOnScrollTargetView  = new  WeakReference <>(targetView );
1248+     }
1249+     return  null ;
1250+   }
1251+ 
1252+   private  boolean  hasScrollingBehavior (@ NonNull  View  view ) {
1253+     if  (view .getLayoutParams () instanceof  CoordinatorLayout .LayoutParams ) {
1254+       CoordinatorLayout .LayoutParams  lp  = (CoordinatorLayout .LayoutParams ) view .getLayoutParams ();
1255+       return  lp .getBehavior () instanceof  ScrollingViewBehavior ;
1256+     }
1257+ 
1258+     return  false ;
1259+   }
1260+ 
1261+   @ Nullable 
1262+   private  View  findClosestScrollableView (@ NonNull  View  rootView ) {
1263+     final  Queue <View > queue  = new  ArrayDeque <>();
1264+     queue .add (rootView );
1265+ 
1266+     while  (!queue .isEmpty ()) {
1267+       final  View  view  = queue .remove ();
1268+       if  (isScrollableView (view )) {
1269+         return  view ;
1270+       } else  {
1271+         if  (view  instanceof  ViewGroup ) {
1272+           final  ViewGroup  viewGroup  = (ViewGroup ) view ;
1273+           for  (int  i  = 0 , count  = viewGroup .getChildCount (); i  < count ; i ++) {
1274+             queue .add (viewGroup .getChildAt (i ));
1275+           }
1276+         }
12331277      }
12341278    }
1235-     return  liftOnScrollTargetView  != null  ? liftOnScrollTargetView .get () : null ;
1279+ 
1280+     return  null ;
12361281  }
12371282
1238-   private  void  clearLiftOnScrollTargetView () {
1239-     if  (liftOnScrollTargetView  != null ) {
1240-       liftOnScrollTargetView .clear ();
1283+   private  boolean  isScrollableView (@ NonNull  View  view ) {
1284+     return  view  instanceof  NestedScrollingChild 
1285+         || view  instanceof  AbsListView 
1286+         || view  instanceof  ScrollView ;
1287+   }
1288+ 
1289+   private  void  clearLiftOnScrollTargetViewRef () {
1290+     if  (liftOnScrollTargetViewRef  != null ) {
1291+       liftOnScrollTargetViewRef .clear ();
12411292    }
1242-     liftOnScrollTargetView  = null ;
1293+     liftOnScrollTargetViewRef  = null ;
12431294  }
12441295
12451296  /** 
@@ -1655,12 +1706,12 @@ private boolean canScrollChildren(
16551706
16561707    @ Override 
16571708    public  void  onNestedPreScroll (
1658-         CoordinatorLayout  coordinatorLayout ,
1709+         @ NonNull   CoordinatorLayout  coordinatorLayout ,
16591710        @ NonNull  T  child ,
1660-         View  target ,
1711+         @ NonNull   View  target ,
16611712        int  dx ,
16621713        int  dy ,
1663-         int [] consumed ,
1714+         @ NonNull   int [] consumed ,
16641715        int  type ) {
16651716      if  (dy  != 0 ) {
16661717        int  min ;
@@ -1679,7 +1730,7 @@ public void onNestedPreScroll(
16791730        }
16801731      }
16811732      if  (child .isLiftOnScroll ()) {
1682-         child .setLiftedState (child .shouldLift ( target ));
1733+         child .setLiftedState (child .shouldBeLifted ( ));
16831734      }
16841735    }
16851736
@@ -1710,7 +1761,10 @@ public void onNestedScroll(
17101761
17111762    @ Override 
17121763    public  void  onStopNestedScroll (
1713-         CoordinatorLayout  coordinatorLayout , @ NonNull  T  abl , View  target , int  type ) {
1764+         @ NonNull  CoordinatorLayout  coordinatorLayout ,
1765+         @ NonNull  T  abl ,
1766+         @ NonNull  View  target ,
1767+         int  type ) {
17141768      // onStartNestedScroll for a fling will happen before onStopNestedScroll for the scroll. This 
17151769      // isn't necessarily guaranteed yet, but it should be in the future. We use this to our 
17161770      // advantage to check if a fling (ViewCompat.TYPE_NON_TOUCH) will start after the touch scroll 
@@ -1719,7 +1773,7 @@ public void onStopNestedScroll(
17191773        // If we haven't been flung, or a fling is ending 
17201774        snapToChildIfNeeded (coordinatorLayout , abl );
17211775        if  (abl .isLiftOnScroll ()) {
1722-           abl .setLiftedState (abl .shouldLift ( target ));
1776+           abl .setLiftedState (abl .shouldBeLifted ( ));
17231777        }
17241778      }
17251779
@@ -2114,7 +2168,7 @@ void onFlingFinished(@NonNull CoordinatorLayout parent, @NonNull T layout) {
21142168      // At the end of a manual fling, check to see if we need to snap to the edge-child 
21152169      snapToChildIfNeeded (parent , layout );
21162170      if  (layout .isLiftOnScroll ()) {
2117-         layout .setLiftedState (layout .shouldLift ( findFirstScrollingChild ( parent ) ));
2171+         layout .setLiftedState (layout .shouldBeLifted ( ));
21182172      }
21192173    }
21202174
@@ -2281,9 +2335,7 @@ private void updateAppBarLayoutDrawableState(
22812335      }
22822336
22832337      if  (layout .isLiftOnScroll ()) {
2284-         // Use first scrolling child as default scrolling view for updating lifted state because 
2285-         // it represents the content that would be scrolled beneath the app bar. 
2286-         lifted  = layout .shouldLift (findFirstScrollingChild (parent ));
2338+         lifted  = layout .shouldBeLifted ();
22872339      }
22882340
22892341      final  boolean  changed  = layout .setLiftedState (lifted );
@@ -2333,19 +2385,6 @@ private static View getAppBarChildOnOffset(
23332385      return  null ;
23342386    }
23352387
2336-     @ Nullable 
2337-     private  View  findFirstScrollingChild (@ NonNull  CoordinatorLayout  parent ) {
2338-       for  (int  i  = 0 , z  = parent .getChildCount (); i  < z ; i ++) {
2339-         final  View  child  = parent .getChildAt (i );
2340-         if  (child  instanceof  NestedScrollingChild 
2341-             || child  instanceof  AbsListView 
2342-             || child  instanceof  ScrollView ) {
2343-           return  child ;
2344-         }
2345-       }
2346-       return  null ;
2347-     }
2348- 
23492388    @ Override 
23502389    int  getTopBottomOffsetForScrollingSibling () {
23512390      return  getTopAndBottomOffset () + offsetDelta ;
@@ -2482,7 +2521,7 @@ public boolean layoutDependsOn(CoordinatorLayout parent, View child, View depend
24822521    public  boolean  onDependentViewChanged (
24832522        @ NonNull  CoordinatorLayout  parent , @ NonNull  View  child , @ NonNull  View  dependency ) {
24842523      offsetChildAsNeeded (child , dependency );
2485-       updateLiftedStateIfNeeded (child ,  dependency );
2524+       updateLiftedStateIfNeeded (dependency );
24862525      return  false ;
24872526    }
24882527
@@ -2587,11 +2626,11 @@ int getScrollRange(View v) {
25872626      }
25882627    }
25892628
2590-     private  void  updateLiftedStateIfNeeded (View   child ,  View  dependency ) {
2629+     private  void  updateLiftedStateIfNeeded (@ NonNull  View  dependency ) {
25912630      if  (dependency  instanceof  AppBarLayout ) {
25922631        AppBarLayout  appBarLayout  = (AppBarLayout ) dependency ;
25932632        if  (appBarLayout .isLiftOnScroll ()) {
2594-           appBarLayout .setLiftedState (appBarLayout .shouldLift ( child ));
2633+           appBarLayout .setLiftedState (appBarLayout .shouldBeLifted ( ));
25952634        }
25962635      }
25972636    }
0 commit comments