BaseRecyclerViewInstrumentationTest.java revision e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950
1d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase/* 2d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * Copyright (C) 2014 The Android Open Source Project 3d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * 4d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * Licensed under the Apache License, Version 2.0 (the "License"); 5d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * you may not use this file except in compliance with the License. 6d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * You may obtain a copy of the License at 7d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * 8d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * http://www.apache.org/licenses/LICENSE-2.0 9d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * 10d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * Unless required by applicable law or agreed to in writing, software 11d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * distributed under the License is distributed on an "AS IS" BASIS, 12d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * See the License for the specific language governing permissions and 14d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * limitations under the License. 15d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase */ 16d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 17d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haasepackage android.support.v7.widget; 18d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 192bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyarimport android.app.Instrumentation; 20c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyarimport android.graphics.Rect; 218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyarimport android.os.Looper; 22e2437126f0225135891544315ff72e54c7de0f59Yigit Boyarimport android.support.v4.view.ViewCompat; 23d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.test.ActivityInstrumentationTestCase2; 24d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.util.Log; 25e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.view.LayoutInflater; 26d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.view.View; 27d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.view.ViewGroup; 28310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyarimport android.widget.FrameLayout; 29d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.widget.TextView; 30d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 312bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyarimport java.lang.reflect.InvocationTargetException; 322bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyarimport java.lang.reflect.Method; 33d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.ArrayList; 3490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyarimport java.util.HashSet; 35d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.List; 3690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyarimport java.util.Set; 37d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.CountDownLatch; 38d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.TimeUnit; 3911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.concurrent.atomic.AtomicInteger; 40da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyarimport java.util.concurrent.locks.Lock; 41da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyarimport java.util.concurrent.locks.ReentrantLock; 42e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.support.v7.recyclerview.test.R; 43d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 44d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseabstract public class BaseRecyclerViewInstrumentationTest extends 45d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase ActivityInstrumentationTestCase2<TestActivity> { 46d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 47d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase private static final String TAG = "RecyclerViewTest"; 48d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 49b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar private boolean mDebug; 50b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 51b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar protected RecyclerView mRecyclerView; 52d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar protected AdapterHelper mAdapterHelper; 548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 557e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar Throwable mainThreadException; 567e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 57d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public BaseRecyclerViewInstrumentationTest() { 58b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar this(false); 59b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 60b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 61b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public BaseRecyclerViewInstrumentationTest(boolean debug) { 628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super("android.support.v7.recyclerview", TestActivity.class); 63b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mDebug = debug; 64b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 65b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 667e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar void checkForMainThreadException() throws Throwable { 677e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar if (mainThreadException != null) { 687e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar throw mainThreadException; 697e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 707e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 717e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 72e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar void setHasTransientState(final View view, final boolean value) { 73e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar try { 74e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar runTestOnUiThread(new Runnable() { 75e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar @Override 76e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar public void run() { 77e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar ViewCompat.setHasTransientState(view, value); 78e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 79e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar }); 80e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } catch (Throwable throwable) { 81e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar Log.e(TAG, "", throwable); 82e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 83e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 84e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar 852bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyar protected void enableAccessibility() 862bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyar throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { 872bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyar Method getUIAutomation = Instrumentation.class.getMethod("getUiAutomation"); 882bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyar getUIAutomation.invoke(getInstrumentation()); 892bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyar } 902bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyar 910b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar void setAdapter(final RecyclerView.Adapter adapter) throws Throwable { 920b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar runTestOnUiThread(new Runnable() { 930b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 940b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void run() { 950b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar mRecyclerView.setAdapter(adapter); 960b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 970b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar }); 980b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 990b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar 100e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar protected WrappedRecyclerView inflateWrappedRV() { 101e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return (WrappedRecyclerView) 102e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar LayoutInflater.from(getActivity()).inflate(R.layout.wrapped_test_rv, 103e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar getRecyclerViewContainer(), false); 104e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 105e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1060b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar void swapAdapter(final RecyclerView.Adapter adapter, 1070b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar final boolean removeAndRecycleExistingViews) throws Throwable { 1080b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar runTestOnUiThread(new Runnable() { 1090b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 1100b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void run() { 111824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar try { 112824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar mRecyclerView.swapAdapter(adapter, removeAndRecycleExistingViews); 113824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar } catch (Throwable t) { 114824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar postExceptionToInstrumentation(t); 115824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar } 1160b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 1170b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar }); 118824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar checkForMainThreadException(); 1190b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 1200b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar 1217e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar void postExceptionToInstrumentation(Throwable t) { 1226e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (mainThreadException != null) { 1236e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar Log.e(TAG, "receiving another main thread exception. dropping.", t); 1246e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } else { 12590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar Log.e(TAG, "captured exception on main thread", t); 1266e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mainThreadException = t; 1276e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 1286e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 1297e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar if (mRecyclerView != null && mRecyclerView 1307e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar .getLayoutManager() instanceof TestLayoutManager) { 1317e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar TestLayoutManager lm = (TestLayoutManager) mRecyclerView.getLayoutManager(); 1327e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar // finish all layouts so that we get the correct exception 1337e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar while (lm.layoutLatch.getCount() > 0) { 1347e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar lm.layoutLatch.countDown(); 1357e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 1367e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 1377e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 1387e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 1398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 1408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar protected void tearDown() throws Exception { 1418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (mRecyclerView != null) { 1428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar try { 1438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar removeRecyclerView(); 1448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } catch (Throwable throwable) { 1458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar throwable.printStackTrace(); 1468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 1478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 1488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar getInstrumentation().waitForIdleSync(); 149ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar super.tearDown(); 150ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar 1516e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar try { 1526e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar checkForMainThreadException(); 1536e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } catch (Exception e) { 1546e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar throw e; 1556e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } catch (Throwable throwable) { 156ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar throw new Exception(throwable); 1576e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 1588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 1598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 160c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar public Rect getDecoratedRecyclerViewBounds() { 161c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar return new Rect( 162c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mRecyclerView.getPaddingLeft(), 163c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mRecyclerView.getPaddingTop(), 164c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mRecyclerView.getPaddingLeft() + mRecyclerView.getWidth(), 165c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mRecyclerView.getPaddingTop() + mRecyclerView.getHeight() 166c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar ); 167c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 168c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 169b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void removeRecyclerView() throws Throwable { 170d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mRecyclerView == null) { 171d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return; 172d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 17390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar if (!isMainThread()) { 17490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar getInstrumentation().waitForIdleSync(); 17590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 176b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 177b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 178b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 179abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar try { 180abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar final RecyclerView.Adapter adapter = mRecyclerView.getAdapter(); 181abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar if (adapter instanceof AttachDetachCountingAdapter) { 182abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar ((AttachDetachCountingAdapter) adapter).getCounter() 183abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar .validateRemaining(mRecyclerView); 184abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar } 185abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar getActivity().mContainer.removeAllViews(); 186abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar } catch (Throwable t) { 187abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar postExceptionToInstrumentation(t); 18890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 189b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 190b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 19190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mRecyclerView = null; 192b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 193b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 194504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar void waitForAnimations(int seconds) throws InterruptedException { 195504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final CountDownLatch latch = new CountDownLatch(2); 196504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar boolean running = mRecyclerView.mItemAnimator 197504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar .isRunning(new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() { 198504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 199504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void onAnimationsFinished() { 200504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar latch.countDown(); 201504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 202504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar }); 203504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (running) { 204504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar latch.await(seconds, TimeUnit.SECONDS); 205504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 206504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 207504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 208542f1260934df280985294eaef1ec8469863281fYigit Boyar public boolean requestFocus(final View view) { 209542f1260934df280985294eaef1ec8469863281fYigit Boyar final boolean[] result = new boolean[1]; 210542f1260934df280985294eaef1ec8469863281fYigit Boyar getActivity().runOnUiThread(new Runnable() { 211542f1260934df280985294eaef1ec8469863281fYigit Boyar @Override 212542f1260934df280985294eaef1ec8469863281fYigit Boyar public void run() { 213542f1260934df280985294eaef1ec8469863281fYigit Boyar result[0] = view.requestFocus(); 214542f1260934df280985294eaef1ec8469863281fYigit Boyar } 215542f1260934df280985294eaef1ec8469863281fYigit Boyar }); 216542f1260934df280985294eaef1ec8469863281fYigit Boyar return result[0]; 217542f1260934df280985294eaef1ec8469863281fYigit Boyar } 218542f1260934df280985294eaef1ec8469863281fYigit Boyar 219b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void setRecyclerView(final RecyclerView recyclerView) throws Throwable { 2200b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar setRecyclerView(recyclerView, true); 2210b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 2220b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void setRecyclerView(final RecyclerView recyclerView, boolean assignDummyPool) 2230b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar throws Throwable { 224e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar setRecyclerView(recyclerView, true, true); 225e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar } 226e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar public void setRecyclerView(final RecyclerView recyclerView, boolean assignDummyPool, 227e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar boolean addPositionCheckItemAnimator) 228e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar throws Throwable { 229b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mRecyclerView = recyclerView; 2300b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar if (assignDummyPool) { 2310b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool() { 2320b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 2330b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public RecyclerView.ViewHolder getRecycledView(int viewType) { 2340b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar RecyclerView.ViewHolder viewHolder = super.getRecycledView(viewType); 2350b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar if (viewHolder == null) { 2360b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar return null; 2370b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 2380b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.addFlags(RecyclerView.ViewHolder.FLAG_BOUND); 2390b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.mPosition = 200; 2400b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.mOldPosition = 300; 2410b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.mPreLayoutPosition = 500; 2420b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar return viewHolder; 2438da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar } 2448da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar 2450b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 2460b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void putRecycledView(RecyclerView.ViewHolder scrap) { 247619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar assertNull(scrap.mOwnerRecyclerView); 2480b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar super.putRecycledView(scrap); 2490b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 2500b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar }; 2510b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar mRecyclerView.setRecycledViewPool(pool); 2520b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 253e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar if (addPositionCheckItemAnimator) { 254e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() { 255e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar @Override 256e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar public void getItemOffsets(Rect outRect, View view, RecyclerView parent, 257e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar RecyclerView.State state) { 258e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar RecyclerView.ViewHolder vh = parent.getChildViewHolder(view); 259e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar if (!vh.isRemoved()) { 260e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar assertNotSame("If getItemOffsets is called, child should have a valid" 261619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar + " adapter position unless it is removed : " + vh, 262e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar vh.getAdapterPosition(), RecyclerView.NO_POSITION); 263e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar } 264e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar } 265e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar }); 266e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar } 2678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mAdapterHelper = recyclerView.mAdapterHelper; 268b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 269b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 270b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 271b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar getActivity().mContainer.addView(recyclerView); 272b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 273b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 274b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 275b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 276310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar protected FrameLayout getRecyclerViewContainer() { 277310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar return getActivity().mContainer; 278310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 279310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 280e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar public void requestLayoutOnUIThread(final View view) { 281e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar try { 282e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar runTestOnUiThread(new Runnable() { 283e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar @Override 284e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar public void run() { 285e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar view.requestLayout(); 286e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 287e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar }); 288e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } catch (Throwable throwable) { 289e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar Log.e(TAG, "", throwable); 290e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 291b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 292b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 293e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar public void scrollBy(final int dt) { 294e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar try { 295e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar runTestOnUiThread(new Runnable() { 296e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar @Override 297e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar public void run() { 298e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar if (mRecyclerView.getLayoutManager().canScrollHorizontally()) { 299e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar mRecyclerView.scrollBy(dt, 0); 300e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } else { 301e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar mRecyclerView.scrollBy(0, dt); 302e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 303b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 304e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 305e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar }); 306e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } catch (Throwable throwable) { 307e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar Log.e(TAG, "", throwable); 308e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 309d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 310d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 3118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void scrollToPosition(final int position) throws Throwable { 3128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 3138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 3158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.getLayoutManager().scrollToPosition(position); 3168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 3188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void smoothScrollToPosition(final int position) 3218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throws Throwable { 322619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar if (mDebug) { 323619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar Log.d(TAG, "SMOOTH scrolling to " + position); 324619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar } 3258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 3268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 3288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.smoothScrollToPosition(position); 3298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 331de5f54783555fa2e778c4ed6760472a002b2589bYigit Boyar getInstrumentation().waitForIdleSync(); 332de5f54783555fa2e778c4ed6760472a002b2589bYigit Boyar Thread.sleep(200); //give scroller some time so start 3338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar while (mRecyclerView.getLayoutManager().isSmoothScrolling() || 3348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) { 3358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (mDebug) { 3368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "SMOOTH scrolling step"); 3378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Thread.sleep(200); 3398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 340619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar if (mDebug) { 341619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar Log.d(TAG, "SMOOTH scrolling done"); 342619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar } 343504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar getInstrumentation().waitForIdleSync(); 3448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 346d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase class TestViewHolder extends RecyclerView.ViewHolder { 347d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 348115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar Item mBoundItem; 349d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 350d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public TestViewHolder(View itemView) { 351d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase super(itemView); 352310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar itemView.setFocusable(true); 353d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 354504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 355504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 356504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public String toString() { 357115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar return super.toString() + " item:" + mBoundItem; 358504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 359d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 360da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar class DumbLayoutManager extends TestLayoutManager { 361da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar ReentrantLock mLayoutLock = new ReentrantLock(); 362da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar public void blockLayout() { 363da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar mLayoutLock.lock(); 364da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar } 365d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 366da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar public void unblockLayout() { 367da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar mLayoutLock.unlock(); 368da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar } 369da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar @Override 370da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 371da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar mLayoutLock.lock(); 372da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar detachAndScrapAttachedViews(recycler); 373da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar layoutRange(recycler, 0, state.getItemCount()); 374da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar if (layoutLatch != null) { 375da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar layoutLatch.countDown(); 376da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar } 377da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar mLayoutLock.unlock(); 378da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar } 379da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar } 380e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public class TestLayoutManager extends RecyclerView.LayoutManager { 381e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar protected CountDownLatch layoutLatch; 382d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 383d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public void expectLayouts(int count) { 384d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase layoutLatch = new CountDownLatch(count); 385d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 386d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 387b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void waitForLayout(long timeout, TimeUnit timeUnit) throws Throwable { 388b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout * (mDebug ? 100 : 1), timeUnit); 389d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase assertEquals("all expected layouts should be executed at the expected time", 390d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 0, layoutLatch.getCount()); 391e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar getInstrumentation().waitForIdleSync(); 392d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 393d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 394b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void assertLayoutCount(int count, String msg, long timeout) throws Throwable { 395b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout, TimeUnit.SECONDS); 396b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertEquals(msg, count, layoutLatch.getCount()); 397b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 398b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 399b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void assertNoLayout(String msg, long timeout) throws Throwable { 400b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout, TimeUnit.SECONDS); 401b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertFalse(msg, layoutLatch.getCount() == 0); 402b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 403b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 404b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void waitForLayout(long timeout) throws Throwable { 4058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar waitForLayout(timeout * (mDebug ? 10000 : 1), TimeUnit.SECONDS); 406b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 407b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 408d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 409d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public RecyclerView.LayoutParams generateDefaultLayoutParams() { 410d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 411d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase ViewGroup.LayoutParams.WRAP_CONTENT); 412d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 413d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 414d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase void assertVisibleItemPositions() { 415d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase int i = getChildCount(); 416d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase TestAdapter testAdapter = (TestAdapter) mRecyclerView.getAdapter(); 417d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase while (i-- > 0) { 418d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase View view = getChildAt(i); 419d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams(); 420115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar Item item = ((TestViewHolder) lp.mViewHolder).mBoundItem; 421b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 422d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "testing item " + i); 423d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 4248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (!lp.isItemRemoved()) { 4258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar RecyclerView.ViewHolder vh = mRecyclerView.getChildViewHolder(view); 4268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertSame("item position in LP should match adapter value :" + vh, 4278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar testAdapter.mItems.get(vh.mPosition), item); 4288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 429d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 430d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 431d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 432b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView.LayoutParams getLp(View v) { 433b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar return (RecyclerView.LayoutParams) v.getLayoutParams(); 434b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 435b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 436e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar protected void layoutRange(RecyclerView.Recycler recycler, int start, int end) { 437fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar assertScrap(recycler); 438b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 439d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "will layout items from " + start + " to " + end); 440d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 4415ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar int diff = end > start ? 1 : -1; 442504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar int top = 0; 4435ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar for (int i = start; i != end; i+=diff) { 444b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 445d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "laying out item " + i); 446d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 447d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase View view = recycler.getViewForPosition(i); 448b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertNotNull("view should not be null for valid position. " 449b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar + "got null view at position " + i, view); 450c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar if (!mRecyclerView.mState.isPreLayout()) { 451c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view 452c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar .getLayoutParams(); 453c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar assertFalse("In post layout, getViewForPosition should never return a view " 454c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar + "that is removed", layoutParams != null 455c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar && layoutParams.isItemRemoved()); 456c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar 457b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 4588da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar assertEquals("getViewForPosition should return correct position", 4598da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar i, getPosition(view)); 460c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar addView(view); 461b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 462d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase measureChildWithMargins(view, 0, 0); 463e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL) { 464e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar layoutDecorated(view, getWidth() - getDecoratedMeasuredWidth(view), top, 465e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar getWidth(), top + getDecoratedMeasuredHeight(view)); 466e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 467e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar layoutDecorated(view, 0, top, getDecoratedMeasuredWidth(view) 468e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar , top + getDecoratedMeasuredHeight(view)); 469e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 470e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 471504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar top += view.getMeasuredHeight(); 472d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 473d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 474fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar 475fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar private void assertScrap(RecyclerView.Recycler recycler) { 476e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar if (mRecyclerView.getAdapter() != null && 477e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar !mRecyclerView.getAdapter().hasStableIds()) { 478e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar for (RecyclerView.ViewHolder viewHolder : recycler.getScrapList()) { 479e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar assertFalse("Invalid scrap should be no kept", viewHolder.isInvalid()); 480e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 481fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar } 482fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar } 483bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar 484bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar @Override 485bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar public boolean canScrollHorizontally() { 486bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar return true; 487bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar } 488bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar 489bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar @Override 490bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar public boolean canScrollVertically() { 491bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar return true; 492bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar } 493bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar 494bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar @Override 495bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, 496bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar RecyclerView.State state) { 497bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar return dx; 498bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar } 499bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar 500bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar @Override 501bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, 502bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar RecyclerView.State state) { 503bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar return dy; 504bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar } 505d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 506d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 507d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase static class Item { 50811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final static AtomicInteger idCounter = new AtomicInteger(0); 50911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final public int mId = idCounter.incrementAndGet(); 510d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 5112d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int mAdapterIndex; 512d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 5132d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final String mText; 514d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 5152d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar Item(int adapterIndex, String text) { 5162d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mAdapterIndex = adapterIndex; 5172d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mText = text; 518d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 5198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 5208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 5218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public String toString() { 5228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return "Item{" + 5238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar "mId=" + mId + 5242d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar ", originalIndex=" + mAdapterIndex + 5252d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar ", text='" + mText + '\'' + 5268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar '}'; 5278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 528d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 529d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 530e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public class TestAdapter extends RecyclerView.Adapter<TestViewHolder> 53190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar implements AttachDetachCountingAdapter { 532d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 53390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar ViewAttachDetachCounter mAttachmentCounter = new ViewAttachDetachCounter(); 534d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase List<Item> mItems; 535d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 536e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public TestAdapter(int count) { 537d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase mItems = new ArrayList<Item>(count); 538d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase for (int i = 0; i < count; i++) { 539d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase mItems.add(new Item(i, "Item " + i)); 540d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 541d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 542d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 543d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 54490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onViewAttachedToWindow(TestViewHolder holder) { 54590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar super.onViewAttachedToWindow(holder); 54690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachmentCounter.onViewAttached(holder); 54790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 54890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 54990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 55090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onViewDetachedFromWindow(TestViewHolder holder) { 55190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar super.onViewDetachedFromWindow(holder); 55290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachmentCounter.onViewDetached(holder); 55390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 55490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 55590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 55690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onAttachedToRecyclerView(RecyclerView recyclerView) { 55790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar super.onAttachedToRecyclerView(recyclerView); 55890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachmentCounter.onAttached(recyclerView); 55990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 56090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 56190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 56290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onDetachedFromRecyclerView(RecyclerView recyclerView) { 56390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar super.onDetachedFromRecyclerView(recyclerView); 56490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachmentCounter.onDetached(recyclerView); 56590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 56690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 56790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 568d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public TestViewHolder onCreateViewHolder(ViewGroup parent, 569d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase int viewType) { 570d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return new TestViewHolder(new TextView(parent.getContext())); 571d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 572d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 573d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 574d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public void onBindViewHolder(TestViewHolder holder, int position) { 575619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar assertNotNull(holder.mOwnerRecyclerView); 576619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar assertEquals(position, holder.getAdapterPosition()); 577d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase final Item item = mItems.get(position); 5782d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar ((TextView) (holder.itemView)).setText(item.mText + "(" + item.mAdapterIndex + ")"); 579115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar holder.mBoundItem = item; 580d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 581d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 582888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar public Item getItemAt(int position) { 583888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar return mItems.get(position); 584888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar } 585888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar 586619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar @Override 587619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar public void onViewRecycled(TestViewHolder holder) { 588619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar super.onViewRecycled(holder); 589619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar final int adapterPosition = holder.getAdapterPosition(); 590619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar final boolean shouldHavePosition = !holder.isRemoved() && holder.isBound() && 591619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar !holder.isAdapterPositionUnknown() && !holder.isInvalid(); 592619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar String log = "Position check for " + holder.toString(); 593619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar assertEquals(log, shouldHavePosition, adapterPosition != RecyclerView.NO_POSITION); 594619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar if (shouldHavePosition) { 595619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar assertTrue(log, mItems.size() > adapterPosition); 596619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar assertSame(log, holder.mBoundItem, mItems.get(adapterPosition)); 597619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar } 598619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar } 599619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar 600b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void deleteAndNotify(final int start, final int count) throws Throwable { 601b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar deleteAndNotify(new int[]{start, count}); 602b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 603b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 604b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar /** 605b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * Deletes items in the given ranges. 606b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <p> 607b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * Note that each operation affects the one after so you should offset them properly. 608b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <p> 609b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * For example, if adapter has 5 items (A,B,C,D,E), and then you call this method with 610b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <code>[1, 2],[2, 1]</code>, it will first delete items B,C and the new adapter will be 611b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * A D E. Then it will delete 2,1 which means it will delete E. 612b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar */ 613b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void deleteAndNotify(final int[]... startCountTuples) throws Throwable { 6148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int[] tuple : startCountTuples) { 6158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar tuple[1] = -tuple[1]; 6168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new AddRemoveRunnable(startCountTuples).runOnMainThread(); 618d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 619d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 620c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar @Override 621c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar public long getItemId(int position) { 622c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar return hasStableIds() ? mItems.get(position).mId : super.getItemId(position); 623c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar } 624c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar 6252d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public void offsetOriginalIndices(int start, int offset) { 6262d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (int i = start; i < mItems.size(); i++) { 6272d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mItems.get(i).mAdapterIndex += offset; 6282d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 6292d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 6302d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 631e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar /** 632e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar * @param start inclusive 633e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar * @param end exclusive 634e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar * @param offset 635e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar */ 636e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void offsetOriginalIndicesBetween(int start, int end, int offset) { 637e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar for (int i = start; i < end && i < mItems.size(); i++) { 638e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar mItems.get(i).mAdapterIndex += offset; 639e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 640e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 641e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar 642b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void addAndNotify(final int start, final int count) throws Throwable { 643b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar addAndNotify(new int[]{start, count}); 644b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 645b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 646b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void addAndNotify(final int[]... startCountTuples) throws Throwable { 6478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new AddRemoveRunnable(startCountTuples).runOnMainThread(); 6488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 649b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 650cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void dispatchDataSetChanged() throws Throwable { 65122b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar runTestOnUiThread(new Runnable() { 65222b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar @Override 65322b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar public void run() { 65422b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar notifyDataSetChanged(); 65522b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar } 65622b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar }); 65722b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar } 65822b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar 659cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void changeAndNotify(final int start, final int count) throws Throwable { 6607e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar runTestOnUiThread(new Runnable() { 6617e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar @Override 6627e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar public void run() { 6637e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar notifyItemRangeChanged(start, count); 6647e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 6657e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar }); 6667e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 6677e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 668cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void changePositionsAndNotify(final int... positions) throws Throwable { 669cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase runTestOnUiThread(new Runnable() { 670cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase @Override 671cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void run() { 672cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase for (int i = 0; i < positions.length; i += 1) { 673cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase TestAdapter.super.notifyItemRangeChanged(positions[i], 1); 674cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase } 675cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase } 676cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase }); 677cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase } 678cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase 6798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar /** 6808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * Similar to other methods but negative count means delete and position count means add. 6818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * <p> 6828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * For instance, calling this method with <code>[1,1], [2,-1]</code> it will first add an 6838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * item to index 1, then remove an item from index 2 (updated index 2) 6848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar */ 6858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void addDeleteAndNotify(final int[]... startCountTuples) throws Throwable { 6868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new AddRemoveRunnable(startCountTuples).runOnMainThread(); 687d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 688d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 689d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 690d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public int getItemCount() { 691d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return mItems.size(); 692d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 6938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 694115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar /** 695115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar * Uses notifyDataSetChanged 696115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar */ 697e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void moveItems(boolean notifyChange, int[]... fromToTuples) throws Throwable { 698e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar for (int i = 0; i < fromToTuples.length; i += 1) { 699e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar int[] tuple = fromToTuples[i]; 700e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar moveItem(tuple[0], tuple[1], false); 701e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 702e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar if (notifyChange) { 703cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase dispatchDataSetChanged(); 704e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 705e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 706e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar 707115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar /** 708115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar * Uses notifyDataSetChanged 709115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar */ 710e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void moveItem(final int from, final int to, final boolean notifyChange) 711e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar throws Throwable { 712e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar runTestOnUiThread(new Runnable() { 713e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar @Override 714e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void run() { 715ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar moveInUIThread(from, to); 716e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar if (notifyChange) { 717e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar notifyDataSetChanged(); 718e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 719e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 720e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar }); 721e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 722e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar 723115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar /** 724115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar * Uses notifyItemMoved 725115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar */ 726115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar public void moveAndNotify(final int from, final int to) throws Throwable { 727115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar runTestOnUiThread(new Runnable() { 728115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar @Override 729115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar public void run() { 730ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar moveInUIThread(from, to); 731115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar notifyItemMoved(from, to); 732115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar } 733115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar }); 734115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar } 735115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar 736ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar protected void moveInUIThread(int from, int to) { 737ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar Item item = mItems.remove(from); 738ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar offsetOriginalIndices(from, -1); 739ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar mItems.add(to, item); 740ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar offsetOriginalIndices(to + 1, 1); 741ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar item.mAdapterIndex = to; 742ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar } 743115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar 744115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar 74590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 74690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public ViewAttachDetachCounter getCounter() { 74790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar return mAttachmentCounter; 74890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 74990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 7508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 7518ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private class AddRemoveRunnable implements Runnable { 7528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar final int[][] mStartCountTuples; 7538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 7548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public AddRemoveRunnable(int[][] startCountTuples) { 7558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mStartCountTuples = startCountTuples; 7568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 7588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void runOnMainThread() throws Throwable { 7598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (Looper.myLooper() == Looper.getMainLooper()) { 7608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar run(); 7618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 7628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar runTestOnUiThread(this); 7638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 7668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 7678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void run() { 7688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int[] tuple : mStartCountTuples) { 7698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (tuple[1] < 0) { 7708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar delete(tuple); 7718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 7728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar add(tuple); 7738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 7778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private void add(int[] tuple) { 7782d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar // offset others 7792d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar offsetOriginalIndices(tuple[0], tuple[1]); 7808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int i = 0; i < tuple[1]; i++) { 7818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mItems.add(tuple[0], new Item(i, "new item " + i)); 7828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar notifyItemRangeInserted(tuple[0], tuple[1]); 7848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 7868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private void delete(int[] tuple) { 7872d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int count = -tuple[1]; 7882d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar offsetOriginalIndices(tuple[0] + count, tuple[1]); 7892d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (int i = 0; i < count; i++) { 7908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mItems.remove(tuple[0]); 7918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7922d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar notifyItemRangeRemoved(tuple[0], count); 7938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 79790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public boolean isMainThread() { 79890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar return Looper.myLooper() == Looper.getMainLooper(); 79990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 80090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 8018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 8028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void runTestOnUiThread(Runnable r) throws Throwable { 8038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (Looper.myLooper() == Looper.getMainLooper()) { 8048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar r.run(); 8058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 8068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super.runTestOnUiThread(r); 8078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 808d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 809c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 810c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar static class TargetTuple { 811c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 812c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int mPosition; 813c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 814c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int mLayoutDirection; 815c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 816c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar TargetTuple(int position, int layoutDirection) { 817c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar this.mPosition = position; 818c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar this.mLayoutDirection = layoutDirection; 819c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 820c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 821c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar @Override 822c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar public String toString() { 823c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar return "TargetTuple{" + 824c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar "mPosition=" + mPosition + 825c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar ", mLayoutDirection=" + mLayoutDirection + 826c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar '}'; 827c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 828c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 82990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 83090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public interface AttachDetachCountingAdapter { 83190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 83290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar ViewAttachDetachCounter getCounter(); 83390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 83490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 83590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public class ViewAttachDetachCounter { 83690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 83790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar Set<RecyclerView.ViewHolder> mAttachedSet = new HashSet<RecyclerView.ViewHolder>(); 83890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 83990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void validateRemaining(RecyclerView recyclerView) { 84090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar final int childCount = recyclerView.getChildCount(); 84190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar for (int i = 0; i < childCount; i++) { 84290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar View view = recyclerView.getChildAt(i); 84390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(view); 84490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar assertTrue("remaining view should be in attached set " + vh, 84590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachedSet.contains(vh)); 84690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 84790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar assertEquals("there should not be any views left in attached set", 84890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar childCount, mAttachedSet.size()); 84990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 85090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 85190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onViewDetached(RecyclerView.ViewHolder viewHolder) { 85290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar try { 85390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar assertTrue("view holder should be in attached set", 85490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachedSet.remove(viewHolder)); 85590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } catch (Throwable t) { 85690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar postExceptionToInstrumentation(t); 85790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 85890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 85990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 86090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onViewAttached(RecyclerView.ViewHolder viewHolder) { 86190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar try { 86290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar assertTrue("view holder should not be in attached set", 86390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachedSet.add(viewHolder)); 86490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } catch (Throwable t) { 86590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar postExceptionToInstrumentation(t); 86690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 86790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 86890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 86990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onAttached(RecyclerView recyclerView) { 87090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar // when a new RV is attached, clear the set and add all view holders 87190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachedSet.clear(); 87290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar final int childCount = recyclerView.getChildCount(); 87390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar for (int i = 0; i < childCount; i ++) { 87490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar View view = recyclerView.getChildAt(i); 87590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachedSet.add(recyclerView.getChildViewHolder(view)); 87690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 87790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 87890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 87990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onDetached(RecyclerView recyclerView) { 88090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar validateRemaining(recyclerView); 88190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 88290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 8837a59e4b36500bcfddd842376d6c3abe0d9f23d84Yigit Boyar} 884