RecyclerViewLayoutTest.java revision 5ced882cabdcefbb469e332f6f73983999aad0e5
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 18package android.support.v7.widget; 19 20import android.view.View; 21 22import java.util.concurrent.TimeUnit; 23import java.util.concurrent.atomic.AtomicBoolean; 24import java.util.concurrent.atomic.AtomicInteger; 25 26public class RecyclerViewLayoutTest extends BaseRecyclerViewInstrumentationTest { 27 28 private static final boolean DEBUG = false; 29 30 public RecyclerViewLayoutTest() { 31 super(DEBUG); 32 } 33 34 public void testFindViewById() throws Throwable { 35 findViewByIdTest(false); 36 removeRecyclerView(); 37 findViewByIdTest(true); 38 } 39 40 public void findViewByIdTest(final boolean supportPredictive) throws Throwable { 41 final RecyclerView recyclerView = new RecyclerView(getActivity()); 42 final int initialAdapterSize = 20; 43 final TestAdapter adapter = new TestAdapter(initialAdapterSize); 44 final int deleteStart = 6; 45 final int deleteCount = 5; 46 recyclerView.setAdapter(adapter); 47 final AtomicBoolean assertPositions = new AtomicBoolean(false); 48 TestLayoutManager lm = new TestLayoutManager() { 49 @Override 50 public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 51 super.onLayoutChildren(recycler, state); 52 if (assertPositions.get()) { 53 if (state.isPreLayout()) { 54 for (int i = 0; i < deleteStart; i ++) { 55 View view = findViewByPosition(i); 56 assertNotNull("find view by position for existing items should work " 57 + "fine", view); 58 assertFalse("view should not be marked as removed", 59 ((RecyclerView.LayoutParams) view.getLayoutParams()) 60 .isItemRemoved()); 61 } 62 for (int i = 0; i < deleteCount; i++) { 63 View view = findViewByPosition(i + deleteStart); 64 assertNotNull("find view by position should work fine for removed " 65 + "views in pre-layout", view); 66 assertTrue("view should be marked as removed", 67 ((RecyclerView.LayoutParams) view.getLayoutParams()) 68 .isItemRemoved()); 69 } 70 for (int i = deleteStart + deleteCount; i < 20; i++) { 71 View view = findViewByPosition(i); 72 assertNotNull(view); 73 assertFalse("view should not be marked as removed", 74 ((RecyclerView.LayoutParams) view.getLayoutParams()) 75 .isItemRemoved()); 76 } 77 } else { 78 for (int i = 0; i < initialAdapterSize - deleteCount; i ++) { 79 View view = findViewByPosition(i); 80 assertNotNull("find view by position for existing items should work " 81 + "fine", view); 82 TestViewHolder viewHolder = 83 (TestViewHolder) mRecyclerView.getChildViewHolder(view); 84 assertSame("should be the correct item " + viewHolder 85 ,viewHolder.mBindedItem, 86 adapter.mItems.get(viewHolder.mPosition)); 87 assertFalse("view should not be marked as removed", 88 ((RecyclerView.LayoutParams) view.getLayoutParams()) 89 .isItemRemoved()); 90 } 91 } 92 93 } 94 detachAndScrapAttachedViews(recycler); 95 layoutRange(recycler, state.getItemCount() - 1, -1); 96 layoutLatch.countDown(); 97 } 98 99 @Override 100 public boolean supportsPredictiveItemAnimations() { 101 return supportPredictive; 102 } 103 }; 104 recyclerView.setLayoutManager(lm); 105 lm.expectLayouts(1); 106 setRecyclerView(recyclerView); 107 lm.waitForLayout(2); 108 getInstrumentation().waitForIdleSync(); 109 110 assertPositions.set(true); 111 lm.expectLayouts(supportPredictive ? 2 : 1); 112 adapter.deleteAndNotify(new int[]{deleteStart, deleteCount - 1}, new int[]{deleteStart, 1}); 113 lm.waitForLayout(2); 114 } 115 116 public void testTypeForCache() throws Throwable { 117 final AtomicInteger viewType = new AtomicInteger(1); 118 final TestAdapter adapter = new TestAdapter(100) { 119 @Override 120 public int getItemViewType(int position) { 121 return viewType.get(); 122 } 123 124 @Override 125 public long getItemId(int position) { 126 return mItems.get(position).mId; 127 } 128 }; 129 adapter.setHasStableIds(true); 130 final AtomicInteger layoutStart = new AtomicInteger(2); 131 final int childCount = 10; 132 final TestLayoutManager lm = new TestLayoutManager() { 133 @Override 134 public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 135 super.onLayoutChildren(recycler, state); 136 detachAndScrapAttachedViews(recycler); 137 layoutRange(recycler, layoutStart.get(), layoutStart.get() + childCount); 138 layoutLatch.countDown(); 139 } 140 }; 141 final RecyclerView recyclerView = new RecyclerView(getActivity()); 142 recyclerView.setAdapter(adapter); 143 recyclerView.setLayoutManager(lm); 144 recyclerView.setItemViewCacheSize(10); 145 lm.expectLayouts(1); 146 setRecyclerView(recyclerView); 147 lm.waitForLayout(2); 148 getInstrumentation().waitForIdleSync(); 149 layoutStart.set(4); // trigger a cache for 3,4 150 lm.expectLayouts(1); 151 requestLayoutOnUIThread(recyclerView); 152 lm.waitForLayout(2); 153 // 154 viewType.incrementAndGet(); 155 layoutStart.set(2); // go back to bring views from cache 156 lm.expectLayouts(1); 157 adapter.mItems.remove(1); 158 adapter.notifyChange(); 159 lm.waitForLayout(2); 160 runTestOnUiThread(new Runnable() { 161 @Override 162 public void run() { 163 for (int i = 2; i < 4; i++) { 164 RecyclerView.ViewHolder vh = recyclerView.findViewHolderForPosition(2); 165 assertEquals("View holder's type should match latest type", viewType.get(), 166 vh.getItemViewType()); 167 } 168 } 169 }); 170 } 171 172 public void testState() throws Throwable { 173 final TestAdapter adapter = new TestAdapter(10); 174 final RecyclerView recyclerView = new RecyclerView(getActivity()); 175 recyclerView.setAdapter(adapter); 176 recyclerView.setItemAnimator(null); 177 final AtomicInteger itemCount = new AtomicInteger(); 178 final AtomicBoolean structureChanged = new AtomicBoolean(); 179 TestLayoutManager testLayoutManager = new TestLayoutManager() { 180 @Override 181 public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 182 detachAndScrapAttachedViews(recycler); 183 layoutRange(recycler, 0, state.getItemCount()); 184 itemCount.set(state.getItemCount()); 185 structureChanged.set(state.didStructureChange()); 186 layoutLatch.countDown(); 187 } 188 }; 189 recyclerView.setLayoutManager(testLayoutManager); 190 testLayoutManager.expectLayouts(1); 191 runTestOnUiThread(new Runnable() { 192 @Override 193 public void run() { 194 getActivity().mContainer.addView(recyclerView); 195 } 196 }); 197 testLayoutManager.waitForLayout(2, TimeUnit.SECONDS); 198 199 assertEquals("item count in state should be correct", adapter.getItemCount() 200 , itemCount.get()); 201 assertEquals("structure changed should be true for first layout", true, 202 structureChanged.get()); 203 Thread.sleep(1000); //wait for other layouts. 204 testLayoutManager.expectLayouts(1); 205 runTestOnUiThread(new Runnable() { 206 @Override 207 public void run() { 208 recyclerView.requestLayout(); 209 } 210 }); 211 testLayoutManager.waitForLayout(2); 212 assertEquals("in second layout,structure changed should be false", false, 213 structureChanged.get()); 214 testLayoutManager.expectLayouts(1); // 215 adapter.deleteAndNotify(3, 2); 216 testLayoutManager.waitForLayout(2); 217 assertEquals("when items are removed, item count in state should be updated", 218 adapter.getItemCount(), 219 itemCount.get()); 220 assertEquals("structure changed should be true when items are removed", true, 221 structureChanged.get()); 222 testLayoutManager.expectLayouts(1); 223 adapter.addAndNotify(2, 5); 224 testLayoutManager.waitForLayout(2); 225 226 assertEquals("when items are added, item count in state should be updated", 227 adapter.getItemCount(), 228 itemCount.get()); 229 assertEquals("structure changed should be true when items are removed", true, 230 structureChanged.get()); 231 232 } 233 234}