19aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar/* 29aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * Copyright (C) 2016 The Android Open Source Project 39aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * 49aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * Licensed under the Apache License, Version 2.0 (the "License"); 59aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * you may not use this file except in compliance with the License. 69aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * You may obtain a copy of the License at 79aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * 89aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * http://www.apache.org/licenses/LICENSE-2.0 99aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * 109aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * Unless required by applicable law or agreed to in writing, software 119aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * distributed under the License is distributed on an "AS IS" BASIS, 129aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * See the License for the specific language governing permissions and 149aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * limitations under the License. 159aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar */ 169aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar 179aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarpackage android.support.v7.widget; 189aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar 199aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport static android.view.View.MeasureSpec.AT_MOST; 209aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport static android.view.View.MeasureSpec.EXACTLY; 219aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport static android.view.View.MeasureSpec.makeMeasureSpec; 229aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar 239aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport static org.hamcrest.CoreMatchers.is; 249aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport static org.hamcrest.CoreMatchers.notNullValue; 259aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport static org.hamcrest.MatcherAssert.assertThat; 269aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar 279aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport android.graphics.Rect; 289aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport android.test.suitebuilder.annotation.MediumTest; 299aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport android.view.View; 309aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar 319aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport org.junit.Test; 329aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport org.junit.runner.RunWith; 339aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport org.junit.runners.Parameterized; 349aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar 359aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport java.util.ArrayList; 369aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport java.util.HashMap; 379aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport java.util.List; 389aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport java.util.Map; 399aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar 409aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar/** 419aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * Tests whether the layout manager can keep its children positions properly after it is re-laid 429aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * out with larger/smaller intermediate size but the same final size. 439aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar */ 449aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar@MediumTest 459aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar@RunWith(Parameterized.class) 469aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarpublic class TestResizingRelayoutWithAutoMeasure extends BaseRecyclerViewInstrumentationTest { 479aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar private final RecyclerView.LayoutManager mLayoutManager; 489aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar private final float mWidthMultiplier; 499aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar private final float mHeightMultiplier; 509aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar 519aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar public TestResizingRelayoutWithAutoMeasure(@SuppressWarnings("UnusedParameters") String name, 529aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar RecyclerView.LayoutManager layoutManager, float widthMultiplier, 539aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar float heightMultiplier) { 549aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar mLayoutManager = layoutManager; 559aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar mWidthMultiplier = widthMultiplier; 569aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar mHeightMultiplier = heightMultiplier; 579aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar } 589aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar 599aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar @Parameterized.Parameters(name = "{0} w:{2} h:{3}") 609aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar public static List<Object[]> getParams() { 619aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar List<Object[]> params = new ArrayList<>(); 629aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar for (float w : new float[]{.5f, 1f, 2f}) { 639aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar for (float h : new float[]{.5f, 1f, 2f}) { 649aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar params.add( 659aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar new Object[]{"linear layout", new LinearLayoutManager(null), w, h} 669aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar ); 679aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar params.add( 689aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar new Object[]{"grid layout", new GridLayoutManager(null, 3), w, h} 699aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar ); 709aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar params.add( 719aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar new Object[]{"staggered", new StaggeredGridLayoutManager(3, 729aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar StaggeredGridLayoutManager.VERTICAL), w, h} 739aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar ); 749aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar } 759aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar } 769aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar return params; 779aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar } 789aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar 799aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar @Test 809aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar public void testResizeDuringMeasurements() throws Throwable { 819aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar final RecyclerView recyclerView = new RecyclerView(getActivity()); 829aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar recyclerView.setLayoutManager(mLayoutManager); 839aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar recyclerView.setAdapter(new TestAdapter(500)); 849aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar setRecyclerView(recyclerView); 859aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar getInstrumentation().waitForIdleSync(); 869aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar assertThat("Test sanity", recyclerView.getChildCount() > 0, is(true)); 879aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar final int lastPosition = recyclerView.getAdapter().getItemCount() - 1; 889aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar smoothScrollToPosition(lastPosition); 899aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar assertThat("test sanity", recyclerView.findViewHolderForAdapterPosition(lastPosition), 909aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar notNullValue()); 919aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar runTestOnUiThread(new Runnable() { 929aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar @Override 939aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar public void run() { 949aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar int startHeight = recyclerView.getMeasuredHeight(); 959aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar int startWidth = recyclerView.getMeasuredWidth(); 969aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar Map<Integer, Rect> startPositions = capturePositions(recyclerView); 979aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar recyclerView.measure( 989aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar makeMeasureSpec((int) (startWidth * mWidthMultiplier), 999aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar mWidthMultiplier == 1f ? EXACTLY : AT_MOST), 1009aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar makeMeasureSpec((int) (startHeight * mHeightMultiplier), 1019aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar mHeightMultiplier == 1f ? EXACTLY : AT_MOST)); 1029aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar 1039aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar recyclerView.measure( 1049aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar makeMeasureSpec(startWidth, EXACTLY), 1059aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar makeMeasureSpec(startHeight, EXACTLY)); 1069aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar recyclerView.dispatchLayout(); 1079aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar Map<Integer, Rect> endPositions = capturePositions(recyclerView); 1089aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar assertStartItemPositions(startPositions, endPositions); 1099aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar } 1109aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar }); 1119aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar } 1129aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar 1139aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar private void assertStartItemPositions(Map<Integer, Rect> startPositions, 1149aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar Map<Integer, Rect> endPositions) { 1159aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar for (Map.Entry<Integer, Rect> entry : startPositions.entrySet()) { 1169aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar Rect rect = endPositions.get(entry.getKey()); 1179aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar assertThat("view for position " + entry.getKey() + " at" + entry.getValue(), rect, 1189aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar notNullValue()); 1199aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar assertThat("rect for position " + entry.getKey(), entry.getValue(), is(rect)); 1209aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar } 1219aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar } 1229aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar 1239aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar private Map<Integer, Rect> capturePositions(RecyclerView recyclerView) { 1249aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar Map<Integer, Rect> positions = new HashMap<>(); 1259aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar for (int i = 0; i < mLayoutManager.getChildCount(); i++) { 1269aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar View view = mLayoutManager.getChildAt(i); 1279aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar int childAdapterPosition = recyclerView.getChildAdapterPosition(view); 1289aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar Rect outRect = new Rect(); 1299aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar mLayoutManager.getDecoratedBoundsWithMargins(view, outRect); 1309aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar // only record if outRect is visible 1319aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar if (outRect.left >= mRecyclerView.getWidth() || 1329aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar outRect.top >= mRecyclerView.getHeight() || 1339aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar outRect.right < 0 || 1349aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar outRect.bottom < 0) { 1359aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar continue; 1369aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar } 1379aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar positions.put(childAdapterPosition, outRect); 1389aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar } 1399aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar return positions; 1409aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar } 1419aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar}