BaseRecyclerViewInstrumentationTest.java revision 5ced882cabdcefbb469e332f6f73983999aad0e5
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 43d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public BaseRecyclerViewInstrumentationTest() { 44b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar this(false); 45b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 46b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 47b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public BaseRecyclerViewInstrumentationTest(boolean debug) { 488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super("android.support.v7.recyclerview", TestActivity.class); 49b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mDebug = debug; 50b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 51b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar protected void tearDown() throws Exception { 548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (mRecyclerView != null) { 558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar try { 568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar removeRecyclerView(); 578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } catch (Throwable throwable) { 588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar throwable.printStackTrace(); 598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar getInstrumentation().waitForIdleSync(); 628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super.tearDown(); 638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 65b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void removeRecyclerView() throws Throwable { 66b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mRecyclerView = null; 67b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 68b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 69b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 70b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar getActivity().mContainer.removeAllViews(); 71b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 72b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 73b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 74b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 75b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void setRecyclerView(final RecyclerView recyclerView) throws Throwable { 76b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mRecyclerView = recyclerView; 778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mAdapterHelper = recyclerView.mAdapterHelper; 78b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 79b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 80b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 81b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar getActivity().mContainer.addView(recyclerView); 82b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 83b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 84b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 85b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 86b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void requestLayoutOnUIThread(final View view) throws Throwable { 87b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 88b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 89b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 90b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar view.requestLayout(); 91b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 92b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 93b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 94b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 95b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void scrollBy(final int dt) throws Throwable { 96b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 97b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 98b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 99b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mRecyclerView.getLayoutManager().canScrollHorizontally()) { 100b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mRecyclerView.scrollBy(dt, 0); 101b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } else { 102b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mRecyclerView.scrollBy(0, dt); 103b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 104b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 105b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 106b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 107d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 108d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 1098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void scrollToPosition(final int position) throws Throwable { 1108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 1118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 1128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 1138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.getLayoutManager().scrollToPosition(position); 1148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 1168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 1188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void smoothScrollToPosition(final int position) 1198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throws Throwable { 1208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "SMOOTH scrolling to " + position); 1218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 1228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 1238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 1248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.smoothScrollToPosition(position); 1258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 1278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar while (mRecyclerView.getLayoutManager().isSmoothScrolling() || 1288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) { 1298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (mDebug) { 1308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "SMOOTH scrolling step"); 1318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Thread.sleep(200); 1338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "SMOOTH scrolling done"); 1358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 137d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase class TestViewHolder extends RecyclerView.ViewHolder { 138d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 139d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Item mBindedItem; 140d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 141d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public TestViewHolder(View itemView) { 142d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase super(itemView); 143d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 144d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 145d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 146d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase class TestLayoutManager extends RecyclerView.LayoutManager { 147d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 148d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase CountDownLatch layoutLatch; 149d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 150d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public void expectLayouts(int count) { 151d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase layoutLatch = new CountDownLatch(count); 152d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 153d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 154b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void waitForLayout(long timeout, TimeUnit timeUnit) throws Throwable { 155b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout * (mDebug ? 100 : 1), timeUnit); 156d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase assertEquals("all expected layouts should be executed at the expected time", 157d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 0, layoutLatch.getCount()); 158d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 159d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 160b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void assertLayoutCount(int count, String msg, long timeout) throws Throwable { 161b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout, TimeUnit.SECONDS); 162b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertEquals(msg, count, layoutLatch.getCount()); 163b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 164b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 165b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void assertNoLayout(String msg, long timeout) throws Throwable { 166b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout, TimeUnit.SECONDS); 167b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertFalse(msg, layoutLatch.getCount() == 0); 168b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 169b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 170b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void waitForLayout(long timeout) throws Throwable { 1718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar waitForLayout(timeout * (mDebug ? 10000 : 1), TimeUnit.SECONDS); 172b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 173b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 174d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 175d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public RecyclerView.LayoutParams generateDefaultLayoutParams() { 176d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 177d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase ViewGroup.LayoutParams.WRAP_CONTENT); 178d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 179d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 180d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase void assertVisibleItemPositions() { 181d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase int i = getChildCount(); 182d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase TestAdapter testAdapter = (TestAdapter) mRecyclerView.getAdapter(); 183d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase while (i-- > 0) { 184d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase View view = getChildAt(i); 185d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams(); 186d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Item item = ((TestViewHolder) lp.mViewHolder).mBindedItem; 187b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 188d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "testing item " + i); 189d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 1908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (!lp.isItemRemoved()) { 1918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar RecyclerView.ViewHolder vh = mRecyclerView.getChildViewHolder(view); 1928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertSame("item position in LP should match adapter value :" + vh, 1938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar testAdapter.mItems.get(vh.mPosition), item); 1948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 195d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 196d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 197d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 198b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView.LayoutParams getLp(View v) { 199b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar return (RecyclerView.LayoutParams) v.getLayoutParams(); 200b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 201b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 2028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar /** 2038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * returns skipped (removed) view count. 2048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar */ 2058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar int layoutRange(RecyclerView.Recycler recycler, int start, 206d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase int end) { 2078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar int skippedAdd = 0; 208b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 209d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "will layout items from " + start + " to " + end); 210d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 2115ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar int diff = end > start ? 1 : -1; 2125ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar for (int i = start; i != end; i+=diff) { 213b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 214d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "laying out item " + i); 215d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 216d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase View view = recycler.getViewForPosition(i); 217b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertNotNull("view should not be null for valid position. " 218b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar + "got null view at position " + i, view); 219b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (!getLp(view).isItemRemoved()) { 220b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar addView(view); 2218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 2228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar skippedAdd ++; 223b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 224b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 225d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase measureChildWithMargins(view, 0, 0); 2265ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar layoutDecorated(view, 0, Math.abs(i - start) * 10, getDecoratedMeasuredWidth(view) 227d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase , getDecoratedMeasuredHeight(view)); 228d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 2298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return skippedAdd; 230d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 231d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 232d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 233d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase static class Item { 23411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final static AtomicInteger idCounter = new AtomicInteger(0); 23511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final public int mId = idCounter.incrementAndGet(); 236d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 237d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase int originalIndex; 238d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 239d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase final String text; 240d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 241d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Item(int originalIndex, String text) { 242d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase this.originalIndex = originalIndex; 243d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase this.text = text; 244d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 2458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 2468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 2478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public String toString() { 2488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return "Item{" + 2498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar "mId=" + mId + 2508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ", originalIndex=" + originalIndex + 2518ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ", text='" + text + '\'' + 2528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar '}'; 2538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 254d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 255d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 256d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase class TestAdapter extends RecyclerView.Adapter<TestViewHolder> { 257d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 258d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase List<Item> mItems; 259d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 260d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase TestAdapter(int count) { 261d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase mItems = new ArrayList<Item>(count); 262d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase for (int i = 0; i < count; i++) { 263d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase mItems.add(new Item(i, "Item " + i)); 264d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 265d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 266d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 267d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 268d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public TestViewHolder onCreateViewHolder(ViewGroup parent, 269d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase int viewType) { 270d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return new TestViewHolder(new TextView(parent.getContext())); 271d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 272d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 273d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 274d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public void onBindViewHolder(TestViewHolder holder, int position) { 275d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase final Item item = mItems.get(position); 276d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase ((TextView) (holder.itemView)).setText(item.text); 277d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase holder.mBindedItem = item; 278d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 279d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 280b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void deleteAndNotify(final int start, final int count) throws Throwable { 281b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar deleteAndNotify(new int[]{start, count}); 282b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 283b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 284b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar /** 285b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * Deletes items in the given ranges. 286b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <p> 287b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * Note that each operation affects the one after so you should offset them properly. 288b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <p> 289b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * For example, if adapter has 5 items (A,B,C,D,E), and then you call this method with 290b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <code>[1, 2],[2, 1]</code>, it will first delete items B,C and the new adapter will be 291b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * A D E. Then it will delete 2,1 which means it will delete E. 292b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar */ 293b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void deleteAndNotify(final int[]... startCountTuples) throws Throwable { 2948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int[] tuple : startCountTuples) { 2958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar tuple[1] = -tuple[1]; 2968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 2978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new AddRemoveRunnable(startCountTuples).runOnMainThread(); 298d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 299d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 300b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void addAndNotify(final int start, final int count) throws Throwable { 301b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar addAndNotify(new int[]{start, count}); 302b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 303b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 304b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void addAndNotify(final int[]... startCountTuples) throws Throwable { 3058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new AddRemoveRunnable(startCountTuples).runOnMainThread(); 3068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 307b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 30822b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar public void notifyChange() throws Throwable { 30922b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar runTestOnUiThread(new Runnable() { 31022b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar @Override 31122b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar public void run() { 31222b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar notifyDataSetChanged(); 31322b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar } 31422b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar }); 31522b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar } 31622b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar 3178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar /** 3188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * Similar to other methods but negative count means delete and position count means add. 3198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * <p> 3208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * For instance, calling this method with <code>[1,1], [2,-1]</code> it will first add an 3218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * item to index 1, then remove an item from index 2 (updated index 2) 3228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar */ 3238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void addDeleteAndNotify(final int[]... startCountTuples) throws Throwable { 3248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new AddRemoveRunnable(startCountTuples).runOnMainThread(); 325d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 326d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 327d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 328d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public int getItemCount() { 329d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return mItems.size(); 330d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 3318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 3328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 3338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private class AddRemoveRunnable implements Runnable { 3348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar final int[][] mStartCountTuples; 3358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 3368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public AddRemoveRunnable(int[][] startCountTuples) { 3378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mStartCountTuples = startCountTuples; 3388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 3408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void runOnMainThread() throws Throwable { 3418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (Looper.myLooper() == Looper.getMainLooper()) { 3428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar run(); 3438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 3448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar runTestOnUiThread(this); 3458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 3488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 3498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void run() { 3508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int[] tuple : mStartCountTuples) { 3518ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (tuple[1] < 0) { 3528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar delete(tuple); 3538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 3548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar add(tuple); 3558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 3598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private void add(int[] tuple) { 3608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int i = 0; i < tuple[1]; i++) { 3618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mItems.add(tuple[0], new Item(i, "new item " + i)); 3628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // offset others 3648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int i = tuple[0] + tuple[1]; i < mItems.size(); i++) { 3658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mItems.get(i).originalIndex += tuple[1]; 3668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar notifyItemRangeInserted(tuple[0], tuple[1]); 3688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 3708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private void delete(int[] tuple) { 3718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int i = 0; i < -tuple[1]; i++) { 3728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mItems.remove(tuple[0]); 3738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar notifyItemRangeRemoved(tuple[0], -tuple[1]); 3758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 3798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 3808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void runTestOnUiThread(Runnable r) throws Throwable { 3818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (Looper.myLooper() == Looper.getMainLooper()) { 3828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar r.run(); 3838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 3848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super.runTestOnUiThread(r); 3858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 386d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 387b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar}