BaseRecyclerViewInstrumentationTest.java revision 7e9da1a2629b657d56e2f54abd18c6249f3be037
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 198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyarimport android.os.Looper; 20d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.test.ActivityInstrumentationTestCase2; 21d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.util.Log; 22d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.view.View; 23d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.view.ViewGroup; 24d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.widget.TextView; 25d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 26d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.ArrayList; 27d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.List; 28d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.CountDownLatch; 29d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.TimeUnit; 3011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.concurrent.atomic.AtomicInteger; 31d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 32d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseabstract public class BaseRecyclerViewInstrumentationTest extends 33d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase ActivityInstrumentationTestCase2<TestActivity> { 34d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 35d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase private static final String TAG = "RecyclerViewTest"; 36d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 37b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar private boolean mDebug; 38b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 39b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar protected RecyclerView mRecyclerView; 40d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar protected AdapterHelper mAdapterHelper; 428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 437e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar Throwable mainThreadException; 447e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 45d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public BaseRecyclerViewInstrumentationTest() { 46b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar this(false); 47b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 48b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 49b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public BaseRecyclerViewInstrumentationTest(boolean debug) { 508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super("android.support.v7.recyclerview", TestActivity.class); 51b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mDebug = debug; 52b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 53b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 547e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar void checkForMainThreadException() throws Throwable { 557e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar if (mainThreadException != null) { 567e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar throw mainThreadException; 577e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 587e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 597e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 607e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar void postExceptionToInstrumentation(Throwable t) { 617e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar if (mDebug) { 627e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar Log.e(TAG, "captured exception on main thread", t); 637e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 647e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar mainThreadException = t; 657e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar if (mRecyclerView != null && mRecyclerView 667e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar .getLayoutManager() instanceof TestLayoutManager) { 677e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar TestLayoutManager lm = (TestLayoutManager) mRecyclerView.getLayoutManager(); 687e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar // finish all layouts so that we get the correct exception 697e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar while (lm.layoutLatch.getCount() > 0) { 707e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar lm.layoutLatch.countDown(); 717e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 727e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 737e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 747e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar protected void tearDown() throws Exception { 778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (mRecyclerView != null) { 788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar try { 798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar removeRecyclerView(); 808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } catch (Throwable throwable) { 818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar throwable.printStackTrace(); 828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar getInstrumentation().waitForIdleSync(); 858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super.tearDown(); 868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 88b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void removeRecyclerView() throws Throwable { 89b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mRecyclerView = null; 90b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 91b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 92b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 93b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar getActivity().mContainer.removeAllViews(); 94b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 95b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 96b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 97b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 98b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void setRecyclerView(final RecyclerView recyclerView) throws Throwable { 99b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mRecyclerView = recyclerView; 1008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mAdapterHelper = recyclerView.mAdapterHelper; 101b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 102b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 103b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 104b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar getActivity().mContainer.addView(recyclerView); 105b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 106b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 107b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 108b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 109b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void requestLayoutOnUIThread(final View view) throws Throwable { 110b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 111b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 112b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 113b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar view.requestLayout(); 114b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 115b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 116b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 117b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 118b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void scrollBy(final int dt) throws Throwable { 119b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 120b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 121b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 122b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mRecyclerView.getLayoutManager().canScrollHorizontally()) { 123b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mRecyclerView.scrollBy(dt, 0); 124b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } else { 125b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mRecyclerView.scrollBy(0, dt); 126b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 127b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 128b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 129b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 130d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 131d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 1328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void scrollToPosition(final int position) throws Throwable { 1338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 1348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 1358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 1368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.getLayoutManager().scrollToPosition(position); 1378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 1398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 1418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void smoothScrollToPosition(final int position) 1428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throws Throwable { 1438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "SMOOTH scrolling to " + position); 1448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 1458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 1468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 1478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.smoothScrollToPosition(position); 1488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 1508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar while (mRecyclerView.getLayoutManager().isSmoothScrolling() || 1518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) { 1528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (mDebug) { 1538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "SMOOTH scrolling step"); 1548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Thread.sleep(200); 1568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "SMOOTH scrolling done"); 1588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 160d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase class TestViewHolder extends RecyclerView.ViewHolder { 161d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 162d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Item mBindedItem; 163d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 164d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public TestViewHolder(View itemView) { 165d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase super(itemView); 166d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 167d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 168d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 169d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase class TestLayoutManager extends RecyclerView.LayoutManager { 170d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 171d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase CountDownLatch layoutLatch; 172d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 173d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public void expectLayouts(int count) { 174d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase layoutLatch = new CountDownLatch(count); 175d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 176d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 177b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void waitForLayout(long timeout, TimeUnit timeUnit) throws Throwable { 178b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout * (mDebug ? 100 : 1), timeUnit); 179d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase assertEquals("all expected layouts should be executed at the expected time", 180d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 0, layoutLatch.getCount()); 181d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 182d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 183b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void assertLayoutCount(int count, String msg, long timeout) throws Throwable { 184b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout, TimeUnit.SECONDS); 185b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertEquals(msg, count, layoutLatch.getCount()); 186b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 187b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 188b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void assertNoLayout(String msg, long timeout) throws Throwable { 189b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout, TimeUnit.SECONDS); 190b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertFalse(msg, layoutLatch.getCount() == 0); 191b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 192b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 193b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void waitForLayout(long timeout) throws Throwable { 1948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar waitForLayout(timeout * (mDebug ? 10000 : 1), TimeUnit.SECONDS); 195b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 196b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 197d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 198d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public RecyclerView.LayoutParams generateDefaultLayoutParams() { 199d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 200d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase ViewGroup.LayoutParams.WRAP_CONTENT); 201d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 202d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 203d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase void assertVisibleItemPositions() { 204d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase int i = getChildCount(); 205d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase TestAdapter testAdapter = (TestAdapter) mRecyclerView.getAdapter(); 206d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase while (i-- > 0) { 207d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase View view = getChildAt(i); 208d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams(); 209d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Item item = ((TestViewHolder) lp.mViewHolder).mBindedItem; 210b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 211d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "testing item " + i); 212d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 2138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (!lp.isItemRemoved()) { 2148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar RecyclerView.ViewHolder vh = mRecyclerView.getChildViewHolder(view); 2158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertSame("item position in LP should match adapter value :" + vh, 2168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar testAdapter.mItems.get(vh.mPosition), item); 2178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 218d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 219d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 220d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 221b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView.LayoutParams getLp(View v) { 222b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar return (RecyclerView.LayoutParams) v.getLayoutParams(); 223b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 224b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 2258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar /** 2268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * returns skipped (removed) view count. 2278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar */ 2288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar int layoutRange(RecyclerView.Recycler recycler, int start, 229d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase int end) { 2308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar int skippedAdd = 0; 231b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 232d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "will layout items from " + start + " to " + end); 233d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 234d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase for (int i = start; i < end; i++) { 235b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 236d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "laying out item " + i); 237d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 238d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase View view = recycler.getViewForPosition(i); 239b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertNotNull("view should not be null for valid position. " 240b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar + "got null view at position " + i, view); 241b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (!getLp(view).isItemRemoved()) { 242b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar addView(view); 2438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 2448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar skippedAdd ++; 245b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 246b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 247d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase measureChildWithMargins(view, 0, 0); 248d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase layoutDecorated(view, 0, (i - start) * 10, getDecoratedMeasuredWidth(view) 249d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase , getDecoratedMeasuredHeight(view)); 250d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 2518ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return skippedAdd; 252d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 253d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 254d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 255d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase static class Item { 25611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final static AtomicInteger idCounter = new AtomicInteger(0); 25711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final public int mId = idCounter.incrementAndGet(); 258d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 259d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase int originalIndex; 260d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 261d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase final String text; 262d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 263d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Item(int originalIndex, String text) { 264d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase this.originalIndex = originalIndex; 265d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase this.text = text; 266d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 2678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 2688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 2698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public String toString() { 2708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return "Item{" + 2718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar "mId=" + mId + 2728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ", originalIndex=" + originalIndex + 2738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ", text='" + text + '\'' + 2748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar '}'; 2758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 276d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 277d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 278d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase class TestAdapter extends RecyclerView.Adapter<TestViewHolder> { 279d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 280d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase List<Item> mItems; 281d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 282d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase TestAdapter(int count) { 283d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase mItems = new ArrayList<Item>(count); 284d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase for (int i = 0; i < count; i++) { 285d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase mItems.add(new Item(i, "Item " + i)); 286d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 287d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 288d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 289d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 290d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public TestViewHolder onCreateViewHolder(ViewGroup parent, 291d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase int viewType) { 292d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return new TestViewHolder(new TextView(parent.getContext())); 293d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 294d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 295d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 296d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public void onBindViewHolder(TestViewHolder holder, int position) { 297d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase final Item item = mItems.get(position); 298d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase ((TextView) (holder.itemView)).setText(item.text); 299d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase holder.mBindedItem = item; 300d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 301d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 302b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void deleteAndNotify(final int start, final int count) throws Throwable { 303b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar deleteAndNotify(new int[]{start, count}); 304b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 305b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 306b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar /** 307b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * Deletes items in the given ranges. 308b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <p> 309b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * Note that each operation affects the one after so you should offset them properly. 310b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <p> 311b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * For example, if adapter has 5 items (A,B,C,D,E), and then you call this method with 312b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <code>[1, 2],[2, 1]</code>, it will first delete items B,C and the new adapter will be 313b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * A D E. Then it will delete 2,1 which means it will delete E. 314b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar */ 315b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void deleteAndNotify(final int[]... startCountTuples) throws Throwable { 3168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int[] tuple : startCountTuples) { 3178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar tuple[1] = -tuple[1]; 3188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new AddRemoveRunnable(startCountTuples).runOnMainThread(); 320d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 321d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 322b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void addAndNotify(final int start, final int count) throws Throwable { 323b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar addAndNotify(new int[]{start, count}); 324b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 325b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 326b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void addAndNotify(final int[]... startCountTuples) throws Throwable { 3278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new AddRemoveRunnable(startCountTuples).runOnMainThread(); 3288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 329b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 33022b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar public void notifyChange() throws Throwable { 33122b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar runTestOnUiThread(new Runnable() { 33222b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar @Override 33322b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar public void run() { 33422b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar notifyDataSetChanged(); 33522b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar } 33622b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar }); 33722b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar } 33822b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar 3397e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar public void notifyItemChange(final int start, final int count) throws Throwable { 3407e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar runTestOnUiThread(new Runnable() { 3417e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar @Override 3427e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar public void run() { 3437e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar notifyItemRangeChanged(start, count); 3447e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 3457e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar }); 3467e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 3477e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 3488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar /** 3498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * Similar to other methods but negative count means delete and position count means add. 3508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * <p> 3518ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * For instance, calling this method with <code>[1,1], [2,-1]</code> it will first add an 3528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * item to index 1, then remove an item from index 2 (updated index 2) 3538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar */ 3548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void addDeleteAndNotify(final int[]... startCountTuples) throws Throwable { 3558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new AddRemoveRunnable(startCountTuples).runOnMainThread(); 356d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 357d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 358d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 359d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public int getItemCount() { 360d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return mItems.size(); 361d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 3628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 3638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 3648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private class AddRemoveRunnable implements Runnable { 3658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar final int[][] mStartCountTuples; 3668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 3678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public AddRemoveRunnable(int[][] startCountTuples) { 3688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mStartCountTuples = startCountTuples; 3698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 3718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void runOnMainThread() throws Throwable { 3728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (Looper.myLooper() == Looper.getMainLooper()) { 3738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar run(); 3748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 3758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar runTestOnUiThread(this); 3768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 3798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 3808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void run() { 3818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int[] tuple : mStartCountTuples) { 3828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (tuple[1] < 0) { 3838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar delete(tuple); 3848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 3858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar add(tuple); 3868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 3908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private void add(int[] tuple) { 3918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int i = 0; i < tuple[1]; i++) { 3928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mItems.add(tuple[0], new Item(i, "new item " + i)); 3938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // offset others 3958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int i = tuple[0] + tuple[1]; i < mItems.size(); i++) { 3968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mItems.get(i).originalIndex += tuple[1]; 3978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar notifyItemRangeInserted(tuple[0], tuple[1]); 3998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 4008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 4018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private void delete(int[] tuple) { 4028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int i = 0; i < -tuple[1]; i++) { 4038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mItems.remove(tuple[0]); 4048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 4058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar notifyItemRangeRemoved(tuple[0], -tuple[1]); 4068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 4078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 4088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 4098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 4108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 4118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void runTestOnUiThread(Runnable r) throws Throwable { 4128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (Looper.myLooper() == Looper.getMainLooper()) { 4138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar r.run(); 4148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 4158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super.runTestOnUiThread(r); 4168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 417d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 418b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar}