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