7474import com .google .android .material .color .MaterialColors ;
7575import com .google .android .material .drawable .DrawableUtils ;
7676import com .google .android .material .internal .ThemeEnforcement ;
77+ import com .google .android .material .internal .ViewUtils ;
7778import com .google .android .material .motion .MotionUtils ;
7879import com .google .android .material .resources .MaterialResources ;
7980import com .google .android .material .shape .MaterialShapeDrawable ;
8283import java .lang .annotation .Retention ;
8384import java .lang .annotation .RetentionPolicy ;
8485import java .lang .ref .WeakReference ;
86+ import java .util .ArrayDeque ;
8587import java .util .ArrayList ;
8688import java .util .LinkedHashSet ;
8789import java .util .List ;
90+ import java .util .Queue ;
8891
8992/**
9093 * AppBarLayout is a vertical {@link LinearLayout} which implements many of the features of material
@@ -226,7 +229,7 @@ public abstract void onUpdate(
226229
227230 private boolean liftOnScroll ;
228231 @ IdRes private int liftOnScrollTargetViewId ;
229- @ Nullable private WeakReference <View > liftOnScrollTargetView ;
232+ @ Nullable private WeakReference <View > liftOnScrollTargetViewRef ;
230233 private final boolean hasLiftOnScrollColor ;
231234 @ Nullable private ValueAnimator liftOnScrollColorAnimator ;
232235 @ Nullable private AnimatorUpdateListener liftOnScrollColorUpdateListener ;
@@ -823,7 +826,7 @@ protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
823826 protected void onDetachedFromWindow () {
824827 super .onDetachedFromWindow ();
825828
826- clearLiftOnScrollTargetView ();
829+ clearLiftOnScrollTargetViewRef ();
827830 }
828831
829832 boolean hasChildWithInterpolator () {
@@ -1148,9 +1151,9 @@ public boolean isLiftOnScroll() {
11481151 public void setLiftOnScrollTargetView (@ Nullable View liftOnScrollTargetView ) {
11491152 this .liftOnScrollTargetViewId = View .NO_ID ;
11501153 if (liftOnScrollTargetView == null ) {
1151- clearLiftOnScrollTargetView ();
1154+ clearLiftOnScrollTargetViewRef ();
11521155 } else {
1153- this .liftOnScrollTargetView = new WeakReference <>(liftOnScrollTargetView );
1156+ this .liftOnScrollTargetViewRef = new WeakReference <>(liftOnScrollTargetView );
11541157 }
11551158 }
11561159
@@ -1161,7 +1164,7 @@ public void setLiftOnScrollTargetView(@Nullable View liftOnScrollTargetView) {
11611164 public void setLiftOnScrollTargetViewId (@ IdRes int liftOnScrollTargetViewId ) {
11621165 this .liftOnScrollTargetViewId = liftOnScrollTargetViewId ;
11631166 // Invalidate cached target view so it will be looked up on next scroll.
1164- clearLiftOnScrollTargetView ();
1167+ clearLiftOnScrollTargetViewRef ();
11651168 }
11661169
11671170 /**
@@ -1173,39 +1176,88 @@ public int getLiftOnScrollTargetViewId() {
11731176 return liftOnScrollTargetViewId ;
11741177 }
11751178
1176- boolean shouldLift (@ Nullable View defaultScrollingView ) {
1177- View scrollingView = findLiftOnScrollTargetView (defaultScrollingView );
1178- if (scrollingView == null ) {
1179- scrollingView = defaultScrollingView ;
1180- }
1179+ boolean shouldBeLifted () {
1180+ final View scrollingView = findLiftOnScrollTargetView ();
11811181 return scrollingView != null
11821182 && (scrollingView .canScrollVertically (-1 ) || scrollingView .getScrollY () > 0 );
11831183 }
11841184
11851185 @ Nullable
1186- private View findLiftOnScrollTargetView (@ Nullable View defaultScrollingView ) {
1186+ private View findLiftOnScrollTargetView () {
1187+ View liftOnScrollTargetView = liftOnScrollTargetViewRef != null
1188+ ? liftOnScrollTargetViewRef .get ()
1189+ : null ;
1190+
1191+ final ViewGroup parent = (ViewGroup ) getParent ();
1192+
11871193 if (liftOnScrollTargetView == null && liftOnScrollTargetViewId != View .NO_ID ) {
1188- View targetView = null ;
1189- if (defaultScrollingView != null ) {
1190- targetView = defaultScrollingView .findViewById (liftOnScrollTargetViewId );
1194+ liftOnScrollTargetView = parent .findViewById (liftOnScrollTargetViewId );
1195+ if (liftOnScrollTargetView != null ) {
1196+ clearLiftOnScrollTargetViewRef ();
1197+ liftOnScrollTargetViewRef = new WeakReference <>(liftOnScrollTargetView );
11911198 }
1192- if (targetView == null && getParent () instanceof ViewGroup ) {
1193- // Assumes the scrolling view is a child of the AppBarLayout's parent,
1194- // which should be true due to the CoordinatorLayout pattern.
1195- targetView = ((ViewGroup ) getParent ()).findViewById (liftOnScrollTargetViewId );
1199+ }
1200+
1201+ return liftOnScrollTargetView != null
1202+ ? liftOnScrollTargetView
1203+ : getDefaultLiftOnScrollTargetView (parent );
1204+ }
1205+
1206+ private View getDefaultLiftOnScrollTargetView (@ NonNull ViewGroup parent ) {
1207+ for (int i = 0 , z = parent .getChildCount (); i < z ; i ++) {
1208+ final View child = parent .getChildAt (i );
1209+ if (hasScrollingBehavior (child )) {
1210+ final View scrollableView = findClosestScrollableView (child );
1211+ if (scrollableView != null ) {
1212+ return scrollableView ;
1213+ }
11961214 }
1197- if (targetView != null ) {
1198- liftOnScrollTargetView = new WeakReference <>(targetView );
1215+ }
1216+ return null ;
1217+ }
1218+
1219+ private boolean hasScrollingBehavior (@ NonNull View view ) {
1220+ if (view .getLayoutParams () instanceof CoordinatorLayout .LayoutParams ) {
1221+ CoordinatorLayout .LayoutParams lp = (CoordinatorLayout .LayoutParams ) view .getLayoutParams ();
1222+ return lp .getBehavior () instanceof ScrollingViewBehavior ;
1223+ }
1224+
1225+ return false ;
1226+ }
1227+
1228+ @ Nullable
1229+ private View findClosestScrollableView (@ NonNull View rootView ) {
1230+ final Queue <View > queue = new ArrayDeque <>();
1231+ queue .add (rootView );
1232+
1233+ while (!queue .isEmpty ()) {
1234+ final View view = queue .remove ();
1235+ if (isScrollableView (view )) {
1236+ return view ;
1237+ } else {
1238+ if (view instanceof ViewGroup ) {
1239+ final ViewGroup viewGroup = (ViewGroup ) view ;
1240+ for (int i = 0 , count = viewGroup .getChildCount (); i < count ; i ++) {
1241+ queue .add (viewGroup .getChildAt (i ));
1242+ }
1243+ }
11991244 }
12001245 }
1201- return liftOnScrollTargetView != null ? liftOnScrollTargetView .get () : null ;
1246+
1247+ return null ;
12021248 }
12031249
1204- private void clearLiftOnScrollTargetView () {
1205- if (liftOnScrollTargetView != null ) {
1206- liftOnScrollTargetView .clear ();
1250+ private boolean isScrollableView (@ NonNull View view ) {
1251+ return view instanceof NestedScrollingChild
1252+ || view instanceof AbsListView
1253+ || view instanceof ScrollView ;
1254+ }
1255+
1256+ private void clearLiftOnScrollTargetViewRef () {
1257+ if (liftOnScrollTargetViewRef != null ) {
1258+ liftOnScrollTargetViewRef .clear ();
12071259 }
1208- liftOnScrollTargetView = null ;
1260+ liftOnScrollTargetViewRef = null ;
12091261 }
12101262
12111263 /**
@@ -1621,12 +1673,12 @@ private boolean canScrollChildren(
16211673
16221674 @ Override
16231675 public void onNestedPreScroll (
1624- CoordinatorLayout coordinatorLayout ,
1676+ @ NonNull CoordinatorLayout coordinatorLayout ,
16251677 @ NonNull T child ,
1626- View target ,
1678+ @ NonNull View target ,
16271679 int dx ,
16281680 int dy ,
1629- int [] consumed ,
1681+ @ NonNull int [] consumed ,
16301682 int type ) {
16311683 if (dy != 0 ) {
16321684 int min ;
@@ -1645,7 +1697,7 @@ public void onNestedPreScroll(
16451697 }
16461698 }
16471699 if (child .isLiftOnScroll ()) {
1648- child .setLiftedState (child .shouldLift ( target ));
1700+ child .setLiftedState (child .shouldBeLifted ( ));
16491701 }
16501702 }
16511703
@@ -1676,7 +1728,10 @@ public void onNestedScroll(
16761728
16771729 @ Override
16781730 public void onStopNestedScroll (
1679- CoordinatorLayout coordinatorLayout , @ NonNull T abl , View target , int type ) {
1731+ @ NonNull CoordinatorLayout coordinatorLayout ,
1732+ @ NonNull T abl ,
1733+ @ NonNull View target ,
1734+ int type ) {
16801735 // onStartNestedScroll for a fling will happen before onStopNestedScroll for the scroll. This
16811736 // isn't necessarily guaranteed yet, but it should be in the future. We use this to our
16821737 // advantage to check if a fling (ViewCompat.TYPE_NON_TOUCH) will start after the touch scroll
@@ -1685,7 +1740,7 @@ public void onStopNestedScroll(
16851740 // If we haven't been flung, or a fling is ending
16861741 snapToChildIfNeeded (coordinatorLayout , abl );
16871742 if (abl .isLiftOnScroll ()) {
1688- abl .setLiftedState (abl .shouldLift ( target ));
1743+ abl .setLiftedState (abl .shouldBeLifted ( ));
16891744 }
16901745 }
16911746
@@ -2080,7 +2135,7 @@ void onFlingFinished(@NonNull CoordinatorLayout parent, @NonNull T layout) {
20802135 // At the end of a manual fling, check to see if we need to snap to the edge-child
20812136 snapToChildIfNeeded (parent , layout );
20822137 if (layout .isLiftOnScroll ()) {
2083- layout .setLiftedState (layout .shouldLift ( findFirstScrollingChild ( parent ) ));
2138+ layout .setLiftedState (layout .shouldBeLifted ( ));
20842139 }
20852140 }
20862141
@@ -2247,9 +2302,7 @@ private void updateAppBarLayoutDrawableState(
22472302 }
22482303
22492304 if (layout .isLiftOnScroll ()) {
2250- // Use first scrolling child as default scrolling view for updating lifted state because
2251- // it represents the content that would be scrolled beneath the app bar.
2252- lifted = layout .shouldLift (findFirstScrollingChild (parent ));
2305+ lifted = layout .shouldBeLifted ();
22532306 }
22542307
22552308 final boolean changed = layout .setLiftedState (lifted );
@@ -2299,19 +2352,6 @@ private static View getAppBarChildOnOffset(
22992352 return null ;
23002353 }
23012354
2302- @ Nullable
2303- private View findFirstScrollingChild (@ NonNull CoordinatorLayout parent ) {
2304- for (int i = 0 , z = parent .getChildCount (); i < z ; i ++) {
2305- final View child = parent .getChildAt (i );
2306- if (child instanceof NestedScrollingChild
2307- || child instanceof AbsListView
2308- || child instanceof ScrollView ) {
2309- return child ;
2310- }
2311- }
2312- return null ;
2313- }
2314-
23152355 @ Override
23162356 int getTopBottomOffsetForScrollingSibling () {
23172357 return getTopAndBottomOffset () + offsetDelta ;
@@ -2448,7 +2488,7 @@ public boolean layoutDependsOn(CoordinatorLayout parent, View child, View depend
24482488 public boolean onDependentViewChanged (
24492489 @ NonNull CoordinatorLayout parent , @ NonNull View child , @ NonNull View dependency ) {
24502490 offsetChildAsNeeded (child , dependency );
2451- updateLiftedStateIfNeeded (child , dependency );
2491+ updateLiftedStateIfNeeded (dependency );
24522492 return false ;
24532493 }
24542494
@@ -2553,11 +2593,11 @@ int getScrollRange(View v) {
25532593 }
25542594 }
25552595
2556- private void updateLiftedStateIfNeeded (View child , View dependency ) {
2596+ private void updateLiftedStateIfNeeded (@ NonNull View dependency ) {
25572597 if (dependency instanceof AppBarLayout ) {
25582598 AppBarLayout appBarLayout = (AppBarLayout ) dependency ;
25592599 if (appBarLayout .isLiftOnScroll ()) {
2560- appBarLayout .setLiftedState (appBarLayout .shouldLift ( child ));
2600+ appBarLayout .setLiftedState (appBarLayout .shouldBeLifted ( ));
25612601 }
25622602 }
25632603 }
0 commit comments