1/* 2 * Copyright 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package androidx.core.widget; 18 19import static org.hamcrest.CoreMatchers.is; 20import static org.hamcrest.MatcherAssert.assertThat; 21 22import android.graphics.Rect; 23import android.graphics.drawable.GradientDrawable; 24import android.os.Parcelable; 25import android.support.test.filters.SmallTest; 26import android.support.test.runner.AndroidJUnit4; 27import android.support.v4.BaseInstrumentationTestCase; 28import android.view.KeyEvent; 29import android.view.View; 30import android.view.ViewGroup; 31 32import androidx.core.test.R; 33import androidx.core.view.NestedScrollingParent2; 34 35import org.junit.Test; 36import org.junit.runner.RunWith; 37 38import java.util.concurrent.CountDownLatch; 39import java.util.concurrent.TimeUnit; 40 41/** 42 * So far these tests only cover {@code NestedScrollView}'s implementation of 43 * {@link NestedScrollingParent2} and the backwards compatibility of {@code NestedScrollView}'s 44 * implementation of {@link androidx.core.view.NestedScrollingParent} for the methods that 45 * {@link NestedScrollingParent2} overloads. 46 */ 47 48@RunWith(AndroidJUnit4.class) 49@SmallTest 50public class NestedScrollViewTest extends 51 BaseInstrumentationTestCase<TestContentViewActivity> { 52 53 private NestedScrollView mNestedScrollView; 54 private View mChild; 55 56 public NestedScrollViewTest() { 57 super(TestContentViewActivity.class); 58 } 59 60 @Test 61 public void getBottomFadingEdgeStrength_childBottomIsBelowParentWithoutMargins_isCorrect() { 62 setup(200); 63 mNestedScrollView.setVerticalFadingEdgeEnabled(true); 64 measureAndLayout(100); 65 66 float expected = mNestedScrollView.getBottomFadingEdgeStrength(); 67 68 assertThat(expected, is(1.0f)); 69 } 70 71 @Test 72 public void getBottomFadingEdgeStrength_childBottomIsBelowParentDuetoMargins_isCorrect() { 73 setup(100); 74 mNestedScrollView.setVerticalFadingEdgeEnabled(true); 75 setChildMargins(100, 0); 76 measureAndLayout(100); 77 78 float expected = mNestedScrollView.getBottomFadingEdgeStrength(); 79 80 assertThat(expected, is(1.0f)); 81 } 82 83 @Test 84 public void getBottomFadingEdgeStrength_childIsAboveButMarginIsBelowParent_isCorrect() { 85 setup(100); 86 mNestedScrollView.setVerticalFadingEdgeEnabled(true); 87 setChildMargins(0, 100); 88 measureAndLayout(100); 89 90 float expected = mNestedScrollView.getBottomFadingEdgeStrength(); 91 92 assertThat(expected, is(1.0f)); 93 } 94 95 @Test 96 public void getBottomFadingEdgeStrength_childBottomIsAboveParentAndNoMargin_isZero() { 97 setup(100); 98 mNestedScrollView.setVerticalFadingEdgeEnabled(true); 99 measureAndLayout(100); 100 101 float expected = mNestedScrollView.getBottomFadingEdgeStrength(); 102 103 assertThat(expected, is(0f)); 104 } 105 106 @Test 107 public void onMeasure_fillViewPortEnabledChildSmallButWithMarginBig_childMeasuredCorrectly() { 108 setup(50); 109 setChildMargins(25, 25); 110 mNestedScrollView.setFillViewport(true); 111 112 measure(100); 113 114 assertThat(mChild.getMeasuredHeight(), is(50)); 115 } 116 117 @Test 118 public void onMeasure_fillViewPortEnabledChildSmallWithMargins_childMeasuredCorrectly() { 119 setup(50); 120 setChildMargins(20, 20); 121 mNestedScrollView.setFillViewport(true); 122 123 measure(100); 124 125 assertThat(mChild.getMeasuredHeight(), is(60)); 126 } 127 128 @Test 129 public void onMeasure_fillViewPortEnabledChildSmallNoMargins_childMeasuredCorrectly() { 130 setup(50); 131 setChildMargins(0, 0); 132 mNestedScrollView.setFillViewport(true); 133 134 measure(100); 135 136 assertThat(mChild.getMeasuredHeight(), is(100)); 137 } 138 139 @Test 140 public void executeKeyEvent_spaceBarCanScrollDueToMargins_scrolls() { 141 setup(75); 142 setChildMargins(0, 50); 143 mNestedScrollView.setSmoothScrollingEnabled(false); 144 measureAndLayout(100); 145 KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SPACE); 146 147 mNestedScrollView.executeKeyEvent(keyEvent); 148 149 assertThat(mNestedScrollView.getScrollY(), is(25)); 150 } 151 152 @Test 153 public void pageScroll_takesAccountOfMargin() { 154 setup(75); 155 setChildMargins(20, 30); 156 mNestedScrollView.setSmoothScrollingEnabled(false); 157 measureAndLayout(100); 158 159 mNestedScrollView.pageScroll(View.FOCUS_DOWN); 160 161 assertThat(mNestedScrollView.getScrollY(), is(25)); 162 } 163 164 @Test 165 public void getScrollRange_takesAccountOfMargin() { 166 setup(100); 167 setChildMargins(20, 30); 168 measureAndLayout(100); 169 170 int expected = mNestedScrollView.getScrollRange(); 171 172 assertThat(expected, is(50)); 173 } 174 175 @Test 176 public void fullScroll_scrollsToEndOfMargin() { 177 setup(300); 178 setChildMargins(20, 30); 179 mNestedScrollView.setSmoothScrollingEnabled(false); 180 measureAndLayout(100); 181 182 mNestedScrollView.fullScroll(View.FOCUS_DOWN); 183 184 assertThat(mNestedScrollView.getScrollY(), is(250)); 185 } 186 187 @Test 188 public void arrowScroll_canScrollHalfDownDueToSizeAndMargin_scrollsHalfDown() { 189 setup(130); 190 setChildMargins(10, 20); 191 mNestedScrollView.setSmoothScrollingEnabled(false); 192 measureAndLayout(100); 193 194 mNestedScrollView.arrowScroll(View.FOCUS_DOWN); 195 196 assertThat(mNestedScrollView.getScrollY(), is(50)); 197 } 198 199 @Test 200 public void arrowScroll_canScrollQuarterDownDueToSizeAndMargin_scrollsQuarterDown() { 201 setup(75); 202 setChildMargins(25, 25); 203 mNestedScrollView.setSmoothScrollingEnabled(false); 204 measureAndLayout(100); 205 206 mNestedScrollView.arrowScroll(View.FOCUS_DOWN); 207 208 assertThat(mNestedScrollView.getScrollY(), is(25)); 209 } 210 211 @Test 212 public void arrowScroll_canOnlyScrollQuarterUp_scrollsQuarterUp() { 213 setup(75); 214 setChildMargins(25, 25); 215 mNestedScrollView.setSmoothScrollingEnabled(false); 216 measureAndLayout(100); 217 218 mNestedScrollView.scrollTo(0, 25); 219 mNestedScrollView.arrowScroll(View.FOCUS_UP); 220 221 assertThat(mNestedScrollView.getScrollY(), is(0)); 222 } 223 224 @Test 225 public void arrowScroll_canScroll_returnsTrue() { 226 setup(75); 227 setChildMargins(20, 30); 228 mNestedScrollView.setSmoothScrollingEnabled(false); 229 measureAndLayout(100); 230 231 boolean actualResult = mNestedScrollView.arrowScroll(View.FOCUS_DOWN); 232 233 assertThat(actualResult, is(true)); 234 } 235 236 @Test 237 public void arrowScroll_cantScroll_returnsFalse() { 238 setup(50); 239 setChildMargins(25, 25); 240 mNestedScrollView.setSmoothScrollingEnabled(false); 241 measureAndLayout(100); 242 243 boolean actualResult = mNestedScrollView.arrowScroll(View.FOCUS_DOWN); 244 245 assertThat(actualResult, is(false)); 246 } 247 248 //SmoothScrollBy 249 250 @Test 251 public void smoothScrollBy_scrollsEntireDistanceIncludingMargins() throws Throwable { 252 setup(200); 253 setChildMargins(20, 30); 254 attachToActivity(100); 255 256 final CountDownLatch countDownLatch = new CountDownLatch(1); 257 final int expectedTarget = 150; 258 final int scrollDistance = 150; 259 mActivityTestRule.runOnUiThread(new Runnable() { 260 @Override 261 public void run() { 262 mNestedScrollView.setOnScrollChangeListener( 263 new NestedScrollView.OnScrollChangeListener() { 264 265 @Override 266 public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, 267 int oldScrollX, int oldScrollY) { 268 if (scrollY == expectedTarget) { 269 countDownLatch.countDown(); 270 } 271 } 272 }); 273 mNestedScrollView.smoothScrollBy(0, scrollDistance); 274 } 275 }); 276 assertThat(countDownLatch.await(2000, TimeUnit.SECONDS), is(true)); 277 278 assertThat(mNestedScrollView.getScrollY(), is(expectedTarget)); 279 } 280 281 @Test 282 public void computeVerticalScrollRange_takesAccountOfMargin() { 283 setup(200); 284 setChildMargins(20, 30); 285 measureAndLayout(100); 286 287 int actual = mNestedScrollView.computeVerticalScrollRange(); 288 289 assertThat(actual, is(250)); 290 } 291 292 @Test 293 public void computeScrollDeltaToGetChildRectOnScreen_marginRespectedToMakeRoomForFadingEdge() { 294 setup(200); 295 setChildMargins(0, 1); 296 mNestedScrollView.setVerticalFadingEdgeEnabled(true); 297 mNestedScrollView.setFadingEdgeLength(25); 298 measureAndLayout(100); 299 Rect rect = new Rect(0, 175, 100, 200); 300 301 int actual = mNestedScrollView.computeScrollDeltaToGetChildRectOnScreen(rect); 302 303 assertThat(actual, is(101)); 304 } 305 306 @Test 307 public void computeScrollDeltaToGetChildRectOnScreen_fadingEdgeNoMargin_clampsToEnd() { 308 setup(200); 309 setChildMargins(0, 0); 310 mNestedScrollView.setVerticalFadingEdgeEnabled(true); 311 mNestedScrollView.setFadingEdgeLength(25); 312 measureAndLayout(100); 313 Rect rect = new Rect(0, 175, 100, 200); 314 315 int actual = mNestedScrollView.computeScrollDeltaToGetChildRectOnScreen(rect); 316 317 assertThat(actual, is(100)); 318 } 319 320 @Test 321 public void onLayout_canScrollDistanceFromSavedInstanceStateDueToMargins_scrollsDistance() { 322 323 // Arrange. 324 325 setup(200); 326 setChildMargins(0, 0); 327 measureAndLayout(100); 328 mNestedScrollView.scrollTo(0, 100); 329 Parcelable savedState = mNestedScrollView.onSaveInstanceState(); 330 331 setup(100); 332 setChildMargins(25, 75); 333 mNestedScrollView.onRestoreInstanceState(savedState); 334 335 // Act. 336 337 measureAndLayout(100); 338 339 // Assert 340 341 assertThat(mNestedScrollView.getScrollY(), is(100)); 342 } 343 344 @Test 345 public void scrollTo_childHasMargins_scrollsToEndOfMargins() { 346 setup(100); 347 setChildMargins(25, 75); 348 mNestedScrollView.setSmoothScrollingEnabled(false); 349 measureAndLayout(100); 350 351 mNestedScrollView.scrollTo(0, 100); 352 353 assertThat(mNestedScrollView.getScrollY(), is(100)); 354 } 355 356 private void setup(int childHeight) { 357 mChild = new View(mActivityTestRule.getActivity()); 358 mChild.setMinimumWidth(100); 359 mChild.setMinimumHeight(childHeight); 360 mChild.setBackgroundDrawable( 361 new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, 362 new int[]{0xFFFF0000, 0xFF00FF00})); 363 364 mNestedScrollView = new NestedScrollView(mActivityTestRule.getActivity()); 365 mNestedScrollView.setBackgroundColor(0xFF0000FF); 366 mNestedScrollView.addView(mChild); 367 } 368 369 private void setChildMargins(int top, int bottom) { 370 NestedScrollView.LayoutParams childLayoutParams = 371 new NestedScrollView.LayoutParams(100, 100); 372 childLayoutParams.topMargin = top; 373 childLayoutParams.bottomMargin = bottom; 374 mChild.setLayoutParams(childLayoutParams); 375 } 376 377 private void attachToActivity(int nestedScrollViewHeight) throws Throwable { 378 mNestedScrollView.setLayoutParams(new ViewGroup.LayoutParams(100, nestedScrollViewHeight)); 379 380 final TestContentView testContentView = 381 mActivityTestRule.getActivity().findViewById(R.id.testContentView); 382 testContentView.expectLayouts(1); 383 mActivityTestRule.runOnUiThread(new Runnable() { 384 @Override 385 public void run() { 386 testContentView.addView(mNestedScrollView); 387 } 388 }); 389 testContentView.awaitLayouts(2); 390 } 391 392 private void measure(int height) { 393 int measureSpecWidth = 394 View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY); 395 int measureSpecHeight = 396 View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY); 397 mNestedScrollView.measure(measureSpecWidth, measureSpecHeight); 398 } 399 400 private void measureAndLayout(int height) { 401 measure(height); 402 mNestedScrollView.layout(0, 0, 100, height); 403 } 404} 405