RecyclerViewCacheTest.java revision b9d3b54b0b5fa223867cf11e47ee3f31ee474031
136c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik/* 236c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik * Copyright (C) 2016 The Android Open Source Project 336c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik * 436c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik * Licensed under the Apache License, Version 2.0 (the "License"); 536c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik * you may not use this file except in compliance with the License. 636c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik * You may obtain a copy of the License at 736c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik * 836c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik * http://www.apache.org/licenses/LICENSE-2.0 936c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik * 1036c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik * Unless required by applicable law or agreed to in writing, software 1136c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik * distributed under the License is distributed on an "AS IS" BASIS, 1236c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1336c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik * See the License for the specific language governing permissions and 1436c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik * limitations under the License. 1536c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik */ 1636c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 1736c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craikpackage android.support.v7.widget; 1836c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 19945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craikimport static org.junit.Assert.assertEquals; 200fab45107f043a4856aad2e1fd785b396898616eChris Craikimport static org.junit.Assert.assertFalse; 211e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craikimport static org.junit.Assert.assertNotEquals; 22dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craikimport static org.junit.Assert.assertNotNull; 230fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Guimport static org.junit.Assert.assertSame; 24ee9f1a3ebd8f071cea11fd97f9a3f3d41027189cAurimas Liutikasimport static org.junit.Assert.assertTrue; 25ee9f1a3ebd8f071cea11fd97f9a3f3d41027189cAurimas Liutikasimport static org.mockito.Matchers.any; 26ee9f1a3ebd8f071cea11fd97f9a3f3d41027189cAurimas Liutikasimport static org.mockito.Matchers.anyInt; 271e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craikimport static org.mockito.Matchers.argThat; 28ee9f1a3ebd8f071cea11fd97f9a3f3d41027189cAurimas Liutikasimport static org.mockito.Mockito.mock; 29ee9f1a3ebd8f071cea11fd97f9a3f3d41027189cAurimas Liutikasimport static org.mockito.Mockito.never; 30ee9f1a3ebd8f071cea11fd97f9a3f3d41027189cAurimas Liutikasimport static org.mockito.Mockito.times; 31ee9f1a3ebd8f071cea11fd97f9a3f3d41027189cAurimas Liutikasimport static org.mockito.Mockito.verify; 32ee9f1a3ebd8f071cea11fd97f9a3f3d41027189cAurimas Liutikasimport static org.mockito.Mockito.when; 33ee9f1a3ebd8f071cea11fd97f9a3f3d41027189cAurimas Liutikas 3436c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craikimport android.content.Context; 35e85a5c8c0bfc0aa1ca0796c7bd1f916049e55d68Aurimas Liutikasimport android.os.Build; 360ceae57ee14da826c31a66c67e70f7f17108d5caChris Craikimport android.os.Parcelable; 370951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craikimport android.os.SystemClock; 3836c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craikimport android.support.test.InstrumentationRegistry; 39ee9f1a3ebd8f071cea11fd97f9a3f3d41027189cAurimas Liutikasimport android.support.test.filters.SdkSuppress; 40754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport android.support.test.filters.SmallTest; 4136c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craikimport android.support.test.runner.AndroidJUnit4; 420951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craikimport android.view.MotionEvent; 4336c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craikimport android.view.View; 4436c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craikimport android.view.ViewGroup; 450951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craikimport android.widget.FrameLayout; 4636c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 471e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craikimport org.hamcrest.BaseMatcher; 481e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craikimport org.hamcrest.Description; 49356880d3de117b067522ad452f4e3eed85ce444cChris Craikimport org.junit.After; 5036c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craikimport org.junit.Before; 5136c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craikimport org.junit.Test; 5236c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craikimport org.junit.runner.RunWith; 5336c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craikimport org.mockito.invocation.InvocationOnMock; 5436c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craikimport org.mockito.stubbing.Answer; 5536c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 561e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craikimport java.util.ArrayList; 5736c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craikimport java.util.List; 58a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craikimport java.util.concurrent.TimeUnit; 5936c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 6036c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik@SmallTest 61e85a5c8c0bfc0aa1ca0796c7bd1f916049e55d68Aurimas Liutikas@SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP) 6236c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik@RunWith(AndroidJUnit4.class) 6336c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craikpublic class RecyclerViewCacheTest { 64a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik TimeMockingRecyclerView mRecyclerView; 6536c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik RecyclerView.Recycler mRecycler; 6636c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 67a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik private class TimeMockingRecyclerView extends RecyclerView { 68a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik private long mMockNanoTime = 0; 69a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik 70a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik TimeMockingRecyclerView(Context context) { 71a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik super(context); 72a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik } 73a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik 74a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik public void registerTimePassingMs(long ms) { 75a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik mMockNanoTime += TimeUnit.MILLISECONDS.toNanos(ms); 76a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik } 77a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik 78a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik @Override 79a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik long getNanoTime() { 80a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik return mMockNanoTime; 81a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik } 82bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik 83bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik @Override 84bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik public int getWindowVisibility() { 85bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik // Pretend to be visible to avoid being filtered out 86bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik return View.VISIBLE; 87bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik } 88356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 89a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik 9036c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik @Before 91356880d3de117b067522ad452f4e3eed85ce444cChris Craik public void setup() throws Exception { 92a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik mRecyclerView = new TimeMockingRecyclerView(getContext()); 93356880d3de117b067522ad452f4e3eed85ce444cChris Craik mRecyclerView.onAttachedToWindow(); 9436c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik mRecycler = mRecyclerView.mRecycler; 9536c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik } 9636c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 97356880d3de117b067522ad452f4e3eed85ce444cChris Craik @After 98356880d3de117b067522ad452f4e3eed85ce444cChris Craik public void teardown() throws Exception { 99356880d3de117b067522ad452f4e3eed85ce444cChris Craik if (mRecyclerView.isAttachedToWindow()) { 100356880d3de117b067522ad452f4e3eed85ce444cChris Craik mRecyclerView.onDetachedFromWindow(); 101356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 102356880d3de117b067522ad452f4e3eed85ce444cChris Craik GapWorker gapWorker = GapWorker.sGapWorker.get(); 103356880d3de117b067522ad452f4e3eed85ce444cChris Craik if (gapWorker != null) { 104356880d3de117b067522ad452f4e3eed85ce444cChris Craik assertTrue(gapWorker.mRecyclerViews.isEmpty()); 105356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 106356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 107356880d3de117b067522ad452f4e3eed85ce444cChris Craik 10836c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik private Context getContext() { 10936c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik return InstrumentationRegistry.getContext(); 11036c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik } 11136c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 112dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik private void layout(int width, int height) { 113dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik mRecyclerView.measure( 114dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), 115dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)); 116dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik mRecyclerView.layout(0, 0, width, height); 117dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik } 118dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik 11936c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik @Test 12036c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik public void prefetchReusesCacheItems() { 12136c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik RecyclerView.LayoutManager prefetchingLayoutManager = new RecyclerView.LayoutManager() { 12236c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik @Override 12336c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik public RecyclerView.LayoutParams generateDefaultLayoutParams() { 12436c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 12536c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik ViewGroup.LayoutParams.WRAP_CONTENT); 12636c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik } 12736c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 12836c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik @Override 1291e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state, 1303104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik LayoutPrefetchRegistry prefetchManager) { 131945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik prefetchManager.addPosition(0, 0); 132945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik prefetchManager.addPosition(1, 0); 133945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik prefetchManager.addPosition(2, 0); 13436c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik } 13536c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 13636c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik @Override 13736c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 13836c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik } 13936c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik }; 14036c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik mRecyclerView.setLayoutManager(prefetchingLayoutManager); 14136c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 14236c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik RecyclerView.Adapter mockAdapter = mock(RecyclerView.Adapter.class); 14336c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik when(mockAdapter.onCreateViewHolder(any(ViewGroup.class), anyInt())) 14436c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik .thenAnswer(new Answer<RecyclerView.ViewHolder>() { 14536c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik @Override 14636c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik public RecyclerView.ViewHolder answer(InvocationOnMock invocation) 14736c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik throws Throwable { 14836c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik return new RecyclerView.ViewHolder(new View(getContext())) {}; 14936c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik } 15036c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik }); 15136c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik when(mockAdapter.getItemCount()).thenReturn(10); 15236c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik mRecyclerView.setAdapter(mockAdapter); 15336c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 154dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik layout(320, 320); 15536c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 15636c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik verify(mockAdapter, never()).onCreateViewHolder(any(ViewGroup.class), anyInt()); 15736c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik verify(mockAdapter, never()).onBindViewHolder( 15836c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik any(RecyclerView.ViewHolder.class), anyInt(), any(List.class)); 15936c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik assertTrue(mRecycler.mCachedViews.isEmpty()); 16036c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 16136c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik // Prefetch multiple times... 16236c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik for (int i = 0; i < 4; i++) { 16347d17a72daeb141148d13a0b0f2df146f6524a9dChris Craik mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS); 16436c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 16536c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik // ...but should only see the same three items fetched/bound once each 16636c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik verify(mockAdapter, times(3)).onCreateViewHolder(any(ViewGroup.class), anyInt()); 16736c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik verify(mockAdapter, times(3)).onBindViewHolder( 16836c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik any(RecyclerView.ViewHolder.class), anyInt(), any(List.class)); 16936c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 17036c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik assertTrue(mRecycler.mCachedViews.size() == 3); 1711e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik CacheUtils.verifyCacheContainsPrefetchedPositions(mRecyclerView, 0, 1, 2); 17236c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik } 17336c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik } 17436c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 17536c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik @Test 17636c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik public void prefetchItemsNotEvictedWithInserts() { 177dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik mRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 3)); 17836c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 17936c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik RecyclerView.Adapter mockAdapter = mock(RecyclerView.Adapter.class); 18036c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik when(mockAdapter.onCreateViewHolder(any(ViewGroup.class), anyInt())) 18136c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik .thenAnswer(new Answer<RecyclerView.ViewHolder>() { 18236c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik @Override 18336c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik public RecyclerView.ViewHolder answer(InvocationOnMock invocation) 18436c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik throws Throwable { 185dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik View view = new View(getContext()); 186dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik view.setMinimumWidth(100); 187dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik view.setMinimumHeight(100); 188dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik return new RecyclerView.ViewHolder(view) {}; 18936c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik } 19036c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik }); 19136c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik when(mockAdapter.getItemCount()).thenReturn(100); 19236c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik mRecyclerView.setAdapter(mockAdapter); 19336c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 194dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik layout(300, 100); 19536c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 196945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik assertEquals(2, mRecyclerView.mRecycler.mViewCacheMax); 197dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, 1); 19847d17a72daeb141148d13a0b0f2df146f6524a9dChris Craik mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS); 199945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik assertEquals(5, mRecyclerView.mRecycler.mViewCacheMax); 200945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 2011e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik CacheUtils.verifyCacheContainsPrefetchedPositions(mRecyclerView, 3, 4, 5); 20236c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 20336c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik // further views recycled, as though from scrolling, shouldn't evict prefetched views: 20436c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik mRecycler.recycleView(mRecycler.getViewForPosition(10)); 205dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik CacheUtils.verifyCacheContainsPositions(mRecyclerView, 3, 4, 5, 10); 20636c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 20736c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik mRecycler.recycleView(mRecycler.getViewForPosition(20)); 208dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik CacheUtils.verifyCacheContainsPositions(mRecyclerView, 3, 4, 5, 10, 20); 20936c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 21036c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik mRecycler.recycleView(mRecycler.getViewForPosition(30)); 211dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik CacheUtils.verifyCacheContainsPositions(mRecyclerView, 3, 4, 5, 20, 30); 21236c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 21336c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik mRecycler.recycleView(mRecycler.getViewForPosition(40)); 214dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik CacheUtils.verifyCacheContainsPositions(mRecyclerView, 3, 4, 5, 30, 40); 21536c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 21636c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik // After clearing the cache, the prefetch priorities should be cleared as well: 21736c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik mRecyclerView.mRecycler.recycleAndClearCachedViews(); 218dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik for (int i : new int[] {3, 4, 5, 50, 60, 70, 80, 90}) { 21936c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik mRecycler.recycleView(mRecycler.getViewForPosition(i)); 22036c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik } 22136c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 22236c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik // cache only contains most recent positions, no priority for previous prefetches: 223356880d3de117b067522ad452f4e3eed85ce444cChris Craik CacheUtils.verifyCacheContainsPositions(mRecyclerView, 50, 60, 70, 80, 90); 22436c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik } 22536c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 22636c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik @Test 22736c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik public void prefetchItemsNotEvictedOnScroll() { 22836c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik mRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 3)); 22936c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 23036c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik // 100x100 pixel views 23136c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik RecyclerView.Adapter mockAdapter = mock(RecyclerView.Adapter.class); 23236c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik when(mockAdapter.onCreateViewHolder(any(ViewGroup.class), anyInt())) 23336c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik .thenAnswer(new Answer<RecyclerView.ViewHolder>() { 23436c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik @Override 23536c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik public RecyclerView.ViewHolder answer(InvocationOnMock invocation) 23636c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik throws Throwable { 23736c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik View view = new View(getContext()); 23836c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik view.setMinimumWidth(100); 23936c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik view.setMinimumHeight(100); 24036c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik return new RecyclerView.ViewHolder(view) {}; 24136c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik } 24236c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik }); 24336c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik when(mockAdapter.getItemCount()).thenReturn(100); 24436c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik mRecyclerView.setAdapter(mockAdapter); 24536c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 24636c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik // NOTE: requested cache size must be smaller than span count so two rows cannot fit 24736c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik mRecyclerView.setItemViewCacheSize(2); 24836c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 249dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik layout(300, 150); 25036c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik mRecyclerView.scrollBy(0, 75); 25136c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik assertTrue(mRecycler.mCachedViews.isEmpty()); 25236c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 25336c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik // rows 0, 1, and 2 are all attached and visible. Prefetch row 3: 254945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, 1); 25547d17a72daeb141148d13a0b0f2df146f6524a9dChris Craik mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS); 25636c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 25736c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik // row 3 is cached: 258356880d3de117b067522ad452f4e3eed85ce444cChris Craik CacheUtils.verifyCacheContainsPositions(mRecyclerView, 9, 10, 11); 25936c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik assertTrue(mRecycler.mCachedViews.size() == 3); 26036c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 26136c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik // Scroll so 1 falls off (though 3 is still not on screen) 26236c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik mRecyclerView.scrollBy(0, 50); 26336c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik 26436c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik // row 3 is still cached, with a couple other recycled views: 265356880d3de117b067522ad452f4e3eed85ce444cChris Craik CacheUtils.verifyCacheContainsPositions(mRecyclerView, 9, 10, 11); 26636c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik assertTrue(mRecycler.mCachedViews.size() == 5); 26736c4d66a1bc449309f0a3b97d61dd5bf692d181dChris Craik } 268a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik 269a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik @Test 270b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik public void prefetchIsComputingLayout() { 271b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); 272b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik 273b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik // 100x100 pixel views 274b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik RecyclerView.Adapter mockAdapter = mock(RecyclerView.Adapter.class); 275b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik when(mockAdapter.onCreateViewHolder(any(ViewGroup.class), anyInt())) 276b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik .thenAnswer(new Answer<RecyclerView.ViewHolder>() { 277b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik @Override 278b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik public RecyclerView.ViewHolder answer(InvocationOnMock invocation) 279b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik throws Throwable { 280b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik View view = new View(getContext()); 281b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik view.setMinimumWidth(100); 282b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik view.setMinimumHeight(100); 283b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik assertTrue(mRecyclerView.isComputingLayout()); 284b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik return new RecyclerView.ViewHolder(view) {}; 285b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik } 286b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik }); 287b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik when(mockAdapter.getItemCount()).thenReturn(100); 288b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik mRecyclerView.setAdapter(mockAdapter); 289b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik 290b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik layout(100, 100); 291b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik 292b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik verify(mockAdapter, times(1)).onCreateViewHolder(mRecyclerView, 0); 293b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik 294b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik // prefetch an item, should still observe isComputingLayout in that create 295b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, 1); 296b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS); 297b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik 298b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik verify(mockAdapter, times(2)).onCreateViewHolder(mRecyclerView, 0); 299b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik } 300b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik 301b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik @Test 3020951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik public void prefetchDrag() { 3030951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik // event dispatch requires a parent 3040951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik ViewGroup parent = new FrameLayout(getContext()); 3050951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik parent.addView(mRecyclerView); 3060951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik 3070951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik 3080951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik mRecyclerView.setLayoutManager( 3090951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false)); 3100951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik 3110951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik // 1000x1000 pixel views 3120951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik RecyclerView.Adapter adapter = new RecyclerView.Adapter() { 3130951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik @Override 3140951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 3150951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik mRecyclerView.registerTimePassingMs(5); 3160951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik View view = new View(getContext()); 3170951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik view.setMinimumWidth(1000); 3180951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik view.setMinimumHeight(1000); 3190951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik return new RecyclerView.ViewHolder(view) {}; 3200951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik } 3210951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik 3220951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik @Override 3230951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 3240951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik mRecyclerView.registerTimePassingMs(5); 3250951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik } 3260951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik 3270951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik @Override 3280951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik public int getItemCount() { 3290951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik return 100; 3300951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik } 3310951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik }; 3320951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik mRecyclerView.setAdapter(adapter); 3330951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik 3340951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik layout(1000, 1000); 3350951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik 3360951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik long time = SystemClock.uptimeMillis(); 3370951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik mRecyclerView.onTouchEvent( 3380951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, 500, 1000, 0)); 3390951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik 3400951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik assertEquals(0, mRecyclerView.mPrefetchRegistry.mPrefetchDx); 3410951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik assertEquals(0, mRecyclerView.mPrefetchRegistry.mPrefetchDy); 3420951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik 3430951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik // Consume slop 3440951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik mRecyclerView.onTouchEvent( 3450951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik MotionEvent.obtain(time, time, MotionEvent.ACTION_MOVE, 50, 500, 0)); 3460951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik 3470951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik // move by 0,30 3480951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik mRecyclerView.onTouchEvent( 3490951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik MotionEvent.obtain(time, time, MotionEvent.ACTION_MOVE, 50, 470, 0)); 3500951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik assertEquals(0, mRecyclerView.mPrefetchRegistry.mPrefetchDx); 3510951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik assertEquals(30, mRecyclerView.mPrefetchRegistry.mPrefetchDy); 3520951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik 3530951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik // move by 10,15 3540951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik mRecyclerView.onTouchEvent( 3550951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik MotionEvent.obtain(time, time, MotionEvent.ACTION_MOVE, 40, 455, 0)); 3560951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik assertEquals(10, mRecyclerView.mPrefetchRegistry.mPrefetchDx); 3570951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik assertEquals(15, mRecyclerView.mPrefetchRegistry.mPrefetchDy); 3580951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik 3590951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik // move by 0,0 - IGNORED 3600951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik mRecyclerView.onTouchEvent( 3610951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik MotionEvent.obtain(time, time, MotionEvent.ACTION_MOVE, 40, 455, 0)); 3620951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik assertEquals(10, mRecyclerView.mPrefetchRegistry.mPrefetchDx); // same as prev 3630951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik assertEquals(15, mRecyclerView.mPrefetchRegistry.mPrefetchDy); // same as prev 3640951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik } 3650951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik 3660951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik @Test 367a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik public void prefetchItemsRespectDeadline() { 368a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik mRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 3)); 369a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik 370a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik // 100x100 pixel views 371a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik RecyclerView.Adapter adapter = new RecyclerView.Adapter() { 372a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik @Override 373a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 374a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik mRecyclerView.registerTimePassingMs(5); 375a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik View view = new View(getContext()); 376a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik view.setMinimumWidth(100); 377a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik view.setMinimumHeight(100); 378945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik return new RecyclerView.ViewHolder(view) {}; 379a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik } 380a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik 381a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik @Override 382a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 383a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik mRecyclerView.registerTimePassingMs(5); 384a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik } 385a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik 386a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik @Override 387a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik public int getItemCount() { 388a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik return 100; 389a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik } 390a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik }; 391a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik mRecyclerView.setAdapter(adapter); 392a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik 393dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik layout(300, 300); 394a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik 395356880d3de117b067522ad452f4e3eed85ce444cChris Craik // offset scroll so that no prefetch-able views are directly adjacent to viewport 396356880d3de117b067522ad452f4e3eed85ce444cChris Craik mRecyclerView.scrollBy(0, 50); 397356880d3de117b067522ad452f4e3eed85ce444cChris Craik 398a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik assertTrue(mRecycler.mCachedViews.size() == 0); 399a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik assertTrue(mRecyclerView.getRecycledViewPool().getRecycledViewCount(0) == 0); 400a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik 401a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik // Should take 15 ms to inflate, bind, inflate, so give 19 to be safe 402a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik final long deadlineNs = mRecyclerView.getNanoTime() + TimeUnit.MILLISECONDS.toNanos(19); 403a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik 404a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik // Timed prefetch 405945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, 1); 40647d17a72daeb141148d13a0b0f2df146f6524a9dChris Craik mRecyclerView.mGapWorker.prefetch(deadlineNs); 407a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik 408a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik // will have enough time to inflate/bind one view, and inflate another 409a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik assertTrue(mRecycler.mCachedViews.size() == 1); 410a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik assertTrue(mRecyclerView.getRecycledViewPool().getRecycledViewCount(0) == 1); 411356880d3de117b067522ad452f4e3eed85ce444cChris Craik // Note: order/view below is an implementation detail 412356880d3de117b067522ad452f4e3eed85ce444cChris Craik CacheUtils.verifyCacheContainsPositions(mRecyclerView, 12); 413a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik 414a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik 415a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik // Unbounded prefetch this time 41647d17a72daeb141148d13a0b0f2df146f6524a9dChris Craik mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS); 417a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik 418a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik // Should finish all work 419a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik assertTrue(mRecycler.mCachedViews.size() == 3); 420a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik assertTrue(mRecyclerView.getRecycledViewPool().getRecycledViewCount(0) == 0); 421356880d3de117b067522ad452f4e3eed85ce444cChris Craik CacheUtils.verifyCacheContainsPositions(mRecyclerView, 12, 13, 14); 422945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 423945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 424945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik @Test 4251e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik public void partialPrefetchAvoidsViewRecycledCallback() { 4261e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); 4271e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 4281e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // 100x100 pixel views 4291e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik RecyclerView.Adapter adapter = new RecyclerView.Adapter() { 4301e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik @Override 4311e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 4321e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik mRecyclerView.registerTimePassingMs(5); 4331e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik View view = new View(getContext()); 4341e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik view.setMinimumWidth(100); 4351e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik view.setMinimumHeight(100); 4361e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik return new RecyclerView.ViewHolder(view) {}; 4371e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 4381e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 4391e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik @Override 4401e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 4411e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik mRecyclerView.registerTimePassingMs(5); 4421e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 4431e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 4441e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik @Override 4451e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik public int getItemCount() { 4461e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik return 100; 4471e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 4481e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 4491e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik @Override 4501e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik public void onViewRecycled(RecyclerView.ViewHolder holder) { 4511e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // verify unbound view doesn't get 4521e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik assertNotEquals(RecyclerView.NO_POSITION, holder.getAdapterPosition()); 4531e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 4541e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik }; 4551e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik mRecyclerView.setAdapter(adapter); 4561e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 4571e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik layout(100, 300); 4581e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 4591e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // offset scroll so that no prefetch-able views are directly adjacent to viewport 4601e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik mRecyclerView.scrollBy(0, 50); 4611e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 4621e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik assertTrue(mRecycler.mCachedViews.size() == 0); 4631e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik assertTrue(mRecyclerView.getRecycledViewPool().getRecycledViewCount(0) == 0); 4641e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 4651e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // Should take 10 ms to inflate + bind, so just give it 9 so it doesn't have time to bind 4661e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik final long deadlineNs = mRecyclerView.getNanoTime() + TimeUnit.MILLISECONDS.toNanos(9); 4671e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 4681e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // Timed prefetch 4691e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, 1); 4701e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik mRecyclerView.mGapWorker.prefetch(deadlineNs); 4711e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 4721e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // will have enough time to inflate but not bind one view 4731e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik assertTrue(mRecycler.mCachedViews.size() == 0); 4741e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik assertTrue(mRecyclerView.getRecycledViewPool().getRecycledViewCount(0) == 1); 4751e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik RecyclerView.ViewHolder pooledHolder = mRecyclerView.getRecycledViewPool() 4761e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik .mScrap.get(0).mScrapHeap.get(0); 4771e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik assertEquals(RecyclerView.NO_POSITION, pooledHolder.getAdapterPosition()); 4781e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 4791e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 4801e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik @Test 481945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik public void prefetchStaggeredItemsPriority() { 482945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik StaggeredGridLayoutManager sglm = 483945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); 484945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mRecyclerView.setLayoutManager(sglm); 485945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 486945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik // first view 50x100 pixels, rest are 100x100 so second column is offset 487945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mRecyclerView.setAdapter(new RecyclerView.Adapter() { 488945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik @Override 489945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 490945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik return new RecyclerView.ViewHolder(new View(getContext())) {}; 491945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 492945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 493945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik @Override 494945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 495945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik holder.itemView.setMinimumWidth(100); 496945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik holder.itemView.setMinimumHeight(position == 0 ? 50 : 100); 497945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 498945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 499945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik @Override 500945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik public int getItemCount() { 501945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik return 100; 502945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 503945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik }); 504945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 505dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik layout(200, 200); 506945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 507945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik /* Each row is 50 pixels: 508945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * ------------- * 509945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * 0 | 1 * 510945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * 2 | 1 * 511945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * 2 | 3 * 512945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik *___4___|___3___* 513945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * 4 | 5 * 514945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * 6 | 5 * 515945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * ... * 516945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik */ 517945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik assertEquals(5, mRecyclerView.getChildCount()); 518945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik assertEquals(0, sglm.getFirstChildPosition()); 519945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik assertEquals(4, sglm.getLastChildPosition()); 520945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 521945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik // prefetching down shows 5 at 0 pixels away, 6 at 50 pixels away 522356880d3de117b067522ad452f4e3eed85ce444cChris Craik CacheUtils.verifyPositionsPrefetched(mRecyclerView, 0, 10, 523945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik new Integer[] {5, 0}, new Integer[] {6, 50}); 524945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 525945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik // Prefetch upward shows nothing 526356880d3de117b067522ad452f4e3eed85ce444cChris Craik CacheUtils.verifyPositionsPrefetched(mRecyclerView, 0, -10); 527945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 528945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mRecyclerView.scrollBy(0, 100); 529945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 530945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik /* Each row is 50 pixels: 531945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * ------------- * 532945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * 0 | 1 * 533945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik *___2___|___1___* 534945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * 2 | 3 * 535945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * 4 | 3 * 536945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * 4 | 5 * 537945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik *___6___|___5___* 538945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * 6 | 7 * 539945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * 8 | 7 * 540945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * ... * 541945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik */ 542945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 543945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik assertEquals(5, mRecyclerView.getChildCount()); 544945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik assertEquals(2, sglm.getFirstChildPosition()); 545945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik assertEquals(6, sglm.getLastChildPosition()); 546945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 547945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik // prefetching down shows 7 at 0 pixels away, 8 at 50 pixels away 548356880d3de117b067522ad452f4e3eed85ce444cChris Craik CacheUtils.verifyPositionsPrefetched(mRecyclerView, 0, 10, 549945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik new Integer[] {7, 0}, new Integer[] {8, 50}); 550945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 551945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik // prefetching up shows 1 is 0 pixels away, 0 at 50 pixels away 552356880d3de117b067522ad452f4e3eed85ce444cChris Craik CacheUtils.verifyPositionsPrefetched(mRecyclerView, 0, -10, 553945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik new Integer[] {1, 0}, new Integer[] {0, 50}); 554945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 555dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik 556dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik @Test 557695d9b8629fa048795298359d4845f0747364fafChris Craik public void prefetchStaggeredPastBoundary() { 558695d9b8629fa048795298359d4845f0747364fafChris Craik StaggeredGridLayoutManager sglm = 559695d9b8629fa048795298359d4845f0747364fafChris Craik new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); 560695d9b8629fa048795298359d4845f0747364fafChris Craik mRecyclerView.setLayoutManager(sglm); 561695d9b8629fa048795298359d4845f0747364fafChris Craik mRecyclerView.setAdapter(new RecyclerView.Adapter() { 562695d9b8629fa048795298359d4845f0747364fafChris Craik @Override 563695d9b8629fa048795298359d4845f0747364fafChris Craik public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 564695d9b8629fa048795298359d4845f0747364fafChris Craik return new RecyclerView.ViewHolder(new View(getContext())) {}; 565695d9b8629fa048795298359d4845f0747364fafChris Craik } 566695d9b8629fa048795298359d4845f0747364fafChris Craik 567695d9b8629fa048795298359d4845f0747364fafChris Craik @Override 568695d9b8629fa048795298359d4845f0747364fafChris Craik public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 569695d9b8629fa048795298359d4845f0747364fafChris Craik holder.itemView.setMinimumWidth(100); 570695d9b8629fa048795298359d4845f0747364fafChris Craik holder.itemView.setMinimumHeight(position == 0 ? 100 : 200); 571695d9b8629fa048795298359d4845f0747364fafChris Craik } 572695d9b8629fa048795298359d4845f0747364fafChris Craik 573695d9b8629fa048795298359d4845f0747364fafChris Craik @Override 574695d9b8629fa048795298359d4845f0747364fafChris Craik public int getItemCount() { 575695d9b8629fa048795298359d4845f0747364fafChris Craik return 2; 576695d9b8629fa048795298359d4845f0747364fafChris Craik } 577695d9b8629fa048795298359d4845f0747364fafChris Craik }); 578695d9b8629fa048795298359d4845f0747364fafChris Craik 579695d9b8629fa048795298359d4845f0747364fafChris Craik layout(200, 100); 580695d9b8629fa048795298359d4845f0747364fafChris Craik mRecyclerView.scrollBy(0, 50); 581695d9b8629fa048795298359d4845f0747364fafChris Craik 582695d9b8629fa048795298359d4845f0747364fafChris Craik /* Each row is 50 pixels: 583695d9b8629fa048795298359d4845f0747364fafChris Craik * ------------- * 584695d9b8629fa048795298359d4845f0747364fafChris Craik *___0___|___1___* 585695d9b8629fa048795298359d4845f0747364fafChris Craik * 0 | 1 * 586695d9b8629fa048795298359d4845f0747364fafChris Craik *_______|___1___* 587695d9b8629fa048795298359d4845f0747364fafChris Craik * | 1 * 588695d9b8629fa048795298359d4845f0747364fafChris Craik */ 589695d9b8629fa048795298359d4845f0747364fafChris Craik assertEquals(2, mRecyclerView.getChildCount()); 590695d9b8629fa048795298359d4845f0747364fafChris Craik assertEquals(0, sglm.getFirstChildPosition()); 591695d9b8629fa048795298359d4845f0747364fafChris Craik assertEquals(1, sglm.getLastChildPosition()); 592695d9b8629fa048795298359d4845f0747364fafChris Craik 593695d9b8629fa048795298359d4845f0747364fafChris Craik // prefetch upward gets nothing 594695d9b8629fa048795298359d4845f0747364fafChris Craik CacheUtils.verifyPositionsPrefetched(mRecyclerView, 0, -10); 595695d9b8629fa048795298359d4845f0747364fafChris Craik 596695d9b8629fa048795298359d4845f0747364fafChris Craik // prefetch downward gets nothing (and doesn't crash...) 597695d9b8629fa048795298359d4845f0747364fafChris Craik CacheUtils.verifyPositionsPrefetched(mRecyclerView, 0, 10); 598695d9b8629fa048795298359d4845f0747364fafChris Craik } 599695d9b8629fa048795298359d4845f0747364fafChris Craik 600695d9b8629fa048795298359d4845f0747364fafChris Craik @Test 601dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik public void prefetchItemsSkipAnimations() { 602dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik LinearLayoutManager llm = new LinearLayoutManager(getContext()); 603dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik mRecyclerView.setLayoutManager(llm); 604dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik final int[] expandedPosition = new int[] {-1}; 605dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik 606dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik final RecyclerView.Adapter adapter = new RecyclerView.Adapter() { 607dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik @Override 608dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 6091e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik return new RecyclerView.ViewHolder(new View(parent.getContext())) {}; 610dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik } 611dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik 612dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik @Override 613dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 614dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik int height = expandedPosition[0] == position ? 400 : 100; 615dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik holder.itemView.setLayoutParams(new RecyclerView.LayoutParams(200, height)); 616dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik } 617dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik 618dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik @Override 619dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik public int getItemCount() { 620dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik return 10; 621dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik } 622dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik }; 623dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik 624dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik // make move duration long enough to be able to see the effects 625dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik RecyclerView.ItemAnimator itemAnimator = mRecyclerView.getItemAnimator(); 626dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik itemAnimator.setMoveDuration(10000); 627dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik mRecyclerView.setAdapter(adapter); 628dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik 629dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik layout(200, 400); 630dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik 631dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik expandedPosition[0] = 1; 632dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik // insert payload to avoid creating a new view 633dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik adapter.notifyItemChanged(1, new Object()); 634dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik 635dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik layout(200, 400); 636dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik layout(200, 400); 637dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik 638dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik assertTrue(itemAnimator.isRunning()); 639dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik assertEquals(2, llm.getChildCount()); 640dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik assertEquals(4, mRecyclerView.getChildCount()); 641dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik 642dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik // animating view should be observable as hidden, uncached... 643dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik CacheUtils.verifyCacheDoesNotContainPositions(mRecyclerView, 2); 644dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik assertNotNull("Animating view should be found, hidden", 645dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik mRecyclerView.mChildHelper.findHiddenNonRemovedView(2)); 646dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik assertTrue(GapWorker.isPrefetchPositionAttached(mRecyclerView, 2)); 647dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik 648dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik // ...but must not be removed for prefetch 649dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, 1); 650dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS); 651dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik 652dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik assertEquals("Prefetch must target one view", 1, mRecyclerView.mPrefetchRegistry.mCount); 653dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik int prefetchTarget = mRecyclerView.mPrefetchRegistry.mPrefetchArray[0]; 654dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik assertEquals("Prefetch must target view 2", 2, prefetchTarget); 655dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik 656dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik // animating view still observable as hidden, uncached 657dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik CacheUtils.verifyCacheDoesNotContainPositions(mRecyclerView, 2); 658dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik assertNotNull("Animating view should be found, hidden", 659dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik mRecyclerView.mChildHelper.findHiddenNonRemovedView(2)); 660dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik assertTrue(GapWorker.isPrefetchPositionAttached(mRecyclerView, 2)); 661dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik 662dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik assertTrue(itemAnimator.isRunning()); 663dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik assertEquals(2, llm.getChildCount()); 664dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik assertEquals(4, mRecyclerView.getChildCount()); 665dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik } 6661e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 6671e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik @Test 6681e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik public void viewHolderFindsNestedRecyclerViews() { 6691e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik LinearLayoutManager llm = new LinearLayoutManager(getContext()); 6701e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik mRecyclerView.setLayoutManager(llm); 6711e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 6721e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik RecyclerView.Adapter mockAdapter = mock(RecyclerView.Adapter.class); 6731e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik when(mockAdapter.onCreateViewHolder(any(ViewGroup.class), anyInt())) 6741e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik .thenAnswer(new Answer<RecyclerView.ViewHolder>() { 6751e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik @Override 6761e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik public RecyclerView.ViewHolder answer(InvocationOnMock invocation) 6771e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik throws Throwable { 6781e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik View view = new RecyclerView(getContext()); 6791e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik view.setLayoutParams(new RecyclerView.LayoutParams(100, 100)); 6801e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik return new RecyclerView.ViewHolder(view) {}; 6811e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 6821e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik }); 6831e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik when(mockAdapter.getItemCount()).thenReturn(100); 6841e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik mRecyclerView.setAdapter(mockAdapter); 6851e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 6861e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik layout(100, 200); 6871e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 6881e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik verify(mockAdapter, times(2)).onCreateViewHolder(any(ViewGroup.class), anyInt()); 6891e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik verify(mockAdapter, times(2)).onBindViewHolder( 6901e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik argThat(new BaseMatcher<RecyclerView.ViewHolder>() { 6911e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik @Override 6921e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik public boolean matches(Object item) { 6931e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik RecyclerView.ViewHolder holder = (RecyclerView.ViewHolder) item; 6948123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik return holder.itemView == holder.mNestedRecyclerView.get(); 6951e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 6961e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 6971e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik @Override 6981e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik public void describeTo(Description description) { } 6991e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik }), 7001e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik anyInt(), 7011e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik any(List.class)); 7021e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 7031e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 7040fab45107f043a4856aad2e1fd785b396898616eChris Craik class InnerAdapter extends RecyclerView.Adapter<InnerAdapter.ViewHolder> { 7050ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik private static final int INNER_ITEM_COUNT = 20; 706f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik int mItemsBound = 0; 707f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik 7080fab45107f043a4856aad2e1fd785b396898616eChris Craik class ViewHolder extends RecyclerView.ViewHolder { 7091e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik ViewHolder(View itemView) { 7101e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik super(itemView); 7111e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 7121e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 7131e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 7141e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik InnerAdapter() {} 7151e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 7161e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik @Override 7171e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 7180fab45107f043a4856aad2e1fd785b396898616eChris Craik mRecyclerView.registerTimePassingMs(5); 7191e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik View view = new View(parent.getContext()); 7201e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik view.setLayoutParams(new RecyclerView.LayoutParams(100, 100)); 7211e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik return new ViewHolder(view); 7221e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 7231e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 7241e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik @Override 725f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik public void onBindViewHolder(ViewHolder holder, int position) { 7260fab45107f043a4856aad2e1fd785b396898616eChris Craik mRecyclerView.registerTimePassingMs(5); 727f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik mItemsBound++; 728f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik } 7291e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 7301e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik @Override 7311e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik public int getItemCount() { 7321e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik return INNER_ITEM_COUNT; 7331e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 7341e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 7351e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 7360fab45107f043a4856aad2e1fd785b396898616eChris Craik class OuterAdapter extends RecyclerView.Adapter<OuterAdapter.ViewHolder> { 7371e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 7380ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik private boolean mReverseInner; 7390ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik 7400fab45107f043a4856aad2e1fd785b396898616eChris Craik class ViewHolder extends RecyclerView.ViewHolder { 7411e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik private final RecyclerView mRecyclerView; 7421e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik ViewHolder(RecyclerView itemView) { 7431e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik super(itemView); 7441e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik mRecyclerView = itemView; 7451e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 7461e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 7471e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 7481e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik ArrayList<InnerAdapter> mAdapters = new ArrayList<>(); 7490ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik ArrayList<Parcelable> mSavedStates = new ArrayList<>(); 7501e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik RecyclerView.RecycledViewPool mSharedPool = new RecyclerView.RecycledViewPool(); 7511e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 7521e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik OuterAdapter() { 7530ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik this(false); 7540ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik } 7550ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik 7560ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik OuterAdapter(boolean reverseInner) { 7578123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik this(reverseInner, 10); 7588123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik } 7598123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik 7608123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik OuterAdapter(boolean reverseInner, int itemCount) { 7610ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik mReverseInner = reverseInner; 7628123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik for (int i = 0; i < itemCount; i++) { 7631e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik mAdapters.add(new InnerAdapter()); 7640ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik mSavedStates.add(null); 7651e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 7661e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 7671e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 7688123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik void addItem() { 7698123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik int index = getItemCount(); 7708123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik mAdapters.add(new InnerAdapter()); 7718123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik mSavedStates.add(null); 7728123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik notifyItemInserted(index); 7738123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik } 7748123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik 7751e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik @Override 7761e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 7770fab45107f043a4856aad2e1fd785b396898616eChris Craik mRecyclerView.registerTimePassingMs(5); 7780fab45107f043a4856aad2e1fd785b396898616eChris Craik 779bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik RecyclerView rv = new RecyclerView(parent.getContext()) { 780bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik @Override 781bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik public int getWindowVisibility() { 782bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik // Pretend to be visible to avoid being filtered out 783bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik return View.VISIBLE; 784bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik } 785bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik }; 7861e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik rv.setLayoutManager(new LinearLayoutManager(parent.getContext(), 7870ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik LinearLayoutManager.HORIZONTAL, mReverseInner)); 7881e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik rv.setRecycledViewPool(mSharedPool); 7891e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik rv.setLayoutParams(new RecyclerView.LayoutParams(200, 100)); 7901e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik return new ViewHolder(rv); 7911e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 7921e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 7931e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik @Override 7941e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik public void onBindViewHolder(ViewHolder holder, int position) { 7950fab45107f043a4856aad2e1fd785b396898616eChris Craik mRecyclerView.registerTimePassingMs(5); 7960fab45107f043a4856aad2e1fd785b396898616eChris Craik 7970ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik // Tests may rely on bound holders not being shared between inner adapters, 7980ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik // since we force recycle here 7990ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik holder.mRecyclerView.swapAdapter(mAdapters.get(position), true); 8000ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik 8010ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik Parcelable savedState = mSavedStates.get(position); 8020ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik if (savedState != null) { 8030ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik holder.mRecyclerView.getLayoutManager().onRestoreInstanceState(savedState); 8040ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik mSavedStates.set(position, null); 8050ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik } 8060ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik } 8070ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik 8080ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik @Override 8090ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik public void onViewRecycled(ViewHolder holder) { 8100ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik mSavedStates.set(holder.getAdapterPosition(), 8110ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik holder.mRecyclerView.getLayoutManager().onSaveInstanceState()); 8121e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 8131e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 8141e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik @Override 8151e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik public int getItemCount() { 8168123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik return mAdapters.size(); 8171e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 8181e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 8191e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 8201e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik @Test 8210ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik public void nestedPrefetchSimple() { 8221e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik LinearLayoutManager llm = new LinearLayoutManager(getContext()); 823d6696c2abea2771acd000c2269cf9113acc6c0a9Chris Craik assertEquals(2, llm.getInitialPrefetchItemCount()); 8241e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 8251e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik mRecyclerView.setLayoutManager(llm); 8261e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik mRecyclerView.setAdapter(new OuterAdapter()); 8271e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 8281e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik layout(200, 200); 8291e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, 1); 8301e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 8311e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // prefetch 2 (default) 8321e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS); 8331e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik RecyclerView.ViewHolder holder = CacheUtils.peekAtCachedViewForPosition(mRecyclerView, 2); 8341e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik assertNotNull(holder); 8351e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik assertNotNull(holder.mNestedRecyclerView); 8368123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik RecyclerView innerView = holder.mNestedRecyclerView.get(); 8378123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik CacheUtils.verifyCacheContainsPrefetchedPositions(innerView, 0, 1); 8381e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 8391e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // prefetch 4 8408123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik ((LinearLayoutManager) innerView.getLayoutManager()) 8411e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik .setInitialPrefetchItemCount(4); 8421e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS); 8438123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik CacheUtils.verifyCacheContainsPrefetchedPositions(innerView, 0, 1, 2, 3); 8441e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 8450ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik 8460ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik @Test 8470fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu public void nestedPrefetchNotClearInnerStructureChangeFlag() { 8480fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu LinearLayoutManager llm = new LinearLayoutManager(getContext()); 8490fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu assertEquals(2, llm.getInitialPrefetchItemCount()); 8500fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu 8510fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu mRecyclerView.setLayoutManager(llm); 8520fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu mRecyclerView.setAdapter(new OuterAdapter()); 8530fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu 8540fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu layout(200, 200); 8550fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, 1); 8560fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu 8570fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu // prefetch 2 (default) 8580fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS); 8590fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu RecyclerView.ViewHolder holder = CacheUtils.peekAtCachedViewForPosition(mRecyclerView, 2); 8600fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu assertNotNull(holder); 8610fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu assertNotNull(holder.mNestedRecyclerView); 8620fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu RecyclerView innerView = holder.mNestedRecyclerView.get(); 8630fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu RecyclerView.Adapter innerAdapter = innerView.getAdapter(); 8640fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu CacheUtils.verifyCacheContainsPrefetchedPositions(innerView, 0, 1); 8650fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu // mStructureChanged is initially true before first layout pass. 8660fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu assertTrue(innerView.mState.mStructureChanged); 8670fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu assertTrue(innerView.hasPendingAdapterUpdates()); 8680fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu 8690fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu // layout position 2 and clear mStructureChanged 8700fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu mRecyclerView.scrollToPosition(2); 8710fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu layout(200, 200); 8720fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu mRecyclerView.scrollToPosition(0); 8730fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu layout(200, 200); 8740fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu assertFalse(innerView.mState.mStructureChanged); 8750fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu assertFalse(innerView.hasPendingAdapterUpdates()); 8760fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu 8770fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu // notify change on the cached innerView. 8780fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu innerAdapter.notifyDataSetChanged(); 8790fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu assertTrue(innerView.mState.mStructureChanged); 8800fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu assertTrue(innerView.hasPendingAdapterUpdates()); 8810fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu 8820fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu // prefetch again 8830fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, 1); 8840fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu ((LinearLayoutManager) innerView.getLayoutManager()) 8850fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu .setInitialPrefetchItemCount(2); 8860fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS); 8870fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu CacheUtils.verifyCacheContainsPrefetchedPositions(innerView, 0, 1); 8880fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu 8890fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu // The re-prefetch is not necessary get the same inner view but we will get same Adapter 8900fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu holder = CacheUtils.peekAtCachedViewForPosition(mRecyclerView, 2); 8910fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu innerView = holder.mNestedRecyclerView.get(); 8920fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu assertSame(innerAdapter, innerView.getAdapter()); 8930fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu // prefetch shouldn't clear the mStructureChanged flag 8940fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu assertTrue(innerView.mState.mStructureChanged); 8950fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu assertTrue(innerView.hasPendingAdapterUpdates()); 8960fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu } 8970fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu 8980fd482dee2c8c2aa61e59d6501e46864e16fcb04Dake Gu @Test 8990ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik public void nestedPrefetchReverseInner() { 9000ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); 9010ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik mRecyclerView.setAdapter(new OuterAdapter(/* reverseInner = */ true)); 9020ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik 9030ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik layout(200, 200); 9040ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, 1); 9050ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik 9060ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS); 9070ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik RecyclerView.ViewHolder holder = CacheUtils.peekAtCachedViewForPosition(mRecyclerView, 2); 9080ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik 9090ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik // anchor from right side, should see last two positions 9108123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik CacheUtils.verifyCacheContainsPrefetchedPositions(holder.mNestedRecyclerView.get(), 18, 19); 9110ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik } 9120ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik 9130ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik @Test 9140ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik public void nestedPrefetchOffset() { 9150ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); 9160ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik mRecyclerView.setAdapter(new OuterAdapter()); 9170ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik 9180ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik layout(200, 200); 9190ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik 9200ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik // Scroll top row by 5.5 items, verify positions 5, 6, 7 showing 9210ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik RecyclerView inner = (RecyclerView) mRecyclerView.getChildAt(0); 9220ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik inner.scrollBy(550, 0); 9230ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik assertEquals(5, RecyclerView.getChildViewHolderInt(inner.getChildAt(0)).mPosition); 9240ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik assertEquals(6, RecyclerView.getChildViewHolderInt(inner.getChildAt(1)).mPosition); 9250ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik assertEquals(7, RecyclerView.getChildViewHolderInt(inner.getChildAt(2)).mPosition); 9260ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik 9270ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik // scroll down 4 rows, up 3 so row 0 is adjacent but uncached 9280ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik mRecyclerView.scrollBy(0, 400); 9290ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik mRecyclerView.scrollBy(0, -300); 9300ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik 9310ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik // top row no longer present 9320ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik CacheUtils.verifyCacheDoesNotContainPositions(mRecyclerView, 0); 9330ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik 9340ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik // prefetch upward, and validate that we've gotten the top row with correct offsets 9350ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, -1); 9360ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS); 9370ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik inner = (RecyclerView) CacheUtils.peekAtCachedViewForPosition(mRecyclerView, 0).itemView; 9380ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik CacheUtils.verifyCacheContainsPrefetchedPositions(inner, 5, 6); 9390ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik 9400ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik // prefetch 4 9410ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik ((LinearLayoutManager) inner.getLayoutManager()).setInitialPrefetchItemCount(4); 9420ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS); 9430ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik CacheUtils.verifyCacheContainsPrefetchedPositions(inner, 5, 6, 7, 8); 9440ceae57ee14da826c31a66c67e70f7f17108d5caChris Craik } 945f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik 946f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik @Test 947f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik public void nestedPrefetchNotReset() { 948f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); 949f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik OuterAdapter outerAdapter = new OuterAdapter(); 950f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik mRecyclerView.setAdapter(outerAdapter); 951f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik 952f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik layout(200, 200); 953f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik 954f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, 1); 955f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik 956f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik // prefetch row 2, items 0 & 1 957f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik assertEquals(0, outerAdapter.mAdapters.get(2).mItemsBound); 958f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS); 959f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik RecyclerView.ViewHolder holder = CacheUtils.peekAtCachedViewForPosition(mRecyclerView, 2); 9608123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik RecyclerView innerRecyclerView = holder.mNestedRecyclerView.get(); 961f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik 962f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik assertNotNull(innerRecyclerView); 963f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik CacheUtils.verifyCacheContainsPrefetchedPositions(innerRecyclerView, 0, 1); 964f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik assertEquals(2, outerAdapter.mAdapters.get(2).mItemsBound); 965f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik 966f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik // new row comes on, triggers layout... 967f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik mRecyclerView.scrollBy(0, 50); 968f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik 969f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik // ... which shouldn't require new items to be bound, 970f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik // as prefetch has already done that work 971f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik assertEquals(2, outerAdapter.mAdapters.get(2).mItemsBound); 972f2f73b7cc9bed0e7204ba43796bb0112cda27cf1Chris Craik } 9730fab45107f043a4856aad2e1fd785b396898616eChris Craik 9740fab45107f043a4856aad2e1fd785b396898616eChris Craik static void validateRvChildrenValid(RecyclerView recyclerView, int childCount) { 9750fab45107f043a4856aad2e1fd785b396898616eChris Craik ChildHelper childHelper = recyclerView.mChildHelper; 9760fab45107f043a4856aad2e1fd785b396898616eChris Craik 9770fab45107f043a4856aad2e1fd785b396898616eChris Craik assertEquals(childCount, childHelper.getUnfilteredChildCount()); 9780fab45107f043a4856aad2e1fd785b396898616eChris Craik for (int i = 0; i < childHelper.getUnfilteredChildCount(); i++) { 9790fab45107f043a4856aad2e1fd785b396898616eChris Craik assertFalse(recyclerView.getChildViewHolder( 9800fab45107f043a4856aad2e1fd785b396898616eChris Craik childHelper.getUnfilteredChildAt(i)).isInvalid()); 9810fab45107f043a4856aad2e1fd785b396898616eChris Craik } 9820fab45107f043a4856aad2e1fd785b396898616eChris Craik } 9830fab45107f043a4856aad2e1fd785b396898616eChris Craik 9840fab45107f043a4856aad2e1fd785b396898616eChris Craik @Test 9850fab45107f043a4856aad2e1fd785b396898616eChris Craik public void nestedPrefetchCacheNotTouched() { 9860fab45107f043a4856aad2e1fd785b396898616eChris Craik mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); 9870fab45107f043a4856aad2e1fd785b396898616eChris Craik OuterAdapter outerAdapter = new OuterAdapter(); 9880fab45107f043a4856aad2e1fd785b396898616eChris Craik mRecyclerView.setAdapter(outerAdapter); 9890fab45107f043a4856aad2e1fd785b396898616eChris Craik 9900fab45107f043a4856aad2e1fd785b396898616eChris Craik layout(200, 200); 9910fab45107f043a4856aad2e1fd785b396898616eChris Craik mRecyclerView.scrollBy(0, 100); 9920fab45107f043a4856aad2e1fd785b396898616eChris Craik 9930fab45107f043a4856aad2e1fd785b396898616eChris Craik // item 0 is cached 9940fab45107f043a4856aad2e1fd785b396898616eChris Craik assertEquals(2, outerAdapter.mAdapters.get(0).mItemsBound); 9950fab45107f043a4856aad2e1fd785b396898616eChris Craik RecyclerView.ViewHolder holder = CacheUtils.peekAtCachedViewForPosition(mRecyclerView, 0); 9968123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik validateRvChildrenValid(holder.mNestedRecyclerView.get(), 2); 9970fab45107f043a4856aad2e1fd785b396898616eChris Craik 9980fab45107f043a4856aad2e1fd785b396898616eChris Craik // try and prefetch it 9990fab45107f043a4856aad2e1fd785b396898616eChris Craik mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, -1); 10000fab45107f043a4856aad2e1fd785b396898616eChris Craik mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS); 10010fab45107f043a4856aad2e1fd785b396898616eChris Craik 10020fab45107f043a4856aad2e1fd785b396898616eChris Craik // make sure cache's inner items aren't rebound unnecessarily 10030fab45107f043a4856aad2e1fd785b396898616eChris Craik assertEquals(2, outerAdapter.mAdapters.get(0).mItemsBound); 10048123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik validateRvChildrenValid(holder.mNestedRecyclerView.get(), 2); 10058123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik } 10068123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik 10078123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik @Test 10088123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik public void nestedRemoveAnimatingView() { 10098123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); 10108123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik OuterAdapter outerAdapter = new OuterAdapter(false, 1); 10118123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik mRecyclerView.setAdapter(outerAdapter); 10128123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik mRecyclerView.getItemAnimator().setAddDuration(TimeUnit.MILLISECONDS.toNanos(30)); 10138123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik 10148123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik layout(200, 200); 10158123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik 10168123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik // Insert 3 items - only first one in viewport, so only it animates 10178123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik for (int i = 0; i < 3; i++) { 10188123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik outerAdapter.addItem(); 10198123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik } 10208123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik layout(200, 200); // layout again to kick off animation 10218123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik 10228123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik 10238123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik // item 1 is animating, so scroll it out of viewport 10248123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik mRecyclerView.scrollBy(0, 200); 10258123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik 10268123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik // 2 items attached, 1 cached (pos 0), but item animating pos 1 not accounted for... 10278123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik assertEquals(2, mRecyclerView.mChildHelper.getUnfilteredChildCount()); 10288123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik assertEquals(1, mRecycler.mCachedViews.size()); 10298123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik CacheUtils.verifyCacheContainsPositions(mRecyclerView, 0); 10308123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik assertEquals(0, mRecyclerView.getRecycledViewPool().getRecycledViewCount(0)); 10318123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik 10328123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik // until animation ends 10338123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik mRecyclerView.getItemAnimator().endAnimations(); 10348123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik assertEquals(2, mRecyclerView.mChildHelper.getUnfilteredChildCount()); 10358123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik assertEquals(2, mRecycler.mCachedViews.size()); 10368123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik CacheUtils.verifyCacheContainsPositions(mRecyclerView, 0, 1); 10378123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik assertEquals(0, mRecyclerView.getRecycledViewPool().getRecycledViewCount(0)); 10388123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik 10398123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik for (RecyclerView.ViewHolder viewHolder : mRecycler.mCachedViews) { 10408123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik assertNotNull(viewHolder.mNestedRecyclerView); 10418123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik } 10420fab45107f043a4856aad2e1fd785b396898616eChris Craik } 10430fab45107f043a4856aad2e1fd785b396898616eChris Craik 10449ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik @Test 10459ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik public void nestedExpandCacheCorrectly() { 10469ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik final int DEFAULT_CACHE_SIZE = RecyclerView.Recycler.DEFAULT_CACHE_SIZE; 10479ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik 10489ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); 10499ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik OuterAdapter outerAdapter = new OuterAdapter(); 10509ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik mRecyclerView.setAdapter(outerAdapter); 10519ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik 10529ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik layout(200, 200); 10539ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik 10549ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, 1); 10559ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS); 10569ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik 10579ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik // after initial prefetch, view cache max expanded by number of inner items prefetched (2) 10589ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik RecyclerView.ViewHolder holder = CacheUtils.peekAtCachedViewForPosition(mRecyclerView, 2); 10599ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik RecyclerView innerView = holder.mNestedRecyclerView.get(); 10609ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik assertTrue(innerView.getLayoutManager().mPrefetchMaxObservedInInitialPrefetch); 10619ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik assertEquals(2, innerView.getLayoutManager().mPrefetchMaxCountObserved); 10629ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik assertEquals(2 + DEFAULT_CACHE_SIZE, innerView.mRecycler.mViewCacheMax); 10639ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik 10649ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik try { 10659ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik // Note: As a hack, we not only must manually dispatch attachToWindow(), but we 10669ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik // also have to be careful to call innerView.mGapWorker below. mRecyclerView.mGapWorker 10679ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik // is registered to the wrong thread, since @setup is called on a different thread 10689ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik // from @Test. Assert this, so this test can be fixed when setup == test thread. 10699ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik assertEquals(1, mRecyclerView.mGapWorker.mRecyclerViews.size()); 10709ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik assertFalse(innerView.isAttachedToWindow()); 10719ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik innerView.onAttachedToWindow(); 10729ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik 10739ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik // bring prefetch view into viewport, at which point it shouldn't have cache expanded... 10749ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik mRecyclerView.scrollBy(0, 100); 10759ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik assertFalse(innerView.getLayoutManager().mPrefetchMaxObservedInInitialPrefetch); 10769ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik assertEquals(0, innerView.getLayoutManager().mPrefetchMaxCountObserved); 10779ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik assertEquals(DEFAULT_CACHE_SIZE, innerView.mRecycler.mViewCacheMax); 10789ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik 10799ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik // until a valid horizontal prefetch caches an item, and expands view count by one 10809ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik innerView.mPrefetchRegistry.setPrefetchVector(1, 0); 10819ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik innerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS); // NB: must be innerView.mGapWorker 10829ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik assertFalse(innerView.getLayoutManager().mPrefetchMaxObservedInInitialPrefetch); 10839ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik assertEquals(1, innerView.getLayoutManager().mPrefetchMaxCountObserved); 10849ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik assertEquals(1 + DEFAULT_CACHE_SIZE, innerView.mRecycler.mViewCacheMax); 10859ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik } finally { 10869ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik if (innerView.isAttachedToWindow()) { 10879ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik innerView.onDetachedFromWindow(); 10889ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik } 10899ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik } 10909ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik } 10919ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik 10920fab45107f043a4856aad2e1fd785b396898616eChris Craik /** 10930fab45107f043a4856aad2e1fd785b396898616eChris Craik * Similar to OuterAdapter above, but uses notifyDataSetChanged() instead of set/swapAdapter 10940fab45107f043a4856aad2e1fd785b396898616eChris Craik * to update data for the inner RecyclerViews when containing ViewHolder is bound. 10950fab45107f043a4856aad2e1fd785b396898616eChris Craik */ 10960fab45107f043a4856aad2e1fd785b396898616eChris Craik class OuterNotifyAdapter extends RecyclerView.Adapter<OuterNotifyAdapter.ViewHolder> { 10970fab45107f043a4856aad2e1fd785b396898616eChris Craik private static final int OUTER_ITEM_COUNT = 10; 10980fab45107f043a4856aad2e1fd785b396898616eChris Craik 10990fab45107f043a4856aad2e1fd785b396898616eChris Craik private boolean mReverseInner; 11000fab45107f043a4856aad2e1fd785b396898616eChris Craik 11010fab45107f043a4856aad2e1fd785b396898616eChris Craik class ViewHolder extends RecyclerView.ViewHolder { 11020fab45107f043a4856aad2e1fd785b396898616eChris Craik private final RecyclerView mRecyclerView; 11030fab45107f043a4856aad2e1fd785b396898616eChris Craik private final InnerAdapter mAdapter; 11040fab45107f043a4856aad2e1fd785b396898616eChris Craik ViewHolder(RecyclerView itemView) { 11050fab45107f043a4856aad2e1fd785b396898616eChris Craik super(itemView); 11060fab45107f043a4856aad2e1fd785b396898616eChris Craik mRecyclerView = itemView; 11070fab45107f043a4856aad2e1fd785b396898616eChris Craik mAdapter = new InnerAdapter(); 11080fab45107f043a4856aad2e1fd785b396898616eChris Craik mRecyclerView.setAdapter(mAdapter); 11090fab45107f043a4856aad2e1fd785b396898616eChris Craik } 11100fab45107f043a4856aad2e1fd785b396898616eChris Craik } 11110fab45107f043a4856aad2e1fd785b396898616eChris Craik 11120fab45107f043a4856aad2e1fd785b396898616eChris Craik ArrayList<Parcelable> mSavedStates = new ArrayList<>(); 11130fab45107f043a4856aad2e1fd785b396898616eChris Craik RecyclerView.RecycledViewPool mSharedPool = new RecyclerView.RecycledViewPool(); 11140fab45107f043a4856aad2e1fd785b396898616eChris Craik 11150fab45107f043a4856aad2e1fd785b396898616eChris Craik OuterNotifyAdapter() { 11160fab45107f043a4856aad2e1fd785b396898616eChris Craik this(false); 11170fab45107f043a4856aad2e1fd785b396898616eChris Craik } 11180fab45107f043a4856aad2e1fd785b396898616eChris Craik 11190fab45107f043a4856aad2e1fd785b396898616eChris Craik OuterNotifyAdapter(boolean reverseInner) { 11200fab45107f043a4856aad2e1fd785b396898616eChris Craik mReverseInner = reverseInner; 11210fab45107f043a4856aad2e1fd785b396898616eChris Craik for (int i = 0; i <= OUTER_ITEM_COUNT; i++) { 11220fab45107f043a4856aad2e1fd785b396898616eChris Craik mSavedStates.add(null); 11230fab45107f043a4856aad2e1fd785b396898616eChris Craik } 11240fab45107f043a4856aad2e1fd785b396898616eChris Craik } 11250fab45107f043a4856aad2e1fd785b396898616eChris Craik 11260fab45107f043a4856aad2e1fd785b396898616eChris Craik @Override 11270fab45107f043a4856aad2e1fd785b396898616eChris Craik public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 11280fab45107f043a4856aad2e1fd785b396898616eChris Craik mRecyclerView.registerTimePassingMs(5); 11290fab45107f043a4856aad2e1fd785b396898616eChris Craik RecyclerView rv = new RecyclerView(parent.getContext()); 11300fab45107f043a4856aad2e1fd785b396898616eChris Craik rv.setLayoutManager(new LinearLayoutManager(parent.getContext(), 11310fab45107f043a4856aad2e1fd785b396898616eChris Craik LinearLayoutManager.HORIZONTAL, mReverseInner)); 11320fab45107f043a4856aad2e1fd785b396898616eChris Craik rv.setRecycledViewPool(mSharedPool); 11330fab45107f043a4856aad2e1fd785b396898616eChris Craik rv.setLayoutParams(new RecyclerView.LayoutParams(200, 100)); 11340fab45107f043a4856aad2e1fd785b396898616eChris Craik return new ViewHolder(rv); 11350fab45107f043a4856aad2e1fd785b396898616eChris Craik } 11360fab45107f043a4856aad2e1fd785b396898616eChris Craik 11370fab45107f043a4856aad2e1fd785b396898616eChris Craik @Override 11380fab45107f043a4856aad2e1fd785b396898616eChris Craik public void onBindViewHolder(ViewHolder holder, int position) { 11390fab45107f043a4856aad2e1fd785b396898616eChris Craik mRecyclerView.registerTimePassingMs(5); 11400fab45107f043a4856aad2e1fd785b396898616eChris Craik // if we had actual data to put into our adapter, this is where we'd do it... 11410fab45107f043a4856aad2e1fd785b396898616eChris Craik 11420fab45107f043a4856aad2e1fd785b396898616eChris Craik // ... then notify the adapter that it has new content: 11430fab45107f043a4856aad2e1fd785b396898616eChris Craik holder.mAdapter.notifyDataSetChanged(); 11440fab45107f043a4856aad2e1fd785b396898616eChris Craik 11450fab45107f043a4856aad2e1fd785b396898616eChris Craik Parcelable savedState = mSavedStates.get(position); 11460fab45107f043a4856aad2e1fd785b396898616eChris Craik if (savedState != null) { 11470fab45107f043a4856aad2e1fd785b396898616eChris Craik holder.mRecyclerView.getLayoutManager().onRestoreInstanceState(savedState); 11480fab45107f043a4856aad2e1fd785b396898616eChris Craik mSavedStates.set(position, null); 11490fab45107f043a4856aad2e1fd785b396898616eChris Craik } 11500fab45107f043a4856aad2e1fd785b396898616eChris Craik } 11510fab45107f043a4856aad2e1fd785b396898616eChris Craik 11520fab45107f043a4856aad2e1fd785b396898616eChris Craik @Override 11530fab45107f043a4856aad2e1fd785b396898616eChris Craik public void onViewRecycled(ViewHolder holder) { 1154570ab881350c836743247140a0953f87acc21c8dChris Craik if (holder.getAdapterPosition() >= 0) { 1155570ab881350c836743247140a0953f87acc21c8dChris Craik mSavedStates.set(holder.getAdapterPosition(), 1156570ab881350c836743247140a0953f87acc21c8dChris Craik holder.mRecyclerView.getLayoutManager().onSaveInstanceState()); 1157570ab881350c836743247140a0953f87acc21c8dChris Craik } 11580fab45107f043a4856aad2e1fd785b396898616eChris Craik } 11590fab45107f043a4856aad2e1fd785b396898616eChris Craik 11600fab45107f043a4856aad2e1fd785b396898616eChris Craik @Override 11610fab45107f043a4856aad2e1fd785b396898616eChris Craik public int getItemCount() { 11620fab45107f043a4856aad2e1fd785b396898616eChris Craik return OUTER_ITEM_COUNT; 11630fab45107f043a4856aad2e1fd785b396898616eChris Craik } 11640fab45107f043a4856aad2e1fd785b396898616eChris Craik } 11650fab45107f043a4856aad2e1fd785b396898616eChris Craik 11660fab45107f043a4856aad2e1fd785b396898616eChris Craik @Test 11670fab45107f043a4856aad2e1fd785b396898616eChris Craik public void nestedPrefetchDiscardStaleChildren() { 11680fab45107f043a4856aad2e1fd785b396898616eChris Craik LinearLayoutManager llm = new LinearLayoutManager(getContext()); 1169d6696c2abea2771acd000c2269cf9113acc6c0a9Chris Craik assertEquals(2, llm.getInitialPrefetchItemCount()); 11700fab45107f043a4856aad2e1fd785b396898616eChris Craik 11710fab45107f043a4856aad2e1fd785b396898616eChris Craik mRecyclerView.setLayoutManager(llm); 11720fab45107f043a4856aad2e1fd785b396898616eChris Craik OuterNotifyAdapter outerAdapter = new OuterNotifyAdapter(); 11730fab45107f043a4856aad2e1fd785b396898616eChris Craik mRecyclerView.setAdapter(outerAdapter); 11740fab45107f043a4856aad2e1fd785b396898616eChris Craik 11750fab45107f043a4856aad2e1fd785b396898616eChris Craik // zero cache, so item we prefetch can't already be ready 11760fab45107f043a4856aad2e1fd785b396898616eChris Craik mRecyclerView.setItemViewCacheSize(0); 11770fab45107f043a4856aad2e1fd785b396898616eChris Craik 11780fab45107f043a4856aad2e1fd785b396898616eChris Craik // layout 3 items, then resize to 2... 11790fab45107f043a4856aad2e1fd785b396898616eChris Craik layout(200, 300); 11800fab45107f043a4856aad2e1fd785b396898616eChris Craik layout(200, 200); 11810fab45107f043a4856aad2e1fd785b396898616eChris Craik 11820fab45107f043a4856aad2e1fd785b396898616eChris Craik // so 1 item is evicted into the RecycledViewPool (bypassing cache) 11830fab45107f043a4856aad2e1fd785b396898616eChris Craik assertEquals(1, mRecycler.mRecyclerPool.getRecycledViewCount(0)); 11840fab45107f043a4856aad2e1fd785b396898616eChris Craik assertEquals(0, mRecycler.mCachedViews.size()); 11850fab45107f043a4856aad2e1fd785b396898616eChris Craik 11860fab45107f043a4856aad2e1fd785b396898616eChris Craik // This is a simple imitation of other behavior (namely, varied types in the outer adapter) 11870fab45107f043a4856aad2e1fd785b396898616eChris Craik // that results in the same initial state to test: items in the pool with attached children 11880fab45107f043a4856aad2e1fd785b396898616eChris Craik for (RecyclerView.ViewHolder holder : mRecycler.mRecyclerPool.mScrap.get(0).mScrapHeap) { 11890fab45107f043a4856aad2e1fd785b396898616eChris Craik // verify that children are attached and valid, since the RVs haven't been rebound 11900fab45107f043a4856aad2e1fd785b396898616eChris Craik assertNotNull(holder.mNestedRecyclerView); 11918123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik assertFalse(holder.mNestedRecyclerView.get().mDataSetHasChangedAfterLayout); 11928123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik validateRvChildrenValid(holder.mNestedRecyclerView.get(), 2); 11930fab45107f043a4856aad2e1fd785b396898616eChris Craik } 11940fab45107f043a4856aad2e1fd785b396898616eChris Craik 11950fab45107f043a4856aad2e1fd785b396898616eChris Craik // prefetch the outer item bind, but without enough time to do any inner binds 11960fab45107f043a4856aad2e1fd785b396898616eChris Craik final long deadlineNs = mRecyclerView.getNanoTime() + TimeUnit.MILLISECONDS.toNanos(9); 11970fab45107f043a4856aad2e1fd785b396898616eChris Craik mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, 1); 11980fab45107f043a4856aad2e1fd785b396898616eChris Craik mRecyclerView.mGapWorker.prefetch(deadlineNs); 11990fab45107f043a4856aad2e1fd785b396898616eChris Craik 12000fab45107f043a4856aad2e1fd785b396898616eChris Craik // 2 is prefetched without children 12010fab45107f043a4856aad2e1fd785b396898616eChris Craik CacheUtils.verifyCacheContainsPrefetchedPositions(mRecyclerView, 2); 12020fab45107f043a4856aad2e1fd785b396898616eChris Craik RecyclerView.ViewHolder holder = CacheUtils.peekAtCachedViewForPosition(mRecyclerView, 2); 12030fab45107f043a4856aad2e1fd785b396898616eChris Craik assertNotNull(holder); 12040fab45107f043a4856aad2e1fd785b396898616eChris Craik assertNotNull(holder.mNestedRecyclerView); 12058123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik assertEquals(0, holder.mNestedRecyclerView.get().mChildHelper.getUnfilteredChildCount()); 12068123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik assertEquals(0, holder.mNestedRecyclerView.get().mRecycler.mCachedViews.size()); 12070fab45107f043a4856aad2e1fd785b396898616eChris Craik 12080fab45107f043a4856aad2e1fd785b396898616eChris Craik // but if we give it more time to bind items, it'll now acquire its inner items 12090fab45107f043a4856aad2e1fd785b396898616eChris Craik mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS); 12108123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik CacheUtils.verifyCacheContainsPrefetchedPositions(holder.mNestedRecyclerView.get(), 0, 1); 12110fab45107f043a4856aad2e1fd785b396898616eChris Craik } 1212570ab881350c836743247140a0953f87acc21c8dChris Craik 1213570ab881350c836743247140a0953f87acc21c8dChris Craik 1214570ab881350c836743247140a0953f87acc21c8dChris Craik @Test 1215570ab881350c836743247140a0953f87acc21c8dChris Craik public void nestedPrefetchDiscardStalePrefetch() { 1216570ab881350c836743247140a0953f87acc21c8dChris Craik mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); 1217570ab881350c836743247140a0953f87acc21c8dChris Craik OuterNotifyAdapter outerAdapter = new OuterNotifyAdapter(); 1218570ab881350c836743247140a0953f87acc21c8dChris Craik mRecyclerView.setAdapter(outerAdapter); 1219570ab881350c836743247140a0953f87acc21c8dChris Craik 1220570ab881350c836743247140a0953f87acc21c8dChris Craik // zero cache, so item we prefetch can't already be ready 1221570ab881350c836743247140a0953f87acc21c8dChris Craik mRecyclerView.setItemViewCacheSize(0); 1222570ab881350c836743247140a0953f87acc21c8dChris Craik 1223570ab881350c836743247140a0953f87acc21c8dChris Craik // layout as 2x2, starting on row index 2, with empty cache 1224570ab881350c836743247140a0953f87acc21c8dChris Craik layout(200, 200); 1225570ab881350c836743247140a0953f87acc21c8dChris Craik mRecyclerView.scrollBy(0, 200); 1226570ab881350c836743247140a0953f87acc21c8dChris Craik 1227570ab881350c836743247140a0953f87acc21c8dChris Craik // no views cached, or previously used (so we can trust number in mItemsBound) 1228570ab881350c836743247140a0953f87acc21c8dChris Craik mRecycler.mRecyclerPool.clear(); 1229570ab881350c836743247140a0953f87acc21c8dChris Craik assertEquals(0, mRecycler.mRecyclerPool.getRecycledViewCount(0)); 1230570ab881350c836743247140a0953f87acc21c8dChris Craik assertEquals(0, mRecycler.mCachedViews.size()); 1231570ab881350c836743247140a0953f87acc21c8dChris Craik 1232570ab881350c836743247140a0953f87acc21c8dChris Craik // prefetch the outer item and its inner children 1233570ab881350c836743247140a0953f87acc21c8dChris Craik mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, 1); 1234570ab881350c836743247140a0953f87acc21c8dChris Craik mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS); 1235570ab881350c836743247140a0953f87acc21c8dChris Craik 1236570ab881350c836743247140a0953f87acc21c8dChris Craik // 4 is prefetched with 2 inner children, first two binds 1237570ab881350c836743247140a0953f87acc21c8dChris Craik CacheUtils.verifyCacheContainsPrefetchedPositions(mRecyclerView, 4); 1238570ab881350c836743247140a0953f87acc21c8dChris Craik RecyclerView.ViewHolder holder = CacheUtils.peekAtCachedViewForPosition(mRecyclerView, 4); 1239570ab881350c836743247140a0953f87acc21c8dChris Craik assertNotNull(holder); 1240570ab881350c836743247140a0953f87acc21c8dChris Craik assertNotNull(holder.mNestedRecyclerView); 1241570ab881350c836743247140a0953f87acc21c8dChris Craik RecyclerView innerRecyclerView = holder.mNestedRecyclerView.get(); 1242570ab881350c836743247140a0953f87acc21c8dChris Craik assertEquals(0, innerRecyclerView.mChildHelper.getUnfilteredChildCount()); 1243570ab881350c836743247140a0953f87acc21c8dChris Craik assertEquals(2, innerRecyclerView.mRecycler.mCachedViews.size()); 1244570ab881350c836743247140a0953f87acc21c8dChris Craik assertEquals(2, ((InnerAdapter) innerRecyclerView.getAdapter()).mItemsBound); 1245570ab881350c836743247140a0953f87acc21c8dChris Craik 1246570ab881350c836743247140a0953f87acc21c8dChris Craik // notify data set changed, so any previously prefetched items invalid, and re-prefetch 1247570ab881350c836743247140a0953f87acc21c8dChris Craik innerRecyclerView.getAdapter().notifyDataSetChanged(); 1248570ab881350c836743247140a0953f87acc21c8dChris Craik mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, 1); 1249570ab881350c836743247140a0953f87acc21c8dChris Craik mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS); 1250570ab881350c836743247140a0953f87acc21c8dChris Craik 1251570ab881350c836743247140a0953f87acc21c8dChris Craik // 4 is prefetched again... 1252570ab881350c836743247140a0953f87acc21c8dChris Craik CacheUtils.verifyCacheContainsPrefetchedPositions(mRecyclerView, 4); 1253570ab881350c836743247140a0953f87acc21c8dChris Craik 1254570ab881350c836743247140a0953f87acc21c8dChris Craik // reusing the same instance with 2 inner children... 1255570ab881350c836743247140a0953f87acc21c8dChris Craik assertSame(holder, CacheUtils.peekAtCachedViewForPosition(mRecyclerView, 4)); 1256570ab881350c836743247140a0953f87acc21c8dChris Craik assertSame(innerRecyclerView, holder.mNestedRecyclerView.get()); 1257570ab881350c836743247140a0953f87acc21c8dChris Craik assertEquals(0, innerRecyclerView.mChildHelper.getUnfilteredChildCount()); 1258570ab881350c836743247140a0953f87acc21c8dChris Craik assertEquals(2, innerRecyclerView.mRecycler.mCachedViews.size()); 1259570ab881350c836743247140a0953f87acc21c8dChris Craik 1260570ab881350c836743247140a0953f87acc21c8dChris Craik // ... but there should be two new binds 1261570ab881350c836743247140a0953f87acc21c8dChris Craik assertEquals(4, ((InnerAdapter) innerRecyclerView.getAdapter()).mItemsBound); 1262570ab881350c836743247140a0953f87acc21c8dChris Craik } 1263945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik} 1264