1 /* 2 * Copyright (C) 2008 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 17 package android.widget.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertSame; 22 import static org.junit.Assert.assertTrue; 23 import static org.junit.Assert.fail; 24 import static org.mockito.Matchers.eq; 25 import static org.mockito.Mockito.spy; 26 import static org.mockito.Mockito.verify; 27 28 import android.app.Activity; 29 import android.app.Instrumentation; 30 import android.content.Context; 31 import android.graphics.Rect; 32 import android.support.test.InstrumentationRegistry; 33 import android.support.test.annotation.UiThreadTest; 34 import android.support.test.filters.MediumTest; 35 import android.support.test.rule.ActivityTestRule; 36 import android.support.test.runner.AndroidJUnit4; 37 import android.util.AttributeSet; 38 import android.util.Xml; 39 import android.view.View; 40 import android.view.View.MeasureSpec; 41 import android.view.ViewGroup; 42 import android.widget.FrameLayout; 43 import android.widget.ScrollView; 44 import android.widget.TextView; 45 import android.widget.cts.util.TestUtils; 46 47 import com.android.compatibility.common.util.PollingCheck; 48 49 import org.junit.Before; 50 import org.junit.Rule; 51 import org.junit.Test; 52 import org.junit.runner.RunWith; 53 import org.xmlpull.v1.XmlPullParser; 54 55 /** 56 * Test {@link ScrollView}. 57 */ 58 @MediumTest 59 @RunWith(AndroidJUnit4.class) 60 public class ScrollViewTest { 61 // view dpi constants. Must match those defined in scroll_view layout 62 private static final int ITEM_WIDTH_DPI = 250; 63 private static final int ITEM_HEIGHT_DPI = 100; 64 private static final int ITEM_COUNT = 15; 65 private static final int PAGE_WIDTH_DPI = 100; 66 private static final int PAGE_HEIGHT_DPI = 100; 67 private static final int TOLERANCE = 2; 68 69 private int mItemWidth; 70 private int mItemHeight; 71 private int mPageWidth; 72 private int mPageHeight; 73 private int mScrollBottom; 74 private int mScrollRight; 75 76 private Instrumentation mInstrumentation; 77 private Activity mActivity; 78 private ScrollView mScrollViewRegular; 79 private ScrollView mScrollViewCustom; 80 private MyScrollView mScrollViewCustomEmpty; 81 82 @Rule 83 public ActivityTestRule<ScrollViewCtsActivity> mActivityRule = 84 new ActivityTestRule<>(ScrollViewCtsActivity.class); 85 86 @Before 87 public void setup() { 88 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 89 mActivity = mActivityRule.getActivity(); 90 mScrollViewRegular = (ScrollView) mActivity.findViewById(R.id.scroll_view_regular); 91 mScrollViewCustom = (ScrollView) mActivity.findViewById(R.id.scroll_view_custom); 92 mScrollViewCustomEmpty = (MyScrollView) mActivity.findViewById( 93 R.id.scroll_view_custom_empty); 94 95 // calculate pixel positions from dpi constants. 96 mItemWidth = TestUtils.dpToPx(mActivity, ITEM_WIDTH_DPI); 97 mItemHeight = TestUtils.dpToPx(mActivity, ITEM_HEIGHT_DPI); 98 mPageWidth = TestUtils.dpToPx(mActivity, PAGE_WIDTH_DPI); 99 mPageHeight = TestUtils.dpToPx(mActivity, PAGE_HEIGHT_DPI); 100 101 mScrollBottom = mItemHeight * ITEM_COUNT - mPageHeight; 102 mScrollRight = mItemWidth - mPageWidth; 103 } 104 105 @Test 106 public void testConstructor() { 107 XmlPullParser parser = mActivity.getResources().getLayout(R.layout.scrollview_layout); 108 AttributeSet attrs = Xml.asAttributeSet(parser); 109 new ScrollView(mActivity); 110 111 new ScrollView(mActivity, attrs); 112 113 new ScrollView(mActivity, attrs, 0); 114 } 115 116 @UiThreadTest 117 @Test 118 public void testGetMaxScrollAmount() { 119 // the value is half of total layout height 120 mScrollViewRegular.layout(0, 0, 100, 200); 121 assertEquals((200 - 0) / 2, mScrollViewRegular.getMaxScrollAmount()); 122 123 mScrollViewRegular.layout(0, 0, 150, 100); 124 assertEquals((100 - 0) / 2, mScrollViewRegular.getMaxScrollAmount()); 125 } 126 127 @UiThreadTest 128 @Test 129 public void testAddView() { 130 TextView child0 = new TextView(mActivity); 131 mScrollViewRegular.addView(child0); 132 assertSame(child0, mScrollViewRegular.getChildAt(0)); 133 134 assertEquals(1, mScrollViewRegular.getChildCount()); 135 TextView child1 = new TextView(mActivity); 136 try { 137 mScrollViewRegular.addView(child1); 138 fail("ScrollView can host only one direct child"); 139 } catch (IllegalStateException e) { 140 // expected 141 } 142 assertEquals(1, mScrollViewRegular.getChildCount()); 143 } 144 145 @UiThreadTest 146 @Test 147 public void testAddViewWithIndex() { 148 TextView child0 = new TextView(mActivity); 149 mScrollViewRegular.addView(child0, 0); 150 assertSame(child0, mScrollViewRegular.getChildAt(0)); 151 152 assertEquals(1, mScrollViewRegular.getChildCount()); 153 TextView child1 = new TextView(mActivity); 154 try { 155 mScrollViewRegular.addView(child1, 1); 156 fail("ScrollView can host only one direct child"); 157 } catch (IllegalStateException e) { 158 // expected 159 } 160 assertEquals(1, mScrollViewRegular.getChildCount()); 161 162 mScrollViewRegular.removeAllViews(); 163 mScrollViewRegular = new ScrollView(mActivity); 164 mScrollViewRegular.addView(child0, -1); 165 assertSame(child0, mScrollViewRegular.getChildAt(0)); 166 167 assertEquals(1, mScrollViewRegular.getChildCount()); 168 child1 = new TextView(mActivity); 169 try { 170 mScrollViewRegular.addView(child1, -1); 171 fail("ScrollView can host only one direct child"); 172 } catch (IllegalStateException e) { 173 // expected 174 } 175 assertEquals(1, mScrollViewRegular.getChildCount()); 176 177 mScrollViewRegular.removeAllViews(); 178 mScrollViewRegular = new ScrollView(mActivity); 179 try { 180 mScrollViewRegular.addView(child0, 1); 181 fail("ScrollView can host only one direct child"); 182 } catch (IndexOutOfBoundsException e) { 183 // expected 184 } 185 } 186 187 @UiThreadTest 188 @Test 189 public void testAddViewWithLayoutParams() { 190 TextView child0 = new TextView(mActivity); 191 mScrollViewRegular.addView(child0, new ViewGroup.LayoutParams(200, 100)); 192 assertSame(child0, mScrollViewRegular.getChildAt(0)); 193 assertEquals(200, child0.getLayoutParams().width); 194 assertEquals(100, child0.getLayoutParams().height); 195 196 assertEquals(1, mScrollViewRegular.getChildCount()); 197 TextView child1 = new TextView(mActivity); 198 try { 199 mScrollViewRegular.addView(child1, new ViewGroup.LayoutParams(200, 100)); 200 fail("ScrollView can host only one direct child"); 201 } catch (IllegalStateException e) { 202 // expected 203 } 204 assertEquals(1, mScrollViewRegular.getChildCount()); 205 206 mScrollViewRegular.removeAllViews(); 207 mScrollViewRegular = new ScrollView(mActivity); 208 child0 = new TextView(mActivity); 209 try { 210 mScrollViewRegular.addView(child0, null); 211 fail("The LayoutParams should not be null!"); 212 } catch (NullPointerException e) { 213 // expected 214 } 215 } 216 217 @UiThreadTest 218 @Test 219 public void testAddViewWithIndexAndLayoutParams() { 220 TextView child0 = new TextView(mActivity); 221 mScrollViewRegular.addView(child0, 0, new ViewGroup.LayoutParams(200, 100)); 222 assertSame(child0, mScrollViewRegular.getChildAt(0)); 223 assertEquals(200, child0.getLayoutParams().width); 224 assertEquals(100, child0.getLayoutParams().height); 225 226 assertEquals(1, mScrollViewRegular.getChildCount()); 227 TextView child1 = new TextView(mActivity); 228 try { 229 mScrollViewRegular.addView(child1, 0, new ViewGroup.LayoutParams(200, 100)); 230 fail("ScrollView can host only one direct child"); 231 } catch (IllegalStateException e) { 232 // expected 233 } 234 assertEquals(1, mScrollViewRegular.getChildCount()); 235 236 mScrollViewRegular.removeAllViews(); 237 child0 = new TextView(mActivity); 238 try { 239 mScrollViewRegular.addView(child0, null); 240 fail("The LayoutParams should not be null!"); 241 } catch (NullPointerException e) { 242 // expected 243 } 244 245 mScrollViewRegular.removeAllViews(); 246 mScrollViewRegular.addView(child0, -1, new ViewGroup.LayoutParams(300, 150)); 247 assertSame(child0, mScrollViewRegular.getChildAt(0)); 248 assertEquals(300, child0.getLayoutParams().width); 249 assertEquals(150, child0.getLayoutParams().height); 250 251 assertEquals(1, mScrollViewRegular.getChildCount()); 252 child1 = new TextView(mActivity); 253 try { 254 mScrollViewRegular.addView(child1, -1, new ViewGroup.LayoutParams(200, 100)); 255 fail("ScrollView can host only one direct child"); 256 } catch (IllegalStateException e) { 257 // expected 258 } 259 assertEquals(1, mScrollViewRegular.getChildCount()); 260 261 mScrollViewRegular.removeAllViews(); 262 try { 263 mScrollViewRegular.addView(child0, 1, new ViewGroup.LayoutParams(200, 100)); 264 fail("ScrollView can host only one direct child"); 265 } catch (IndexOutOfBoundsException e) { 266 // expected 267 } 268 } 269 270 @UiThreadTest 271 @Test 272 public void testAccessFillViewport() { 273 assertFalse(mScrollViewRegular.isFillViewport()); 274 mScrollViewRegular.layout(0, 0, 100, 100); 275 assertFalse(mScrollViewRegular.isLayoutRequested()); 276 277 mScrollViewRegular.setFillViewport(false); 278 assertFalse(mScrollViewRegular.isFillViewport()); 279 assertFalse(mScrollViewRegular.isLayoutRequested()); 280 281 mScrollViewRegular.setFillViewport(true); 282 assertTrue(mScrollViewRegular.isFillViewport()); 283 assertTrue(mScrollViewRegular.isLayoutRequested()); 284 285 mScrollViewRegular.layout(0, 0, 100, 100); 286 assertFalse(mScrollViewRegular.isLayoutRequested()); 287 288 mScrollViewRegular.setFillViewport(false); 289 assertFalse(mScrollViewRegular.isFillViewport()); 290 assertTrue(mScrollViewRegular.isLayoutRequested()); 291 } 292 293 @Test 294 public void testAccessSmoothScrollingEnabled() throws Throwable { 295 assertTrue(mScrollViewCustom.isSmoothScrollingEnabled()); 296 297 // scroll immediately 298 mScrollViewCustom.setSmoothScrollingEnabled(false); 299 assertFalse(mScrollViewCustom.isSmoothScrollingEnabled()); 300 301 mActivityRule.runOnUiThread(() -> mScrollViewCustom.fullScroll(View.FOCUS_DOWN)); 302 303 assertEquals(mScrollBottom, mScrollViewCustom.getScrollY(), TOLERANCE); 304 305 mActivityRule.runOnUiThread(() -> mScrollViewCustom.fullScroll(View.FOCUS_UP)); 306 assertEquals(0, mScrollViewCustom.getScrollY()); 307 308 // smooth scroll 309 mScrollViewCustom.setSmoothScrollingEnabled(true); 310 assertTrue(mScrollViewCustom.isSmoothScrollingEnabled()); 311 312 mActivityRule.runOnUiThread(() -> mScrollViewCustom.fullScroll(View.FOCUS_DOWN)); 313 pollingCheckSmoothScrolling(0, 0, 0, mScrollBottom); 314 assertEquals(mScrollBottom, mScrollViewCustom.getScrollY(), TOLERANCE); 315 316 mActivityRule.runOnUiThread(() -> mScrollViewCustom.fullScroll(View.FOCUS_UP)); 317 pollingCheckSmoothScrolling(0, 0, mScrollBottom, 0); 318 assertEquals(0, mScrollViewCustom.getScrollY()); 319 } 320 321 @UiThreadTest 322 @Test 323 public void testMeasureChild() { 324 MyView child = new MyView(mActivity); 325 child.setBackgroundDrawable(null); 326 child.setPadding(0, 0, 0, 0); 327 child.setMinimumHeight(30); 328 child.setLayoutParams(new ViewGroup.LayoutParams(100, 100)); 329 child.measure(MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY), 330 MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY)); 331 332 assertEquals(100, child.getMeasuredHeight()); 333 assertEquals(100, child.getMeasuredWidth()); 334 335 mScrollViewCustomEmpty.measureChild(child, 336 MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY), 337 MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY)); 338 339 assertEquals(30, child.getMeasuredHeight()); 340 assertEquals(100, child.getMeasuredWidth()); 341 } 342 343 @UiThreadTest 344 @Test 345 public void testMeasureChildWithMargins() { 346 MyView child = new MyView(mActivity); 347 child.setBackgroundDrawable(null); 348 child.setPadding(0, 0, 0, 0); 349 child.setMinimumHeight(30); 350 child.setLayoutParams(new ViewGroup.MarginLayoutParams(100, 100)); 351 child.measure(MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY), 352 MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY)); 353 354 assertEquals(100, child.getMeasuredHeight()); 355 assertEquals(100, child.getMeasuredWidth()); 356 357 mScrollViewCustomEmpty.measureChildWithMargins(child, 358 MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY), 5, 359 MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY), 5); 360 361 assertEquals(30, child.getMeasuredHeight()); 362 assertEquals(100, child.getMeasuredWidth()); 363 } 364 365 @UiThreadTest 366 @Test 367 public void testMeasureSpecs() { 368 MyView child = spy(new MyView(mActivity)); 369 mScrollViewCustomEmpty.addView(child); 370 371 mScrollViewCustomEmpty.measureChild(child, 372 MeasureSpec.makeMeasureSpec(150, MeasureSpec.EXACTLY), 373 MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY)); 374 verify(child).onMeasure( 375 eq(MeasureSpec.makeMeasureSpec(150, MeasureSpec.EXACTLY)), 376 eq(MeasureSpec.makeMeasureSpec(100, MeasureSpec.UNSPECIFIED))); 377 } 378 379 @UiThreadTest 380 @Test 381 public void testMeasureSpecsWithPadding() { 382 MyView child = spy(new MyView(mActivity)); 383 mScrollViewCustomEmpty.setPadding(3, 5, 7, 11); 384 mScrollViewCustomEmpty.addView(child); 385 386 mScrollViewCustomEmpty.measureChild(child, 387 MeasureSpec.makeMeasureSpec(150, MeasureSpec.EXACTLY), 388 MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY)); 389 verify(child).onMeasure( 390 eq(MeasureSpec.makeMeasureSpec(140, MeasureSpec.EXACTLY)), 391 eq(MeasureSpec.makeMeasureSpec(84, MeasureSpec.UNSPECIFIED))); 392 } 393 394 @UiThreadTest 395 @Test 396 public void testMeasureSpecsWithMargins() { 397 MyView child = spy(new MyView(mActivity)); 398 mScrollViewCustomEmpty.addView(child); 399 400 mScrollViewCustomEmpty.measureChildWithMargins(child, 401 MeasureSpec.makeMeasureSpec(150, MeasureSpec.EXACTLY), 20, 402 MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY), 15); 403 verify(child).onMeasure( 404 eq(MeasureSpec.makeMeasureSpec(130, MeasureSpec.EXACTLY)), 405 eq(MeasureSpec.makeMeasureSpec(85, MeasureSpec.UNSPECIFIED))); 406 } 407 408 @UiThreadTest 409 @Test 410 public void testMeasureSpecsWithMarginsAndPadding() { 411 MyView child = spy(new MyView(mActivity)); 412 mScrollViewCustomEmpty.setPadding(3, 5, 7, 11); 413 mScrollViewCustomEmpty.addView(child); 414 415 mScrollViewCustomEmpty.measureChildWithMargins(child, 416 MeasureSpec.makeMeasureSpec(150, MeasureSpec.EXACTLY), 20, 417 MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY), 15); 418 verify(child).onMeasure( 419 eq(MeasureSpec.makeMeasureSpec(120, MeasureSpec.EXACTLY)), 420 eq(MeasureSpec.makeMeasureSpec(69, MeasureSpec.UNSPECIFIED))); 421 } 422 423 @UiThreadTest 424 @Test 425 public void testMeasureSpecsWithMarginsAndNoHintWidth() { 426 MyView child = spy(new MyView(mActivity)); 427 mScrollViewCustomEmpty.addView(child); 428 429 mScrollViewCustomEmpty.measureChildWithMargins(child, 430 MeasureSpec.makeMeasureSpec(150, MeasureSpec.EXACTLY), 20, 431 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 15); 432 verify(child).onMeasure( 433 eq(MeasureSpec.makeMeasureSpec(130, MeasureSpec.EXACTLY)), 434 eq(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED))); 435 } 436 437 @UiThreadTest 438 @Test 439 public void testFillViewport() { 440 mScrollViewRegular.setFillViewport(true); 441 442 MyView child = new MyView(mActivity); 443 child.setLayoutParams(new ViewGroup.LayoutParams(100, 100)); 444 445 mScrollViewRegular.addView(child); 446 mScrollViewRegular.measure( 447 MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY), 448 MeasureSpec.makeMeasureSpec(150, MeasureSpec.EXACTLY)); 449 450 assertEquals(150, child.getMeasuredHeight()); 451 assertEquals(100, child.getMeasuredWidth()); 452 453 mScrollViewRegular.layout(0, 0, 100, 150); 454 assertEquals(0, child.getTop()); 455 } 456 457 @UiThreadTest 458 @Test 459 public void testFillViewportWithScrollViewPadding() { 460 mScrollViewRegular.setFillViewport(true); 461 mScrollViewRegular.setPadding(3, 10, 5, 7); 462 463 MyView child = new MyView(mActivity); 464 child.setLayoutParams(new ViewGroup.LayoutParams(10,10)); 465 child.setDesiredHeight(30); 466 467 mScrollViewRegular.addView(child); 468 mScrollViewRegular.measure( 469 MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY), 470 MeasureSpec.makeMeasureSpec(150, MeasureSpec.EXACTLY)); 471 472 assertEquals(133, child.getMeasuredHeight()); 473 assertEquals(10, child.getMeasuredWidth()); 474 475 mScrollViewRegular.layout(0, 0, 100, 150); 476 assertEquals(10, child.getTop()); 477 } 478 479 @UiThreadTest 480 @Test 481 public void testFillViewportWithChildMargins() { 482 mScrollViewRegular.setFillViewport(true); 483 484 MyView child = new MyView(mActivity); 485 FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(10, 10); 486 lp.leftMargin = 3; 487 lp.topMargin = 10; 488 lp.rightMargin = 5; 489 lp.bottomMargin = 7; 490 child.setDesiredHeight(30); 491 child.setLayoutParams(lp); 492 493 mScrollViewRegular.addView(child); 494 mScrollViewRegular.measure(MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY), 495 MeasureSpec.makeMeasureSpec(150, MeasureSpec.EXACTLY)); 496 497 assertEquals(133, child.getMeasuredHeight()); 498 assertEquals(10, child.getMeasuredWidth()); 499 500 mScrollViewRegular.layout(0, 0, 100, 150); 501 assertEquals(10, child.getTop()); 502 } 503 504 @UiThreadTest 505 @Test 506 public void testFillViewportWithScrollViewPaddingAlreadyFills() { 507 mScrollViewRegular.setFillViewport(true); 508 mScrollViewRegular.setPadding(3, 10, 5, 7); 509 510 MyView child = new MyView(mActivity); 511 child.setDesiredHeight(175); 512 513 mScrollViewRegular.addView(child); 514 mScrollViewRegular.measure(MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY), 515 MeasureSpec.makeMeasureSpec(150, MeasureSpec.EXACTLY)); 516 517 518 assertEquals(92, child.getMeasuredWidth()); 519 assertEquals(175, child.getMeasuredHeight()); 520 521 mScrollViewRegular.layout(0, 0, 100, 150); 522 assertEquals(10, child.getTop()); 523 } 524 525 @UiThreadTest 526 @Test 527 public void testFillViewportWithChildMarginsAlreadyFills() { 528 mScrollViewRegular.setFillViewport(true); 529 MyView child = new MyView(mActivity); 530 FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( 531 ViewGroup.LayoutParams.MATCH_PARENT, 532 ViewGroup.LayoutParams.WRAP_CONTENT); 533 534 lp.leftMargin = 3; 535 lp.topMargin = 10; 536 lp.rightMargin = 5; 537 lp.bottomMargin = 7; 538 child.setLayoutParams(lp); 539 child.setDesiredHeight(175); 540 541 mScrollViewRegular.addView(child); 542 mScrollViewRegular.measure(MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY), 543 MeasureSpec.makeMeasureSpec(150, MeasureSpec.EXACTLY)); 544 545 assertEquals(92, child.getMeasuredWidth()); 546 assertEquals(175, child.getMeasuredHeight()); 547 548 mScrollViewRegular.layout(0, 0, 100, 150); 549 assertEquals(10, child.getTop()); 550 } 551 552 @UiThreadTest 553 @Test 554 public void testPageScroll() { 555 mScrollViewCustom.setSmoothScrollingEnabled(false); 556 assertEquals(0, mScrollViewCustom.getScrollY()); 557 558 assertTrue(mScrollViewCustom.pageScroll(View.FOCUS_DOWN)); 559 assertEquals(mPageHeight, mScrollViewCustom.getScrollY(), TOLERANCE); 560 561 assertTrue(mScrollViewCustom.pageScroll(View.FOCUS_DOWN)); 562 assertEquals(mPageHeight * 2, mScrollViewCustom.getScrollY(), TOLERANCE); 563 564 mScrollViewCustom.scrollTo(mPageWidth, mScrollBottom); 565 assertFalse(mScrollViewCustom.pageScroll(View.FOCUS_DOWN)); 566 assertEquals(mScrollBottom, mScrollViewCustom.getScrollY(), TOLERANCE); 567 568 assertTrue(mScrollViewCustom.pageScroll(View.FOCUS_UP)); 569 assertEquals(mScrollBottom - mPageHeight, mScrollViewCustom.getScrollY(), TOLERANCE); 570 571 assertTrue(mScrollViewCustom.pageScroll(View.FOCUS_UP)); 572 assertEquals(mScrollBottom -mPageHeight * 2, mScrollViewCustom.getScrollY(), TOLERANCE); 573 574 mScrollViewCustom.scrollTo(mPageWidth, 0); 575 assertFalse(mScrollViewCustom.pageScroll(View.FOCUS_UP)); 576 assertEquals(0, mScrollViewCustom.getScrollY()); 577 } 578 579 @UiThreadTest 580 @Test 581 public void testFullScroll() { 582 mScrollViewCustom.setSmoothScrollingEnabled(false); 583 assertEquals(0, mScrollViewCustom.getScrollY()); 584 585 assertTrue(mScrollViewCustom.fullScroll(View.FOCUS_DOWN)); 586 assertEquals(mScrollBottom, mScrollViewCustom.getScrollY()); 587 588 assertFalse(mScrollViewCustom.fullScroll(View.FOCUS_DOWN)); 589 assertEquals(mScrollBottom, mScrollViewCustom.getScrollY()); 590 591 assertTrue(mScrollViewCustom.fullScroll(View.FOCUS_UP)); 592 assertEquals(0, mScrollViewCustom.getScrollY()); 593 594 assertFalse(mScrollViewCustom.fullScroll(View.FOCUS_UP)); 595 assertEquals(0, mScrollViewCustom.getScrollY()); 596 } 597 598 @UiThreadTest 599 @Test 600 public void testArrowScroll() { 601 mScrollViewCustom.setSmoothScrollingEnabled(false); 602 assertEquals(0, mScrollViewCustom.getScrollY()); 603 604 int y = mScrollViewCustom.getScrollY(); 605 while (mScrollBottom != y) { 606 assertTrue(mScrollViewCustom.arrowScroll(View.FOCUS_DOWN)); 607 assertTrue(y <= mScrollViewCustom.getScrollY()); 608 y = mScrollViewCustom.getScrollY(); 609 } 610 611 assertFalse(mScrollViewCustom.arrowScroll(View.FOCUS_DOWN)); 612 assertEquals(mScrollBottom, mScrollViewCustom.getScrollY()); 613 614 y = mScrollViewCustom.getScrollY(); 615 while (0 != y) { 616 assertTrue(mScrollViewCustom.arrowScroll(View.FOCUS_UP)); 617 assertTrue(y >= mScrollViewCustom.getScrollY()); 618 y = mScrollViewCustom.getScrollY(); 619 } 620 621 assertFalse(mScrollViewCustom.arrowScroll(View.FOCUS_UP)); 622 assertEquals(0, mScrollViewCustom.getScrollY()); 623 } 624 625 @Test 626 public void testSmoothScrollBy() throws Throwable { 627 assertEquals(0, mScrollViewCustom.getScrollX()); 628 assertEquals(0, mScrollViewCustom.getScrollY()); 629 630 mActivityRule.runOnUiThread( 631 () -> mScrollViewCustom.smoothScrollBy(mScrollRight, mScrollBottom)); 632 // smoothScrollBy doesn't scroll in X 633 pollingCheckSmoothScrolling(0, 0, 0, mScrollBottom); 634 assertEquals(0, mScrollViewCustom.getScrollX()); 635 assertEquals(mScrollBottom, mScrollViewCustom.getScrollY()); 636 637 mActivityRule.runOnUiThread( 638 () -> mScrollViewCustom.smoothScrollBy(-mScrollRight, -mScrollBottom)); 639 pollingCheckSmoothScrolling(mScrollRight, 0, mScrollBottom, 0); 640 assertEquals(0, mScrollViewCustom.getScrollX()); 641 assertEquals(0, mScrollViewCustom.getScrollY()); 642 } 643 644 @Test 645 public void testSmoothScrollTo() throws Throwable { 646 assertEquals(0, mScrollViewCustom.getScrollX()); 647 assertEquals(0, mScrollViewCustom.getScrollY()); 648 649 mActivityRule.runOnUiThread( 650 () -> mScrollViewCustom.smoothScrollTo(mScrollRight, mScrollBottom)); 651 // smoothScrollTo doesn't scroll in X 652 pollingCheckSmoothScrolling(0, 0, 0, mScrollBottom); 653 assertEquals(0, mScrollViewCustom.getScrollX()); 654 assertEquals(mScrollBottom, mScrollViewCustom.getScrollY()); 655 656 mActivityRule.runOnUiThread( 657 () -> mScrollViewCustom.smoothScrollTo(mPageWidth, mPageHeight)); 658 pollingCheckSmoothScrolling(0, 0, mScrollBottom, mPageHeight); 659 assertEquals(0, mScrollViewCustom.getScrollX()); 660 assertEquals(mPageHeight, mScrollViewCustom.getScrollY()); 661 } 662 663 @UiThreadTest 664 @Test 665 public void testComputeScrollDeltaToGetChildRectOnScreen() { 666 mScrollViewCustom.setSmoothScrollingEnabled(false); 667 int edge = mScrollViewCustom.getVerticalFadingEdgeLength(); 668 669 // Rect's height is smaller than scroll view 670 Rect rect = new Rect(0, 0, 0, 0); 671 assertEquals(0, 672 ((MyScrollView) mScrollViewCustom).computeScrollDeltaToGetChildRectOnScreen(rect)); 673 674 rect = new Rect(0, edge, 0, mPageHeight); 675 assertEquals(0, 676 ((MyScrollView) mScrollViewCustom).computeScrollDeltaToGetChildRectOnScreen(rect)); 677 678 mScrollViewCustom.scrollTo(0, 0); 679 rect = new Rect(0, edge + 1, 0, mPageHeight); 680 assertEquals(edge, 681 ((MyScrollView) mScrollViewCustom).computeScrollDeltaToGetChildRectOnScreen(rect)); 682 } 683 684 @UiThreadTest 685 @Test 686 public void testComputeVerticalScrollRange() { 687 assertTrue(mScrollViewCustom.getChildCount() > 0); 688 assertEquals(mItemHeight * ITEM_COUNT, 689 ((MyScrollView) mScrollViewCustom).computeVerticalScrollRange(), TOLERANCE); 690 691 MyScrollView myScrollView = new MyScrollView(mActivity); 692 assertEquals(0, myScrollView.getChildCount()); 693 assertEquals(0, myScrollView.computeVerticalScrollRange()); 694 } 695 696 @UiThreadTest 697 @Test 698 public void testRequestChildFocus() { 699 mScrollViewCustom.setSmoothScrollingEnabled(false); 700 701 View firstChild = mScrollViewCustom.findViewById(R.id.first_child); 702 View lastChild = mScrollViewCustom.findViewById(R.id.last_child); 703 firstChild.requestFocus(); 704 705 int scrollY = mScrollViewCustom.getScrollY(); 706 mScrollViewCustom.requestChildFocus(lastChild, lastChild); 707 // check scrolling to the child which wants focus 708 assertTrue(mScrollViewCustom.getScrollY() > scrollY); 709 710 scrollY = mScrollViewCustom.getScrollY(); 711 mScrollViewCustom.requestChildFocus(firstChild, firstChild); 712 // check scrolling to the child which wants focus 713 assertTrue(mScrollViewCustom.getScrollY() < scrollY); 714 } 715 716 @UiThreadTest 717 @Test 718 public void testRequestChildRectangleOnScreen() { 719 mScrollViewCustom.setSmoothScrollingEnabled(false); 720 mScrollViewCustom.setVerticalFadingEdgeEnabled(true); 721 int edge = mScrollViewCustom.getVerticalFadingEdgeLength(); 722 723 View child = mScrollViewCustom.findViewById(R.id.first_child); 724 int orgRectSize = (int)(10 * mActivity.getResources().getDisplayMetrics().density); 725 final Rect originalRect = new Rect(0, 0, orgRectSize, orgRectSize); 726 final Rect newRect = new Rect(mItemWidth - orgRectSize, mItemHeight - orgRectSize, 727 mItemWidth, mItemHeight); 728 729 assertFalse(mScrollViewCustom.requestChildRectangleOnScreen(child, originalRect, true)); 730 assertEquals(0, mScrollViewCustom.getScrollX()); 731 assertEquals(0, mScrollViewCustom.getScrollY()); 732 733 assertTrue(mScrollViewCustom.requestChildRectangleOnScreen(child, newRect, true)); 734 assertEquals(0, mScrollViewCustom.getScrollX()); 735 assertEquals(edge, mScrollViewCustom.getScrollY()); 736 } 737 738 @UiThreadTest 739 @Test 740 public void testRequestLayout() { 741 mScrollViewCustom.requestLayout(); 742 743 assertTrue(mScrollViewCustom.isLayoutRequested()); 744 } 745 746 @Test 747 public void testFling() throws Throwable { 748 mScrollViewCustom.setSmoothScrollingEnabled(true); 749 assertEquals(0, mScrollViewCustom.getScrollY()); 750 751 // fling towards bottom 752 mActivityRule.runOnUiThread(() -> mScrollViewCustom.fling(2000)); 753 pollingCheckFling(0, true); 754 755 final int currentY = mScrollViewCustom.getScrollY(); 756 // fling towards top 757 mActivityRule.runOnUiThread(() -> mScrollViewCustom.fling(-2000)); 758 pollingCheckFling(currentY, false); 759 } 760 761 @UiThreadTest 762 @Test 763 public void testScrollTo() { 764 mScrollViewCustom.setSmoothScrollingEnabled(false); 765 766 mScrollViewCustom.scrollTo(10, 10); 767 assertEquals(10, mScrollViewCustom.getScrollY()); 768 assertEquals(10, mScrollViewCustom.getScrollX()); 769 770 mScrollViewCustom.scrollTo(mPageWidth, mPageHeight); 771 assertEquals(mPageHeight, mScrollViewCustom.getScrollY()); 772 assertEquals(mPageWidth, mScrollViewCustom.getScrollX()); 773 774 mScrollViewCustom.scrollTo(mScrollRight, mScrollBottom); 775 assertEquals(mScrollBottom, mScrollViewCustom.getScrollY()); 776 assertEquals(mScrollRight, mScrollViewCustom.getScrollX()); 777 778 // reach the top and left 779 mScrollViewCustom.scrollTo(-10, -10); 780 assertEquals(0, mScrollViewCustom.getScrollY()); 781 assertEquals(0, mScrollViewCustom.getScrollX()); 782 } 783 784 @UiThreadTest 785 @Test 786 public void testGetVerticalFadingEdgeStrengths() { 787 MyScrollView myScrollViewCustom = (MyScrollView) mScrollViewCustom; 788 789 assertTrue(myScrollViewCustom.getChildCount() > 0); 790 assertTrue(myScrollViewCustom.getTopFadingEdgeStrength() <= 1.0f); 791 assertTrue(myScrollViewCustom.getTopFadingEdgeStrength() >= 0.0f); 792 assertTrue(myScrollViewCustom.getBottomFadingEdgeStrength() <= 1.0f); 793 assertTrue(myScrollViewCustom.getBottomFadingEdgeStrength() >= 0.0f); 794 795 MyScrollView myScrollView = new MyScrollView(mActivity); 796 assertEquals(0, myScrollView.getChildCount()); 797 assertTrue(myScrollView.getTopFadingEdgeStrength() <= 1.0f); 798 assertTrue(myScrollView.getTopFadingEdgeStrength() >= 0.0f); 799 assertTrue(myScrollView.getBottomFadingEdgeStrength() <= 1.0f); 800 assertTrue(myScrollView.getBottomFadingEdgeStrength() >= 0.0f); 801 } 802 803 private boolean isInRange(int current, int from, int to) { 804 if (from < to) { 805 return current >= from && current <= to; 806 } 807 return current <= from && current >= to; 808 } 809 810 private void pollingCheckSmoothScrolling(final int fromX, final int toX, 811 final int fromY, final int toY) { 812 813 if (fromX == toX && fromY == toY) { 814 return; 815 } 816 817 if (fromY != toY) { 818 PollingCheck.waitFor(() -> isInRange(mScrollViewCustom.getScrollY(), fromY, toY)); 819 } 820 821 if (fromX != toX) { 822 PollingCheck.waitFor(() -> isInRange(mScrollViewCustom.getScrollX(), fromX, toX)); 823 } 824 825 PollingCheck.waitFor( 826 () -> toX == mScrollViewCustom.getScrollX() 827 && toY == mScrollViewCustom.getScrollY()); 828 } 829 830 private void pollingCheckFling(final int startPosition, final boolean movingDown) { 831 PollingCheck.waitFor(() -> { 832 if (movingDown) { 833 return mScrollViewCustom.getScrollY() > startPosition; 834 } 835 return mScrollViewCustom.getScrollY() < startPosition; 836 }); 837 838 final int[] previousScrollY = new int[] { mScrollViewCustom.getScrollY() }; 839 PollingCheck.waitFor(() -> { 840 if (mScrollViewCustom.getScrollY() == previousScrollY[0]) { 841 return true; 842 } else { 843 previousScrollY[0] = mScrollViewCustom.getScrollY(); 844 return false; 845 } 846 }); 847 } 848 849 public static class MyView extends View { 850 // measure in this height if set 851 private Integer mDesiredHeight; 852 public MyView(Context context) { 853 super(context); 854 } 855 856 public void setDesiredHeight(Integer desiredHeight) { 857 mDesiredHeight = desiredHeight; 858 } 859 860 @Override 861 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 862 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 863 if (mDesiredHeight != null) { 864 int mode = MeasureSpec.getMode(heightMeasureSpec); 865 int size = MeasureSpec.getSize(heightMeasureSpec); 866 int newHeight = size; 867 if (mode == MeasureSpec.AT_MOST) { 868 newHeight = Math.max(size, mDesiredHeight); 869 } else if (mode == MeasureSpec.UNSPECIFIED) { 870 newHeight = mDesiredHeight; 871 } 872 setMeasuredDimension(getMeasuredWidth(), newHeight); 873 } 874 } 875 } 876 877 public static class MyScrollView extends ScrollView { 878 public MyScrollView(Context context) { 879 super(context); 880 } 881 882 public MyScrollView(Context context, AttributeSet attrs) { 883 super(context, attrs); 884 } 885 886 public MyScrollView(Context context, AttributeSet attrs, int defStyle) { 887 super(context, attrs, defStyle); 888 } 889 890 @Override 891 protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) { 892 return super.computeScrollDeltaToGetChildRectOnScreen(rect); 893 } 894 895 @Override 896 protected int computeVerticalScrollRange() { 897 return super.computeVerticalScrollRange(); 898 } 899 900 @Override 901 protected float getBottomFadingEdgeStrength() { 902 return super.getBottomFadingEdgeStrength(); 903 } 904 905 @Override 906 protected float getTopFadingEdgeStrength() { 907 return super.getTopFadingEdgeStrength(); 908 } 909 910 @Override 911 protected void measureChild(View c, int pWidthMeasureSpec, int pHeightMeasureSpec) { 912 super.measureChild(c, pWidthMeasureSpec, pHeightMeasureSpec); 913 } 914 915 @Override 916 protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, 917 int widthUsed, int parentHeightMeasureSpec, int heightUsed) { 918 super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed, 919 parentHeightMeasureSpec, heightUsed); 920 } 921 } 922 } 923