Fix CollapsingToolbarLayout with child margins
CTL doesn't currently handle pinnable children with
top/bottom margins very well. This CL fixes it.
BUG: 29497596
Change-Id: If7799b28aaec6648d5add7e482862a0683f3ad3e
diff --git a/design/src/android/support/design/widget/CollapsingToolbarLayout.java b/design/src/android/support/design/widget/CollapsingToolbarLayout.java
index 68ce5d0..e354989 100644
--- a/design/src/android/support/design/widget/CollapsingToolbarLayout.java
+++ b/design/src/android/support/design/widget/CollapsingToolbarLayout.java
@@ -48,6 +48,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import static android.support.design.widget.MathUtils.constrain;
import static android.support.design.widget.ViewUtils.objectEquals;
/**
@@ -411,21 +412,18 @@
== ViewCompat.LAYOUT_DIRECTION_RTL;
// Update the collapsed bounds
- int bottomOffset = 0;
- if (mToolbarDirectChild != null && mToolbarDirectChild != this) {
- final LayoutParams lp = (LayoutParams) mToolbarDirectChild.getLayoutParams();
- bottomOffset = lp.bottomMargin;
- }
+ final int maxOffset = getMaxOffsetForPinChild(
+ mToolbarDirectChild != null ? mToolbarDirectChild : mToolbar);
ViewGroupUtils.getDescendantRect(this, mDummyView, mTmpRect);
mCollapsingTextHelper.setCollapsedBounds(
mTmpRect.left + (isRtl
? mToolbar.getTitleMarginEnd()
: mToolbar.getTitleMarginStart()),
- bottom + mToolbar.getTitleMarginTop() - mTmpRect.height() - bottomOffset,
+ mTmpRect.top + maxOffset + mToolbar.getTitleMarginTop(),
mTmpRect.right + (isRtl
? mToolbar.getTitleMarginStart()
: mToolbar.getTitleMarginEnd()),
- bottom - bottomOffset - mToolbar.getTitleMarginBottom());
+ mTmpRect.bottom + maxOffset - mToolbar.getTitleMarginBottom());
// Update the expanded bounds
mCollapsingTextHelper.setExpandedBounds(
@@ -1197,6 +1195,12 @@
}
}
+ final int getMaxOffsetForPinChild(View child) {
+ final ViewOffsetHelper offsetHelper = getViewOffsetHelper(child);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ return getHeight() - (offsetHelper.getLayoutTop() + child.getHeight() + lp.bottomMargin);
+ }
+
private class OffsetUpdateListener implements AppBarLayout.OnOffsetChangedListener {
@Override
public void onOffsetChanged(AppBarLayout layout, int verticalOffset) {
@@ -1211,9 +1215,8 @@
switch (lp.mCollapseMode) {
case LayoutParams.COLLAPSE_MODE_PIN:
- if (getHeight() - insetTop + verticalOffset >= child.getHeight()) {
- offsetHelper.setTopAndBottomOffset(-verticalOffset);
- }
+ offsetHelper.setTopAndBottomOffset(
+ constrain(-verticalOffset, 0, getMaxOffsetForPinChild(child)));
break;
case LayoutParams.COLLAPSE_MODE_PARALLAX:
offsetHelper.setTopAndBottomOffset(
diff --git a/design/src/android/support/design/widget/ViewOffsetHelper.java b/design/src/android/support/design/widget/ViewOffsetHelper.java
index fc16d28..088430a 100644
--- a/design/src/android/support/design/widget/ViewOffsetHelper.java
+++ b/design/src/android/support/design/widget/ViewOffsetHelper.java
@@ -91,4 +91,12 @@
public int getLeftAndRightOffset() {
return mOffsetLeft;
}
+
+ public int getLayoutTop() {
+ return mLayoutTop;
+ }
+
+ public int getLayoutLeft() {
+ return mLayoutLeft;
+ }
}
\ No newline at end of file
diff --git a/design/tests/res/layout/design_appbar_toolbar_collapse_pin_margins.xml b/design/tests/res/layout/design_appbar_toolbar_collapse_pin_margins.xml
new file mode 100644
index 0000000..92a7268
--- /dev/null
+++ b/design/tests/res/layout/design_appbar_toolbar_collapse_pin_margins.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<android.support.design.widget.CoordinatorLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/col"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <android.support.design.widget.AppBarLayout
+ android:id="@+id/app_bar"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/appbar_height">
+
+ <android.support.design.widget.CollapsingToolbarLayout
+ android:id="@+id/collapsing_app_bar"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:layout_scrollFlags="scroll">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_height="?attr/actionBarSize"
+ android:layout_width="match_parent"
+ android:layout_marginTop="20dp"
+ android:layout_marginBottom="20dp"
+ app:layout_collapseMode="pin"/>
+
+ </android.support.design.widget.CollapsingToolbarLayout>
+
+ </android.support.design.widget.AppBarLayout>
+
+ <include layout="@layout/include_appbar_scrollview" />
+
+</android.support.design.widget.CoordinatorLayout>
diff --git a/design/tests/res/values/strings.xml b/design/tests/res/values/strings.xml
index 380cbe3..f456921 100644
--- a/design/tests/res/values/strings.xml
+++ b/design/tests/res/values/strings.xml
@@ -35,6 +35,7 @@
<string name="design_appbar_toolbar_scroll_tabs_scroll">AppBar/Toolbar Scroll + Tabs Scroll</string>
<string name="design_appbar_toolbar_scroll_tabs_scroll_snap">AppBar/Toolbar Scroll + Tabs Scroll + Snap</string>
<string name="design_appbar_toolbar_scroll_tabs_pin">AppBar/Toolbar Scroll + Tabs Pin</string>
+ <string name="design_appbar_collapsing_toolbar_pin_margins">AppBar/Collapsing Toolbar (pinned + margins)</string>
<string name="design_appbar_collapsing_toolbar_with_image">AppBar/Collapsing Toolbar + Parallax Image</string>
<string name="design_appbar_anchored_fab_margin_bottom">AppBar + anchored FAB with bottom margin</string>
<string name="design_appbar_anchored_fab_margin_top">AppBar + anchored FAB with top margin</string>
diff --git a/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarTest.java b/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarTest.java
index 7be4801..8148720 100644
--- a/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarTest.java
+++ b/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarTest.java
@@ -381,5 +381,60 @@
mCollapsingToolbar.addView(view);
}
});
+
+ }
+
+ @Test
+ public void testPinnedToolbarWithMargins() throws Throwable {
+ configureContent(R.layout.design_appbar_toolbar_collapse_pin_margins,
+ R.string.design_appbar_collapsing_toolbar_pin_margins);
+
+ CollapsingToolbarLayout.LayoutParams toolbarLp =
+ (CollapsingToolbarLayout.LayoutParams) mToolbar.getLayoutParams();
+ assertEquals(CollapsingToolbarLayout.LayoutParams.COLLAPSE_MODE_PIN,
+ toolbarLp.getCollapseMode());
+
+ final int[] appbarOnScreenXY = new int[2];
+ final int[] toolbarOnScreenXY = new int[2];
+ mAppBar.getLocationOnScreen(appbarOnScreenXY);
+ mToolbar.getLocationOnScreen(toolbarOnScreenXY);
+
+ final int originalAppbarTop = appbarOnScreenXY[1];
+ final int originalAppbarBottom = originalAppbarTop + mAppBar.getHeight();
+ final int centerX = appbarOnScreenXY[0] + mAppBar.getWidth() / 2;
+
+ final int toolbarHeight = mToolbar.getHeight();
+ final int toolbarVerticalMargins = toolbarLp.topMargin + toolbarLp.bottomMargin;
+ final int appbarHeight = mAppBar.getHeight();
+
+ // Perform a swipe-up gesture across the horizontal center of the screen.
+ int swipeAmount = appbarHeight - toolbarHeight - toolbarVerticalMargins;
+ performVerticalSwipeUpGesture(
+ R.id.coordinator_layout,
+ centerX,
+ originalAppbarBottom + (3 * swipeAmount / 2),
+ swipeAmount);
+
+ mAppBar.getLocationOnScreen(appbarOnScreenXY);
+ mToolbar.getLocationOnScreen(toolbarOnScreenXY);
+ // At this point the toolbar should be visually pinned to the bottom of the appbar layout,
+ // observing it's margins
+ // The toolbar should still be visually pinned to the bottom of the appbar layout
+ assertEquals(originalAppbarTop, toolbarOnScreenXY[1] - toolbarLp.topMargin, 1);
+
+ // Swipe up again, this time just 50% of the margin size
+ swipeAmount = toolbarVerticalMargins / 2;
+ performVerticalSwipeUpGesture(
+ R.id.coordinator_layout,
+ centerX,
+ originalAppbarBottom + (3 * swipeAmount / 2),
+ swipeAmount);
+
+ mAppBar.getLocationOnScreen(appbarOnScreenXY);
+ mToolbar.getLocationOnScreen(toolbarOnScreenXY);
+
+ // The toolbar should still be visually pinned to the bottom of the appbar layout
+ assertEquals(appbarOnScreenXY[1] + appbarHeight,
+ toolbarOnScreenXY[1] + toolbarHeight + toolbarLp.bottomMargin, 1);
}
}