Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright 2018 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 package androidx.recyclerview.widget;
     18 
     19 import static org.junit.Assert.assertEquals;
     20 import static org.junit.Assert.assertNotNull;
     21 
     22 import android.graphics.Rect;
     23 import android.support.test.filters.MediumTest;
     24 import android.view.View;
     25 
     26 import org.junit.Test;
     27 import org.junit.runner.RunWith;
     28 import org.junit.runners.Parameterized;
     29 
     30 import java.util.ArrayList;
     31 import java.util.HashMap;
     32 import java.util.List;
     33 import java.util.Map;
     34 
     35 /**
     36  * Tests dispatching no-op updates to the GLM and ensures it re-lays out items in the same location
     37  */
     38 @MediumTest
     39 @RunWith(Parameterized.class)
     40 public class GridLayoutManagerNoOpUpdateTest extends BaseGridLayoutManagerTest {
     41     @Parameterized.Parameters(name = "conf:{0},rtl={1}")
     42     public static List<Object[]> getParams() {
     43         List<Object[]> result = new ArrayList<>();
     44         for (BaseGridLayoutManagerTest.Config config : createBaseVariations()) {
     45             result.add(new Object[]{
     46                     config,
     47                     true
     48             });
     49             result.add(new Object[]{
     50                     config,
     51                     false
     52             });
     53         }
     54         return result;
     55     }
     56 
     57     private final Config mConfig;
     58     private final boolean mRtl;
     59 
     60     public GridLayoutManagerNoOpUpdateTest(Config config, boolean rtl) {
     61         mConfig = config;
     62         mRtl = rtl;
     63     }
     64 
     65     @Test
     66     public void rtlChanges() throws Throwable {
     67         RecyclerView rv = createRecyclerView();
     68         mGlm.setFakeRtl(mRtl);
     69         waitForFirstLayout(rv);
     70         Map<Long, Rect> before = takeSnapshot();
     71 
     72         View chosen = mGlm.findViewByPosition(1);
     73         assertNotNull("test sanity", chosen);
     74         mGlm.expectLayout(2);
     75         mAdapter.changeAndNotify(1, 1);
     76         mGlm.waitForLayout(2);
     77         Map<Long, Rect> after = takeSnapshot();
     78         assertSnapshotsEqual(before, after);
     79     }
     80 
     81     private void assertSnapshotsEqual(Map<Long, Rect> before, Map<Long, Rect> after) {
     82         for (Map.Entry<Long, Rect> entry : before.entrySet()) {
     83             Rect newPosition = after.get(entry.getKey());
     84             assertNotNull("cannot find " + entry.getKey() + " in after map", newPosition);
     85             assertEquals("position should be the same", entry.getValue(), newPosition);
     86         }
     87         assertEquals("visible view count should be equal", before.size(), after.size());
     88     }
     89 
     90     private Map<Long, Rect> takeSnapshot() {
     91         Rect rvBounds = new Rect();
     92         if (mRecyclerView.getClipToPadding()) {
     93             rvBounds.set(mRecyclerView.getPaddingLeft(), mRecyclerView.getPaddingTop(),
     94                     mRecyclerView.getWidth() - mRecyclerView.getPaddingRight(),
     95                     mRecyclerView.getHeight() - mRecyclerView.getPaddingBottom());
     96         } else {
     97             rvBounds.set(0, 0, mRecyclerView.getWidth(), mRecyclerView.getHeight());
     98         }
     99         Map<Long, Rect> positionMap = new HashMap<>();
    100         for (int i = 0; i < mGlm.getChildCount(); i++) {
    101             View child = mGlm.getChildAt(i);
    102             Rect childBounds = getChildBounds(mRecyclerView, child, true);
    103             long id = mRecyclerView.getChildViewHolder(child).getItemId();
    104             if (rvBounds.intersect(childBounds)) {
    105                 positionMap.put(id, childBounds);
    106             }
    107         }
    108         return positionMap;
    109     }
    110 
    111     private Rect getChildBounds(RecyclerView recyclerView, View child, boolean offset) {
    112         RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
    113         RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
    114         Rect rect = new Rect(layoutManager.getDecoratedLeft(child) - lp.leftMargin,
    115                 layoutManager.getDecoratedTop(child) - lp.topMargin,
    116                 layoutManager.getDecoratedRight(child) + lp.rightMargin,
    117                 layoutManager.getDecoratedBottom(child) + lp.bottomMargin);
    118         return rect;
    119     }
    120 
    121     private RecyclerView createRecyclerView() throws Throwable {
    122         GridTestAdapter adapter = new GridTestAdapter(mConfig.mItemCount);
    123         adapter.setHasStableIds(true);
    124         return setupBasic(mConfig, adapter);
    125     }
    126 }
    127