/* * 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. */ package android.support.design.widget; import static android.support.test.InstrumentationRegistry.getInstrumentation; import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.action.ViewActions.swipeUp; import static android.support.test.espresso.matcher.ViewMatchers.withId; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.same; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.annotation.TargetApi; import android.graphics.Rect; import android.support.design.testapp.CoordinatorLayoutActivity; import android.support.design.testapp.R; import android.support.design.testutils.CoordinatorLayoutUtils.DependentBehavior; import android.support.design.widget.CoordinatorLayout.Behavior; import android.support.test.filters.MediumTest; import android.support.test.filters.SdkSuppress; import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; import android.support.v4.view.GravityCompat; import android.support.v4.view.ViewCompat; import android.support.v4.view.WindowInsetsCompat; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.View.MeasureSpec; import android.widget.ImageView; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @MediumTest @RunWith(AndroidJUnit4.class) public class CoordinatorLayoutTest { @Rule public final ActivityTestRule<CoordinatorLayoutActivity> activityTestRule = new ActivityTestRule<>(CoordinatorLayoutActivity.class); @Test @TargetApi(21) @SdkSuppress(minSdkVersion = 21) public void testSetFitSystemWindows() throws Throwable { final CoordinatorLayout col = activityTestRule.getActivity().mCoordinatorLayout; final View view = new View(col.getContext()); // Create a mock which calls the default impl of onApplyWindowInsets() final CoordinatorLayout.Behavior<View> mockBehavior = mock(CoordinatorLayout.Behavior.class); doCallRealMethod() .when(mockBehavior) .onApplyWindowInsets(same(col), same(view), any(WindowInsetsCompat.class)); // Assert that the CoL is currently not set to fitSystemWindows assertFalse(col.getFitsSystemWindows()); // Now add a view with our mocked behavior to the CoordinatorLayout view.setFitsSystemWindows(true); activityTestRule.runOnUiThread( new Runnable() { @Override public void run() { final CoordinatorLayout.LayoutParams lp = col.generateDefaultLayoutParams(); lp.setBehavior(mockBehavior); col.addView(view, lp); } }); getInstrumentation().waitForIdleSync(); // Now request some insets and wait for the pass to happen activityTestRule.runOnUiThread( new Runnable() { @Override public void run() { col.requestApplyInsets(); } }); getInstrumentation().waitForIdleSync(); // Verify that onApplyWindowInsets() has not been called verify(mockBehavior, never()) .onApplyWindowInsets(same(col), same(view), any(WindowInsetsCompat.class)); // Now enable fits system windows and wait for a pass to happen activityTestRule.runOnUiThread( new Runnable() { @Override public void run() { col.setFitsSystemWindows(true); } }); getInstrumentation().waitForIdleSync(); // Verify that onApplyWindowInsets() has been called with some insets verify(mockBehavior, atLeastOnce()) .onApplyWindowInsets(same(col), same(view), any(WindowInsetsCompat.class)); } @Test public void testLayoutChildren() throws Throwable { final CoordinatorLayout col = activityTestRule.getActivity().mCoordinatorLayout; final View view = new View(col.getContext()); activityTestRule.runOnUiThread( new Runnable() { @Override public void run() { col.addView(view, 100, 100); } }); getInstrumentation().waitForIdleSync(); int horizontallyCentered = (col.getWidth() - view.getWidth()) / 2; int end = col.getWidth() - view.getWidth(); int verticallyCentered = (col.getHeight() - view.getHeight()) / 2; int bottom = col.getHeight() - view.getHeight(); final int[][] testCases = { // gravity, expected left, expected top {Gravity.NO_GRAVITY, 0, 0}, {Gravity.LEFT, 0, 0}, {GravityCompat.START, 0, 0}, {Gravity.TOP, 0, 0}, {Gravity.CENTER, horizontallyCentered, verticallyCentered}, {Gravity.CENTER_HORIZONTAL, horizontallyCentered, 0}, {Gravity.CENTER_VERTICAL, 0, verticallyCentered}, {Gravity.RIGHT, end, 0}, {GravityCompat.END, end, 0}, {Gravity.BOTTOM, 0, bottom}, {Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM, horizontallyCentered, bottom}, {Gravity.RIGHT | Gravity.CENTER_VERTICAL, end, verticallyCentered}, }; for (final int[] testCase : testCases) { activityTestRule.runOnUiThread( new Runnable() { @Override public void run() { final CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) view.getLayoutParams(); lp.gravity = testCase[0]; view.setLayoutParams(lp); } }); getInstrumentation().waitForIdleSync(); activityTestRule.runOnUiThread( new Runnable() { @Override public void run() { assertThat("Gravity: " + testCase[0], view.getLeft(), is(testCase[1])); assertThat("Gravity: " + testCase[0], view.getTop(), is(testCase[2])); } }); } } @Test public void testInsetDependency() { final CoordinatorLayout col = activityTestRule.getActivity().mCoordinatorLayout; final CoordinatorLayout.LayoutParams lpInsetLeft = col.generateDefaultLayoutParams(); lpInsetLeft.insetEdge = Gravity.LEFT; final CoordinatorLayout.LayoutParams lpInsetRight = col.generateDefaultLayoutParams(); lpInsetRight.insetEdge = Gravity.RIGHT; final CoordinatorLayout.LayoutParams lpInsetTop = col.generateDefaultLayoutParams(); lpInsetTop.insetEdge = Gravity.TOP; final CoordinatorLayout.LayoutParams lpInsetBottom = col.generateDefaultLayoutParams(); lpInsetBottom.insetEdge = Gravity.BOTTOM; final CoordinatorLayout.LayoutParams lpDodgeLeft = col.generateDefaultLayoutParams(); lpDodgeLeft.dodgeInsetEdges = Gravity.LEFT; final CoordinatorLayout.LayoutParams lpDodgeLeftAndTop = col.generateDefaultLayoutParams(); lpDodgeLeftAndTop.dodgeInsetEdges = Gravity.LEFT | Gravity.TOP; final CoordinatorLayout.LayoutParams lpDodgeAll = col.generateDefaultLayoutParams(); lpDodgeAll.dodgeInsetEdges = Gravity.FILL; final View a = new View(col.getContext()); final View b = new View(col.getContext()); assertThat(dependsOn(lpDodgeLeft, lpInsetLeft, col, a, b), is(true)); assertThat(dependsOn(lpDodgeLeft, lpInsetRight, col, a, b), is(false)); assertThat(dependsOn(lpDodgeLeft, lpInsetTop, col, a, b), is(false)); assertThat(dependsOn(lpDodgeLeft, lpInsetBottom, col, a, b), is(false)); assertThat(dependsOn(lpDodgeLeftAndTop, lpInsetLeft, col, a, b), is(true)); assertThat(dependsOn(lpDodgeLeftAndTop, lpInsetRight, col, a, b), is(false)); assertThat(dependsOn(lpDodgeLeftAndTop, lpInsetTop, col, a, b), is(true)); assertThat(dependsOn(lpDodgeLeftAndTop, lpInsetBottom, col, a, b), is(false)); assertThat(dependsOn(lpDodgeAll, lpInsetLeft, col, a, b), is(true)); assertThat(dependsOn(lpDodgeAll, lpInsetRight, col, a, b), is(true)); assertThat(dependsOn(lpDodgeAll, lpInsetTop, col, a, b), is(true)); assertThat(dependsOn(lpDodgeAll, lpInsetBottom, col, a, b), is(true)); assertThat(dependsOn(lpInsetLeft, lpDodgeLeft, col, a, b), is(false)); } private static boolean dependsOn( CoordinatorLayout.LayoutParams lpChild, CoordinatorLayout.LayoutParams lpDependency, CoordinatorLayout col, View child, View dependency) { child.setLayoutParams(lpChild); dependency.setLayoutParams(lpDependency); return lpChild.dependsOn(col, child, dependency); } @Test public void testInsetEdge() throws Throwable { final CoordinatorLayout col = activityTestRule.getActivity().mCoordinatorLayout; final View insetView = new View(col.getContext()); final View dodgeInsetView = new View(col.getContext()); final AtomicInteger originalTop = new AtomicInteger(); activityTestRule.runOnUiThread( new Runnable() { @Override public void run() { CoordinatorLayout.LayoutParams lpInsetView = col.generateDefaultLayoutParams(); lpInsetView.width = CoordinatorLayout.LayoutParams.MATCH_PARENT; lpInsetView.height = 100; lpInsetView.gravity = Gravity.TOP | Gravity.LEFT; lpInsetView.insetEdge = Gravity.TOP; col.addView(insetView, lpInsetView); insetView.setBackgroundColor(0xFF0000FF); CoordinatorLayout.LayoutParams lpDodgeInsetView = col.generateDefaultLayoutParams(); lpDodgeInsetView.width = 100; lpDodgeInsetView.height = 100; lpDodgeInsetView.gravity = Gravity.TOP | Gravity.LEFT; lpDodgeInsetView.dodgeInsetEdges = Gravity.TOP; col.addView(dodgeInsetView, lpDodgeInsetView); dodgeInsetView.setBackgroundColor(0xFFFF0000); } }); getInstrumentation().waitForIdleSync(); activityTestRule.runOnUiThread( new Runnable() { @Override public void run() { List<View> dependencies = col.getDependencies(dodgeInsetView); assertThat(dependencies.size(), is(1)); assertThat(dependencies.get(0), is(insetView)); // Move the insetting view originalTop.set(dodgeInsetView.getTop()); assertThat(originalTop.get(), is(insetView.getBottom())); ViewCompat.offsetTopAndBottom(insetView, 123); } }); getInstrumentation().waitForIdleSync(); activityTestRule.runOnUiThread( new Runnable() { @Override public void run() { // Confirm that the dodging view was moved by the same size assertThat(dodgeInsetView.getTop() - originalTop.get(), is(123)); } }); } @Test public void testDependentViewChanged() throws Throwable { final CoordinatorLayout col = activityTestRule.getActivity().mCoordinatorLayout; // Add two views, A & B, where B depends on A final View viewA = new View(col.getContext()); final CoordinatorLayout.LayoutParams lpA = col.generateDefaultLayoutParams(); lpA.width = 100; lpA.height = 100; final View viewB = new View(col.getContext()); final CoordinatorLayout.LayoutParams lpB = col.generateDefaultLayoutParams(); lpB.width = 100; lpB.height = 100; final CoordinatorLayout.Behavior behavior = spy(new DependentBehavior(viewA)); lpB.setBehavior(behavior); activityTestRule.runOnUiThread( new Runnable() { @Override public void run() { col.addView(viewA, lpA); col.addView(viewB, lpB); } }); getInstrumentation().waitForIdleSync(); // Reset the Behavior since onDependentViewChanged may have already been called as part of // any layout/draw passes already reset(behavior); // Now offset view A activityTestRule.runOnUiThread( new Runnable() { @Override public void run() { ViewCompat.offsetLeftAndRight(viewA, 20); ViewCompat.offsetTopAndBottom(viewA, 20); } }); getInstrumentation().waitForIdleSync(); // And assert that view B's Behavior was called appropriately verify(behavior, times(1)).onDependentViewChanged(col, viewB, viewA); } @Test public void testDependentViewRemoved() throws Throwable { final CoordinatorLayout col = activityTestRule.getActivity().mCoordinatorLayout; // Add two views, A & B, where B depends on A final View viewA = new View(col.getContext()); final View viewB = new View(col.getContext()); final CoordinatorLayout.LayoutParams lpB = col.generateDefaultLayoutParams(); final CoordinatorLayout.Behavior behavior = spy(new DependentBehavior(viewA)); lpB.setBehavior(behavior); activityTestRule.runOnUiThread( new Runnable() { @Override public void run() { col.addView(viewA); col.addView(viewB, lpB); } }); getInstrumentation().waitForIdleSync(); // Now remove view A activityTestRule.runOnUiThread( new Runnable() { @Override public void run() { col.removeView(viewA); } }); // And assert that View B's Behavior was called appropriately verify(behavior, times(1)).onDependentViewRemoved(col, viewB, viewA); } @Test public void testGetDependenciesAfterDependentViewRemoved() throws Throwable { final CoordinatorLayout col = activityTestRule.getActivity().mCoordinatorLayout; // Add two views, A & B, where B depends on A final View viewA = new View(col.getContext()); final View viewB = new View(col.getContext()); final CoordinatorLayout.LayoutParams lpB = col.generateDefaultLayoutParams(); final CoordinatorLayout.Behavior behavior = new DependentBehavior(viewA) { @Override public void onDependentViewRemoved( CoordinatorLayout parent, View child, View dependency) { parent.getDependencies(child); } }; lpB.setBehavior(behavior); // Now add views activityTestRule.runOnUiThread( new Runnable() { @Override public void run() { col.addView(viewA); col.addView(viewB, lpB); } }); // Wait for a layout getInstrumentation().waitForIdleSync(); // Now remove view A, which will trigger onDependentViewRemoved() on view B's behavior activityTestRule.runOnUiThread( new Runnable() { @Override public void run() { col.removeView(viewA); } }); } @Test public void testDodgeInsetBeforeLayout() throws Throwable { final CoordinatorLayout col = activityTestRule.getActivity().mCoordinatorLayout; // Add a dummy view, which will be used to trigger a hierarchy change. final View dummy = new View(col.getContext()); activityTestRule.runOnUiThread( new Runnable() { @Override public void run() { col.addView(dummy); } }); // Wait for a layout. getInstrumentation().waitForIdleSync(); final View dodge = new View(col.getContext()); final CoordinatorLayout.LayoutParams lpDodge = col.generateDefaultLayoutParams(); lpDodge.dodgeInsetEdges = Gravity.BOTTOM; lpDodge.setBehavior( new Behavior() { @Override public boolean getInsetDodgeRect(CoordinatorLayout parent, View child, Rect rect) { // Any non-empty rect is fine here. rect.set(0, 0, 10, 10); return true; } }); activityTestRule.runOnUiThread( new Runnable() { @Override public void run() { col.addView(dodge, lpDodge); // Ensure the new view is in the list of children. int heightSpec = MeasureSpec.makeMeasureSpec(col.getHeight(), MeasureSpec.EXACTLY); int widthSpec = MeasureSpec.makeMeasureSpec(col.getWidth(), MeasureSpec.EXACTLY); col.measure(widthSpec, heightSpec); // Force a hierarchy change. col.removeView(dummy); } }); // Wait for a layout. getInstrumentation().waitForIdleSync(); } @Test public void testGoneViewsNotMeasuredLaidOut() throws Throwable { final CoordinatorLayoutActivity activity = activityTestRule.getActivity(); final CoordinatorLayout col = activity.mCoordinatorLayout; // Now create a GONE view and add it to the CoordinatorLayout final View imageView = new View(activity); imageView.setVisibility(View.GONE); activityTestRule.runOnUiThread( new Runnable() { @Override public void run() { col.addView(imageView, 200, 200); } }); // Wait for a layout and measure pass getInstrumentation().waitForIdleSync(); // And assert that it has not been laid out assertFalse(imageView.getMeasuredWidth() > 0); assertFalse(imageView.getMeasuredHeight() > 0); assertFalse(ViewCompat.isLaidOut(imageView)); // Now set the view to INVISIBLE activityTestRule.runOnUiThread( new Runnable() { @Override public void run() { imageView.setVisibility(View.INVISIBLE); } }); // Wait for a layout and measure pass getInstrumentation().waitForIdleSync(); // And assert that it has been laid out assertTrue(imageView.getMeasuredWidth() > 0); assertTrue(imageView.getMeasuredHeight() > 0); assertTrue(ViewCompat.isLaidOut(imageView)); } @Test public void testNestedScrollingDispatchesToBehavior() throws Throwable { final CoordinatorLayoutActivity activity = activityTestRule.getActivity(); final CoordinatorLayout col = activity.mCoordinatorLayout; // Now create a view and add it to the CoordinatorLayout with the spy behavior, // along with a NestedScrollView final ImageView imageView = new ImageView(activity); final CoordinatorLayout.Behavior behavior = spy(new NestedScrollingBehavior()); activityTestRule.runOnUiThread( new Runnable() { @Override public void run() { LayoutInflater.from(activity).inflate(R.layout.include_nestedscrollview, col, true); CoordinatorLayout.LayoutParams clp = new CoordinatorLayout.LayoutParams(200, 200); clp.setBehavior(behavior); col.addView(imageView, clp); } }); // Now vertically swipe up on the NSV, causing nested scrolling to occur onView(withId(R.id.nested_scrollview)).perform(swipeUp()); // Verify that the Behavior's onStartNestedScroll was called once verify(behavior, times(1)) .onStartNestedScroll( eq(col), // parent eq(imageView), // child any(View.class), // target any(View.class), // direct child target any(int.class)); // axes // Verify that the Behavior's onNestedScrollAccepted was called once verify(behavior, times(1)) .onNestedScrollAccepted( eq(col), // parent eq(imageView), // child any(View.class), // target any(View.class), // direct child target any(int.class)); // axes // Verify that the Behavior's onNestedPreScroll was called at least once verify(behavior, atLeastOnce()) .onNestedPreScroll( eq(col), // parent eq(imageView), // child any(View.class), // target any(int.class), // dx any(int.class), // dy any(int[].class)); // consumed // Verify that the Behavior's onNestedScroll was called at least once verify(behavior, atLeastOnce()) .onNestedScroll( eq(col), // parent eq(imageView), // child any(View.class), // target any(int.class), // dx consumed any(int.class), // dy consumed any(int.class), // dx unconsumed any(int.class)); // dy unconsumed // Verify that the Behavior's onStopNestedScroll was called once verify(behavior, times(1)) .onStopNestedScroll( eq(col), // parent eq(imageView), // child any(View.class)); // target } @Test public void testNestedScrollingDispatchingToBehaviorWithGoneView() throws Throwable { final CoordinatorLayoutActivity activity = activityTestRule.getActivity(); final CoordinatorLayout col = activity.mCoordinatorLayout; // Now create a GONE view and add it to the CoordinatorLayout with the spy behavior, // along with a NestedScrollView final ImageView imageView = new ImageView(activity); imageView.setVisibility(View.GONE); final CoordinatorLayout.Behavior behavior = spy(new NestedScrollingBehavior()); activityTestRule.runOnUiThread( new Runnable() { @Override public void run() { LayoutInflater.from(activity).inflate(R.layout.include_nestedscrollview, col, true); CoordinatorLayout.LayoutParams clp = new CoordinatorLayout.LayoutParams(200, 200); clp.setBehavior(behavior); col.addView(imageView, clp); } }); // Now vertically swipe up on the NSV, causing nested scrolling to occur onView(withId(R.id.nested_scrollview)).perform(swipeUp()); // Verify that the Behavior's onStartNestedScroll was not called verify(behavior, never()) .onStartNestedScroll( eq(col), // parent eq(imageView), // child any(View.class), // target any(View.class), // direct child target any(int.class)); // axes // Verify that the Behavior's onNestedScrollAccepted was not called verify(behavior, never()) .onNestedScrollAccepted( eq(col), // parent eq(imageView), // child any(View.class), // target any(View.class), // direct child target any(int.class)); // axes // Verify that the Behavior's onNestedPreScroll was not called verify(behavior, never()) .onNestedPreScroll( eq(col), // parent eq(imageView), // child any(View.class), // target any(int.class), // dx any(int.class), // dy any(int[].class)); // consumed // Verify that the Behavior's onNestedScroll was not called verify(behavior, never()) .onNestedScroll( eq(col), // parent eq(imageView), // child any(View.class), // target any(int.class), // dx consumed any(int.class), // dy consumed any(int.class), // dx unconsumed any(int.class)); // dy unconsumed // Verify that the Behavior's onStopNestedScroll was not called verify(behavior, never()) .onStopNestedScroll( eq(col), // parent eq(imageView), // child any(View.class)); // target } @Test public void testNestedScrollingTriggeringDependentViewChanged() throws Throwable { final CoordinatorLayoutActivity activity = activityTestRule.getActivity(); final CoordinatorLayout col = activity.mCoordinatorLayout; // First a NestedScrollView to trigger nested scrolling final View scrollView = LayoutInflater.from(activity).inflate(R.layout.include_nestedscrollview, col, false); // Now create a View and Behavior which depend on the scrollview final ImageView dependentView = new ImageView(activity); final CoordinatorLayout.Behavior dependentBehavior = spy(new DependentBehavior(scrollView)); // Finally a view which accepts nested scrolling in the CoordinatorLayout final ImageView nestedScrollAwareView = new ImageView(activity); activityTestRule.runOnUiThread( new Runnable() { @Override public void run() { // First add the ScrollView col.addView(scrollView); // Now add the view which depends on the scrollview CoordinatorLayout.LayoutParams clp = new CoordinatorLayout.LayoutParams(200, 200); clp.setBehavior(dependentBehavior); col.addView(dependentView, clp); // Now add the nested scrolling aware view clp = new CoordinatorLayout.LayoutParams(200, 200); clp.setBehavior(new NestedScrollingBehavior()); col.addView(nestedScrollAwareView, clp); } }); // Wait for any layouts, and reset the Behavior so that the call counts are 0 getInstrumentation().waitForIdleSync(); reset(dependentBehavior); // Now vertically swipe up on the NSV, causing nested scrolling to occur onView(withId(R.id.nested_scrollview)).perform(swipeUp()); // Verify that the Behavior's onDependentViewChanged is not called due to the // nested scroll verify(dependentBehavior, never()) .onDependentViewChanged( eq(col), // parent eq(dependentView), // child eq(scrollView)); // axes } @Test public void testDodgeInsetViewWithEmptyBounds() throws Throwable { final CoordinatorLayout col = activityTestRule.getActivity().mCoordinatorLayout; // Add a view with zero height/width which is set to dodge its bounds final View view = new View(col.getContext()); final Behavior spyBehavior = spy(new DodgeBoundsBehavior()); activityTestRule.runOnUiThread( new Runnable() { @Override public void run() { final CoordinatorLayout.LayoutParams lp = col.generateDefaultLayoutParams(); lp.dodgeInsetEdges = Gravity.BOTTOM; lp.gravity = Gravity.BOTTOM; lp.height = 0; lp.width = 0; lp.setBehavior(spyBehavior); col.addView(view, lp); } }); // Wait for a layout getInstrumentation().waitForIdleSync(); // Now add an non-empty bounds inset view to the bottom of the CoordinatorLayout activityTestRule.runOnUiThread( new Runnable() { @Override public void run() { final View dodge = new View(col.getContext()); final CoordinatorLayout.LayoutParams lp = col.generateDefaultLayoutParams(); lp.insetEdge = Gravity.BOTTOM; lp.gravity = Gravity.BOTTOM; lp.height = 60; lp.width = CoordinatorLayout.LayoutParams.MATCH_PARENT; col.addView(dodge, lp); } }); // Verify that the Behavior of the view with empty bounds does not have its // getInsetDodgeRect() called verify(spyBehavior, never()).getInsetDodgeRect(same(col), same(view), any(Rect.class)); } public static class NestedScrollingBehavior extends CoordinatorLayout.Behavior<ImageView> { @Override public boolean onStartNestedScroll( CoordinatorLayout coordinatorLayout, ImageView child, View directTargetChild, View target, int nestedScrollAxes) { // Return true so that we always accept nested scroll events return true; } } public static class DodgeBoundsBehavior extends Behavior<View> { @Override public boolean getInsetDodgeRect(CoordinatorLayout parent, View child, Rect rect) { rect.set(child.getLeft(), child.getTop(), child.getRight(), child.getBottom()); return true; } } }