1/* 2 * Copyright (C) 2014 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 android.support.v7.widget; 18 19import static org.junit.Assert.assertEquals; 20import static org.junit.Assert.assertFalse; 21import static org.junit.Assert.assertNotEquals; 22import static org.junit.Assert.assertNotNull; 23import static org.junit.Assert.assertNotSame; 24import static org.junit.Assert.assertNull; 25import static org.junit.Assert.assertSame; 26import static org.junit.Assert.assertTrue; 27import static org.junit.Assert.fail; 28 29import android.content.Context; 30import android.os.Build; 31import android.os.Parcel; 32import android.os.Parcelable; 33import android.os.SystemClock; 34import android.support.test.InstrumentationRegistry; 35import android.support.test.filters.SmallTest; 36import android.support.test.runner.AndroidJUnit4; 37import android.util.AttributeSet; 38import android.util.SparseArray; 39import android.view.MotionEvent; 40import android.view.View; 41import android.view.ViewGroup; 42import android.view.animation.Interpolator; 43import android.view.animation.LinearInterpolator; 44import android.widget.FrameLayout; 45import android.widget.TextView; 46 47import org.junit.Before; 48import org.junit.Test; 49import org.junit.runner.RunWith; 50 51import java.lang.ref.WeakReference; 52import java.util.ArrayList; 53import java.util.List; 54import java.util.UUID; 55 56@SmallTest 57@RunWith(AndroidJUnit4.class) 58public class RecyclerViewBasicTest { 59 60 RecyclerView mRecyclerView; 61 62 @Before 63 public void setUp() throws Exception { 64 mRecyclerView = new RecyclerView(getContext()); 65 } 66 67 private Context getContext() { 68 return InstrumentationRegistry.getContext(); 69 } 70 71 @Test 72 public void measureWithoutLayoutManager() { 73 measure(); 74 } 75 76 private void measure() { 77 mRecyclerView.measure(View.MeasureSpec.AT_MOST | 320, View.MeasureSpec.AT_MOST | 240); 78 } 79 80 private void layout() { 81 mRecyclerView.layout(0, 0, 320, 320); 82 } 83 84 private void focusSearch() { 85 mRecyclerView.focusSearch(1); 86 } 87 88 @Test 89 public void layoutWithoutAdapter() throws InterruptedException { 90 MockLayoutManager layoutManager = new MockLayoutManager(); 91 mRecyclerView.setLayoutManager(layoutManager); 92 layout(); 93 assertEquals("layout manager should not be called if there is no adapter attached", 94 0, layoutManager.mLayoutCount); 95 } 96 97 public void setScrollContainer() { 98 assertEquals("RecyclerView should announce itself as scroll container for the IME to " 99 + "handle it properly", true, mRecyclerView.isScrollContainer()); 100 } 101 102 @Test 103 public void layoutWithoutLayoutManager() throws InterruptedException { 104 mRecyclerView.setAdapter(new MockAdapter(20)); 105 measure(); 106 layout(); 107 } 108 109 @Test 110 public void focusWithoutLayoutManager() throws InterruptedException { 111 mRecyclerView.setAdapter(new MockAdapter(20)); 112 measure(); 113 layout(); 114 focusSearch(); 115 } 116 117 @Test 118 public void scrollWithoutLayoutManager() throws InterruptedException { 119 mRecyclerView.setAdapter(new MockAdapter(20)); 120 measure(); 121 layout(); 122 mRecyclerView.scrollBy(10, 10); 123 } 124 125 @Test 126 public void smoothScrollWithoutLayoutManager() throws InterruptedException { 127 mRecyclerView.setAdapter(new MockAdapter(20)); 128 measure(); 129 layout(); 130 mRecyclerView.smoothScrollBy(10, 10); 131 } 132 133 @Test 134 public void scrollToPositionWithoutLayoutManager() throws InterruptedException { 135 mRecyclerView.setAdapter(new MockAdapter(20)); 136 measure(); 137 layout(); 138 mRecyclerView.scrollToPosition(5); 139 } 140 141 @Test 142 public void smoothScrollToPositionWithoutLayoutManager() throws InterruptedException { 143 mRecyclerView.setAdapter(new MockAdapter(20)); 144 measure(); 145 layout(); 146 mRecyclerView.smoothScrollToPosition(5); 147 } 148 149 @Test 150 public void interceptTouchWithoutLayoutManager() { 151 mRecyclerView.setAdapter(new MockAdapter(20)); 152 measure(); 153 layout(); 154 assertFalse(mRecyclerView.onInterceptTouchEvent( 155 MotionEvent.obtain(SystemClock.uptimeMillis(), 156 SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, 10, 10, 0))); 157 } 158 159 @Test 160 public void onTouchWithoutLayoutManager() { 161 mRecyclerView.setAdapter(new MockAdapter(20)); 162 measure(); 163 layout(); 164 assertFalse(mRecyclerView.onTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), 165 SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, 10, 10, 0))); 166 } 167 168 @Test 169 public void layoutSimple() throws InterruptedException { 170 MockLayoutManager layoutManager = new MockLayoutManager(); 171 mRecyclerView.setLayoutManager(layoutManager); 172 mRecyclerView.setAdapter(new MockAdapter(3)); 173 layout(); 174 assertEquals("when both layout manager and activity is set, recycler view should call" 175 + " layout manager's layout method", 1, layoutManager.mLayoutCount); 176 } 177 178 @Test 179 public void observingAdapters() { 180 MockAdapter adapterOld = new MockAdapter(1); 181 mRecyclerView.setAdapter(adapterOld); 182 assertTrue("attached adapter should have observables", adapterOld.hasObservers()); 183 184 MockAdapter adapterNew = new MockAdapter(2); 185 mRecyclerView.setAdapter(adapterNew); 186 assertFalse("detached adapter should lose observable", adapterOld.hasObservers()); 187 assertTrue("new adapter should have observers", adapterNew.hasObservers()); 188 189 mRecyclerView.setAdapter(null); 190 assertNull("adapter should be removed successfully", mRecyclerView.getAdapter()); 191 assertFalse("when adapter is removed, observables should be removed too", 192 adapterNew.hasObservers()); 193 } 194 195 @Test 196 public void adapterChangeCallbacks() { 197 MockLayoutManager layoutManager = new MockLayoutManager(); 198 mRecyclerView.setLayoutManager(layoutManager); 199 MockAdapter adapterOld = new MockAdapter(1); 200 mRecyclerView.setAdapter(adapterOld); 201 layoutManager.assertPrevNextAdapters(null, adapterOld); 202 203 MockAdapter adapterNew = new MockAdapter(2); 204 mRecyclerView.setAdapter(adapterNew); 205 layoutManager.assertPrevNextAdapters("switching adapters should trigger correct callbacks" 206 , adapterOld, adapterNew); 207 208 mRecyclerView.setAdapter(null); 209 layoutManager.assertPrevNextAdapters( 210 "Setting adapter null should trigger correct callbacks", 211 adapterNew, null); 212 } 213 214 @Test 215 public void recyclerOffsetsOnMove() { 216 MockLayoutManager layoutManager = new MockLayoutManager(); 217 final List<RecyclerView.ViewHolder> recycledVhs = new ArrayList<>(); 218 mRecyclerView.setLayoutManager(layoutManager); 219 MockAdapter adapter = new MockAdapter(100) { 220 @Override 221 public void onViewRecycled(RecyclerView.ViewHolder holder) { 222 super.onViewRecycled(holder); 223 recycledVhs.add(holder); 224 } 225 }; 226 MockViewHolder mvh = new MockViewHolder(new TextView(getContext())); 227 mRecyclerView.setAdapter(adapter); 228 adapter.bindViewHolder(mvh, 20); 229 mRecyclerView.mRecycler.mCachedViews.add(mvh); 230 mRecyclerView.offsetPositionRecordsForRemove(10, 9, false); 231 232 mRecyclerView.offsetPositionRecordsForRemove(11, 1, false); 233 assertEquals(1, recycledVhs.size()); 234 assertSame(mvh, recycledVhs.get(0)); 235 } 236 237 @Test 238 public void recyclerOffsetsOnAdd() { 239 MockLayoutManager layoutManager = new MockLayoutManager(); 240 final List<RecyclerView.ViewHolder> recycledVhs = new ArrayList<>(); 241 mRecyclerView.setLayoutManager(layoutManager); 242 MockAdapter adapter = new MockAdapter(100) { 243 @Override 244 public void onViewRecycled(RecyclerView.ViewHolder holder) { 245 super.onViewRecycled(holder); 246 recycledVhs.add(holder); 247 } 248 }; 249 MockViewHolder mvh = new MockViewHolder(new TextView(getContext())); 250 mRecyclerView.setAdapter(adapter); 251 adapter.bindViewHolder(mvh, 20); 252 mRecyclerView.mRecycler.mCachedViews.add(mvh); 253 mRecyclerView.offsetPositionRecordsForRemove(10, 9, false); 254 255 mRecyclerView.offsetPositionRecordsForInsert(15, 10); 256 assertEquals(11, mvh.mPosition); 257 } 258 259 @Test 260 public void savedStateWithStatelessLayoutManager() throws InterruptedException { 261 mRecyclerView.setLayoutManager(new MockLayoutManager() { 262 @Override 263 public Parcelable onSaveInstanceState() { 264 return null; 265 } 266 }); 267 mRecyclerView.setAdapter(new MockAdapter(3)); 268 Parcel parcel = Parcel.obtain(); 269 String parcelSuffix = UUID.randomUUID().toString(); 270 Parcelable savedState = mRecyclerView.onSaveInstanceState(); 271 savedState.writeToParcel(parcel, 0); 272 parcel.writeString(parcelSuffix); 273 274 // reset position for reading 275 parcel.setDataPosition(0); 276 RecyclerView restored = new RecyclerView(getContext()); 277 restored.setLayoutManager(new MockLayoutManager()); 278 mRecyclerView.setAdapter(new MockAdapter(3)); 279 // restore 280 savedState = RecyclerView.SavedState.CREATOR.createFromParcel(parcel); 281 restored.onRestoreInstanceState(savedState); 282 283 assertEquals("Parcel reading should not go out of bounds", parcelSuffix, 284 parcel.readString()); 285 assertEquals("When unmarshalling, all of the parcel should be read", 0, parcel.dataAvail()); 286 287 } 288 289 @Test 290 public void savedState() throws InterruptedException { 291 MockLayoutManager mlm = new MockLayoutManager(); 292 mRecyclerView.setLayoutManager(mlm); 293 mRecyclerView.setAdapter(new MockAdapter(3)); 294 layout(); 295 Parcelable savedState = mRecyclerView.onSaveInstanceState(); 296 // we append a suffix to the parcelable to test out of bounds 297 String parcelSuffix = UUID.randomUUID().toString(); 298 Parcel parcel = Parcel.obtain(); 299 savedState.writeToParcel(parcel, 0); 300 parcel.writeString(parcelSuffix); 301 302 // reset for reading 303 parcel.setDataPosition(0); 304 // re-create 305 savedState = RecyclerView.SavedState.CREATOR.createFromParcel(parcel); 306 307 RecyclerView restored = new RecyclerView(getContext()); 308 MockLayoutManager mlmRestored = new MockLayoutManager(); 309 restored.setLayoutManager(mlmRestored); 310 restored.setAdapter(new MockAdapter(3)); 311 restored.onRestoreInstanceState(savedState); 312 313 assertEquals("Parcel reading should not go out of bounds", parcelSuffix, 314 parcel.readString()); 315 assertEquals("When unmarshalling, all of the parcel should be read", 0, parcel.dataAvail()); 316 assertEquals("uuid in layout manager should be preserved properly", mlm.mUuid, 317 mlmRestored.mUuid); 318 assertNotSame("stateless parameter should not be preserved", mlm.mLayoutCount, 319 mlmRestored.mLayoutCount); 320 layout(); 321 } 322 323 @Test 324 public void dontSaveChildrenState() throws InterruptedException { 325 MockLayoutManager mlm = new MockLayoutManager() { 326 @Override 327 public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 328 super.onLayoutChildren(recycler, state); 329 View view = recycler.getViewForPosition(0); 330 addView(view); 331 measureChildWithMargins(view, 0, 0); 332 view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); 333 } 334 }; 335 mRecyclerView.setLayoutManager(mlm); 336 mRecyclerView.setAdapter(new MockAdapter(3) { 337 @Override 338 public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 339 final LoggingView itemView = new LoggingView(parent.getContext()); 340 //noinspection ResourceType 341 itemView.setId(3); 342 return new MockViewHolder(itemView); 343 } 344 }); 345 measure(); 346 layout(); 347 View view = mRecyclerView.getChildAt(0); 348 assertNotNull("test sanity", view); 349 LoggingView loggingView = (LoggingView) view; 350 SparseArray<Parcelable> container = new SparseArray<Parcelable>(); 351 mRecyclerView.saveHierarchyState(container); 352 assertEquals("children's save state method should not be called", 0, 353 loggingView.getOnSavedInstanceCnt()); 354 } 355 356 @Test 357 public void smoothScrollWithCustomInterpolator() { 358 mRecyclerView.setLayoutManager(new MockLayoutManager()); 359 mRecyclerView.setAdapter(new MockAdapter(20)); 360 Interpolator interpolator = new LinearInterpolator(); 361 mRecyclerView.smoothScrollBy(0, 100, interpolator); 362 assertSame(interpolator, mRecyclerView.mViewFlinger.mInterpolator); 363 364 mRecyclerView.smoothScrollBy(0, -100); 365 assertSame(RecyclerView.sQuinticInterpolator, mRecyclerView.mViewFlinger.mInterpolator); 366 } 367 368 @Test 369 public void prefetchChangesCacheSize() { 370 mRecyclerView.setAdapter(new MockAdapter(20)); 371 MockLayoutManager mlm = new MockLayoutManager() { 372 @Override 373 public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state, 374 RecyclerView.LayoutManager.LayoutPrefetchRegistry prefetchManager) { 375 prefetchManager.addPosition(0, 0); 376 prefetchManager.addPosition(1, 0); 377 prefetchManager.addPosition(2, 0); 378 } 379 }; 380 381 RecyclerView.Recycler recycler = mRecyclerView.mRecycler; 382 assertEquals(RecyclerView.Recycler.DEFAULT_CACHE_SIZE, recycler.mViewCacheMax); 383 mRecyclerView.setLayoutManager(mlm); 384 assertEquals(RecyclerView.Recycler.DEFAULT_CACHE_SIZE, recycler.mViewCacheMax); 385 386 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 387 // layout, so prefetches can occur 388 mRecyclerView.measure(View.MeasureSpec.EXACTLY | 100, View.MeasureSpec.EXACTLY | 100); 389 mRecyclerView.layout(0, 0, 100, 100); 390 391 // prefetch gets 3 items, so expands cache by 3 392 mRecyclerView.mPrefetchRegistry.collectPrefetchPositionsFromView(mRecyclerView, false); 393 assertEquals(3, mRecyclerView.mPrefetchRegistry.mCount); 394 assertEquals(RecyclerView.Recycler.DEFAULT_CACHE_SIZE + 3, recycler.mViewCacheMax); 395 396 // Reset to default by removing layout 397 mRecyclerView.setLayoutManager(null); 398 assertEquals(RecyclerView.Recycler.DEFAULT_CACHE_SIZE, recycler.mViewCacheMax); 399 400 // And restore by restoring layout 401 mRecyclerView.setLayoutManager(mlm); 402 assertEquals(RecyclerView.Recycler.DEFAULT_CACHE_SIZE + 3, recycler.mViewCacheMax); 403 } 404 } 405 406 @Test 407 public void getNanoTime() { 408 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 409 // check that it looks vaguely time-ish 410 long time = mRecyclerView.getNanoTime(); 411 assertNotEquals(0, time); 412 assertNotEquals(time, mRecyclerView.getNanoTime()); 413 } else { 414 // expect to avoid cost of system.nanoTime on older platforms that don't do prefetch 415 assertEquals(0, mRecyclerView.getNanoTime()); 416 } 417 } 418 419 @Test 420 public void findNestedRecyclerView() { 421 RecyclerView recyclerView = new RecyclerView(getContext()); 422 assertEquals(recyclerView, RecyclerView.findNestedRecyclerView(recyclerView)); 423 424 ViewGroup parent = new FrameLayout(getContext()); 425 assertEquals(null, RecyclerView.findNestedRecyclerView(parent)); 426 parent.addView(recyclerView); 427 assertEquals(recyclerView, RecyclerView.findNestedRecyclerView(parent)); 428 429 ViewGroup grandParent = new FrameLayout(getContext()); 430 assertEquals(null, RecyclerView.findNestedRecyclerView(grandParent)); 431 grandParent.addView(parent); 432 assertEquals(recyclerView, RecyclerView.findNestedRecyclerView(grandParent)); 433 } 434 435 @Test 436 public void clearNestedRecyclerViewIfNotNested() { 437 RecyclerView recyclerView = new RecyclerView(getContext()); 438 ViewGroup parent = new FrameLayout(getContext()); 439 parent.addView(recyclerView); 440 ViewGroup grandParent = new FrameLayout(getContext()); 441 grandParent.addView(parent); 442 443 // verify trivial noop case 444 RecyclerView.ViewHolder holder = new RecyclerView.ViewHolder(recyclerView) {}; 445 holder.mNestedRecyclerView = new WeakReference<>(recyclerView); 446 RecyclerView.clearNestedRecyclerViewIfNotNested(holder); 447 assertEquals(recyclerView, holder.mNestedRecyclerView.get()); 448 449 // verify clear case 450 holder = new RecyclerView.ViewHolder(new View(getContext())) {}; 451 holder.mNestedRecyclerView = new WeakReference<>(recyclerView); 452 RecyclerView.clearNestedRecyclerViewIfNotNested(holder); 453 assertNull(holder.mNestedRecyclerView); 454 455 // verify more deeply nested case 456 holder = new RecyclerView.ViewHolder(grandParent) {}; 457 holder.mNestedRecyclerView = new WeakReference<>(recyclerView); 458 RecyclerView.clearNestedRecyclerViewIfNotNested(holder); 459 assertEquals(recyclerView, holder.mNestedRecyclerView.get()); 460 } 461 462 @Test 463 public void toStringContainsClasses() { 464 RecyclerView recyclerView = new RecyclerView(getContext()); 465 recyclerView.setAdapter(new MockAdapter(10)); 466 recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); 467 468 String string = recyclerView.toString(); 469 assertTrue("must contain RV class", string.contains(RecyclerView.class.getName())); 470 assertTrue("must contain Adapter class", string.contains(MockAdapter.class.getName())); 471 assertTrue("must contain LM class", string.contains(LinearLayoutManager.class.getName())); 472 assertTrue("must contain ctx class", string.contains(getContext().getClass().getName())); 473 } 474 475 @Test 476 public void exceptionContainsClasses() { 477 RecyclerView recyclerView = new RecyclerView(getContext()); 478 recyclerView.setAdapter(new MockAdapter(10)); 479 480 try { 481 recyclerView.generateDefaultLayoutParams(); 482 fail("exception expected"); 483 } catch (IllegalStateException e) { 484 String message = e.getMessage(); 485 assertTrue("must contain RV class", 486 message.contains(RecyclerView.class.getName())); 487 assertTrue("must contain Adapter class", 488 message.contains(MockAdapter.class.getName())); 489 } 490 } 491 492 static class MockLayoutManager extends RecyclerView.LayoutManager { 493 494 int mLayoutCount = 0; 495 496 int mAdapterChangedCount = 0; 497 498 RecyclerView.Adapter mPrevAdapter; 499 500 RecyclerView.Adapter mNextAdapter; 501 502 String mUuid = UUID.randomUUID().toString(); 503 504 @Override 505 public void onAdapterChanged(RecyclerView.Adapter oldAdapter, 506 RecyclerView.Adapter newAdapter) { 507 super.onAdapterChanged(oldAdapter, newAdapter); 508 mPrevAdapter = oldAdapter; 509 mNextAdapter = newAdapter; 510 mAdapterChangedCount++; 511 } 512 513 @Override 514 public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 515 mLayoutCount += 1; 516 } 517 518 @Override 519 public Parcelable onSaveInstanceState() { 520 LayoutManagerSavedState lss = new LayoutManagerSavedState(); 521 lss.mUuid = mUuid; 522 return lss; 523 } 524 525 @Override 526 public void onRestoreInstanceState(Parcelable state) { 527 super.onRestoreInstanceState(state); 528 if (state instanceof LayoutManagerSavedState) { 529 mUuid = ((LayoutManagerSavedState) state).mUuid; 530 } 531 } 532 533 @Override 534 public RecyclerView.LayoutParams generateDefaultLayoutParams() { 535 return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 536 ViewGroup.LayoutParams.WRAP_CONTENT); 537 } 538 539 public void assertPrevNextAdapters(String message, RecyclerView.Adapter prevAdapter, 540 RecyclerView.Adapter nextAdapter) { 541 assertSame(message, prevAdapter, mPrevAdapter); 542 assertSame(message, nextAdapter, mNextAdapter); 543 } 544 545 public void assertPrevNextAdapters(RecyclerView.Adapter prevAdapter, 546 RecyclerView.Adapter nextAdapter) { 547 assertPrevNextAdapters("Adapters from onAdapterChanged callback should match", 548 prevAdapter, nextAdapter); 549 } 550 551 @Override 552 public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, 553 RecyclerView.State state) { 554 return dx; 555 } 556 557 @Override 558 public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, 559 RecyclerView.State state) { 560 return dy; 561 } 562 563 @Override 564 public boolean canScrollHorizontally() { 565 return true; 566 } 567 568 @Override 569 public boolean canScrollVertically() { 570 return true; 571 } 572 } 573 574 static class LayoutManagerSavedState implements Parcelable { 575 576 String mUuid; 577 578 public LayoutManagerSavedState(Parcel in) { 579 mUuid = in.readString(); 580 } 581 582 public LayoutManagerSavedState() { 583 584 } 585 586 @Override 587 public int describeContents() { 588 return 0; 589 } 590 591 @Override 592 public void writeToParcel(Parcel dest, int flags) { 593 dest.writeString(mUuid); 594 } 595 596 public static final Parcelable.Creator<LayoutManagerSavedState> CREATOR 597 = new Parcelable.Creator<LayoutManagerSavedState>() { 598 @Override 599 public LayoutManagerSavedState createFromParcel(Parcel in) { 600 return new LayoutManagerSavedState(in); 601 } 602 603 @Override 604 public LayoutManagerSavedState[] newArray(int size) { 605 return new LayoutManagerSavedState[size]; 606 } 607 }; 608 } 609 610 static class MockAdapter extends RecyclerView.Adapter { 611 612 private int mCount = 0; 613 614 615 MockAdapter(int count) { 616 this.mCount = count; 617 } 618 619 @Override 620 public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 621 return new MockViewHolder(new TextView(parent.getContext())); 622 } 623 624 @Override 625 public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 626 627 } 628 629 @Override 630 public int getItemCount() { 631 return mCount; 632 } 633 634 void removeItems(int start, int count) { 635 mCount -= count; 636 notifyItemRangeRemoved(start, count); 637 } 638 639 void addItems(int start, int count) { 640 mCount += count; 641 notifyItemRangeInserted(start, count); 642 } 643 } 644 645 static class MockViewHolder extends RecyclerView.ViewHolder { 646 public Object mItem; 647 public MockViewHolder(View itemView) { 648 super(itemView); 649 } 650 } 651 652 static class LoggingView extends TextView { 653 private int mOnSavedInstanceCnt = 0; 654 655 public LoggingView(Context context) { 656 super(context); 657 } 658 659 public LoggingView(Context context, AttributeSet attrs) { 660 super(context, attrs); 661 } 662 663 public LoggingView(Context context, AttributeSet attrs, int defStyleAttr) { 664 super(context, attrs, defStyleAttr); 665 } 666 667 @Override 668 public Parcelable onSaveInstanceState() { 669 mOnSavedInstanceCnt ++; 670 return super.onSaveInstanceState(); 671 } 672 673 public int getOnSavedInstanceCnt() { 674 return mOnSavedInstanceCnt; 675 } 676 } 677}