12d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar/* 22d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Copyright (C) 2014 The Android Open Source Project 32d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * 42d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License"); 52d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * you may not use this file except in compliance with the License. 62d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * You may obtain a copy of the License at 72d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * 82d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * http://www.apache.org/licenses/LICENSE-2.0 92d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * 102d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Unless required by applicable law or agreed to in writing, software 112d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS, 122d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * See the License for the specific language governing permissions and 142d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * limitations under the License. 152d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 162d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 172d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyarpackage android.support.v7.widget; 182d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 198e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikasimport static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP; 206b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyarimport static android.support.v7.widget.LayoutState.ITEM_DIRECTION_HEAD; 216b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyarimport static android.support.v7.widget.LayoutState.ITEM_DIRECTION_TAIL; 226b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyarimport static android.support.v7.widget.LayoutState.LAYOUT_END; 236b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyarimport static android.support.v7.widget.LayoutState.LAYOUT_START; 246b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyarimport static android.support.v7.widget.RecyclerView.NO_POSITION; 256b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyar 262d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyarimport android.content.Context; 272d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyarimport android.graphics.PointF; 282d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyarimport android.graphics.Rect; 292d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyarimport android.os.Parcel; 302d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyarimport android.os.Parcelable; 31f1811618b8c1a58374da7eb6093038d80c0ede52Yigit Boyarimport android.support.annotation.NonNull; 32f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyarimport android.support.annotation.Nullable; 33c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viveretteimport android.support.annotation.RestrictTo; 342d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyarimport android.support.v4.view.ViewCompat; 35a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; 362d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyarimport android.util.AttributeSet; 372d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyarimport android.util.Log; 382d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyarimport android.view.View; 392d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyarimport android.view.ViewGroup; 40a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.view.accessibility.AccessibilityEvent; 412d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 422d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyarimport java.util.ArrayList; 432d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyarimport java.util.Arrays; 442d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyarimport java.util.BitSet; 45d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyarimport java.util.List; 462d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 472d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar/** 482d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * A LayoutManager that lays out children in a staggered grid formation. 492d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * It supports horizontal & vertical layout as well as an ability to layout children in reverse. 502d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * <p> 512d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Staggered grids are likely to have gaps at the edges of the layout. To avoid these gaps, 522d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * StaggeredGridLayoutManager can offset spans independently or move items between spans. You can 532d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * control this behavior via {@link #setGapStrategy(int)}. 542d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 55c587f7dba5a337169e854e235da59f595255d6ccAga Madurskapublic class StaggeredGridLayoutManager extends RecyclerView.LayoutManager implements 56c587f7dba5a337169e854e235da59f595255d6ccAga Madurska RecyclerView.SmoothScroller.ScrollVectorProvider { 572d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 58ec0c39bc2e86bb08f238956ebb744f54c46c3815Aurimas Liutikas private static final String TAG = "StaggeredGridLayoutManager"; 592d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 603a500f61a8bdf48904f380f2d4925fe420d18ce7Aurimas Liutikas static final boolean DEBUG = false; 612d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 6203a57e1f233833f7d5706bfacb0d5c84d4a039e9Yigit Boyar public static final int HORIZONTAL = OrientationHelper.HORIZONTAL; 6303a57e1f233833f7d5706bfacb0d5c84d4a039e9Yigit Boyar 6403a57e1f233833f7d5706bfacb0d5c84d4a039e9Yigit Boyar public static final int VERTICAL = OrientationHelper.VERTICAL; 6503a57e1f233833f7d5706bfacb0d5c84d4a039e9Yigit Boyar 662d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 67d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * Does not do anything to hide gaps. 682d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 692d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public static final int GAP_HANDLING_NONE = 0; 702d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 71d805095048f6be52cddbd572ee343c4639ba8187Alan Viverette /** 72d805095048f6be52cddbd572ee343c4639ba8187Alan Viverette * @deprecated No longer supported. 73d805095048f6be52cddbd572ee343c4639ba8187Alan Viverette */ 74f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar @SuppressWarnings("unused") 75d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar @Deprecated 762d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public static final int GAP_HANDLING_LAZY = 1; 772d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 782d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 79d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * When scroll state is changed to {@link RecyclerView#SCROLL_STATE_IDLE}, StaggeredGrid will 80d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * check if there are gaps in the because of full span items. If it finds, it will re-layout 81d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * and move items to correct positions with animations. 822d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * <p> 832d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * For example, if LayoutManager ends up with the following layout due to adapter changes: 84115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar * <pre> 852d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * AAA 862d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * _BC 872d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * DDD 88115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar * </pre> 89115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar * <p> 902d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * It will animate to the following state: 91115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar * <pre> 922d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * AAA 932d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * BC_ 942d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * DDD 95115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar * </pre> 962d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 972d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public static final int GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS = 2; 982d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 993a500f61a8bdf48904f380f2d4925fe420d18ce7Aurimas Liutikas static final int INVALID_OFFSET = Integer.MIN_VALUE; 100f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar /** 101f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar * While trying to find next view to focus, LayoutManager will not try to scroll more 102f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar * than this factor times the total space of the list. If layout is vertical, total space is the 103f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar * height minus padding, if layout is horizontal, total space is the width minus padding. 104f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar */ 105f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar private static final float MAX_SCROLL_FACTOR = 1 / 3f; 1062d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 1072d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 1082d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Number of spans 1092d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 1102d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar private int mSpanCount = -1; 1112d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 1123a500f61a8bdf48904f380f2d4925fe420d18ce7Aurimas Liutikas Span[] mSpans; 1132d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 1142d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 1152d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Primary orientation is the layout's orientation, secondary orientation is the orientation 1162d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * for spans. Having both makes code much cleaner for calculations. 1172d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 118f1811618b8c1a58374da7eb6093038d80c0ede52Yigit Boyar @NonNull 1192d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar OrientationHelper mPrimaryOrientation; 120f1811618b8c1a58374da7eb6093038d80c0ede52Yigit Boyar @NonNull 1212d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar OrientationHelper mSecondaryOrientation; 1222d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 1232d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar private int mOrientation; 1242d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 1252d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 1262d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * The width or height per span, depending on the orientation. 1272d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 1282d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar private int mSizePerSpan; 1292d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 130f1811618b8c1a58374da7eb6093038d80c0ede52Yigit Boyar @NonNull 131f1811618b8c1a58374da7eb6093038d80c0ede52Yigit Boyar private final LayoutState mLayoutState; 1322d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 1333a500f61a8bdf48904f380f2d4925fe420d18ce7Aurimas Liutikas boolean mReverseLayout = false; 1342d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 1352d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 1362d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Aggregated reverse layout value that takes RTL into account. 1372d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 138d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar boolean mShouldReverseLayout = false; 1392d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 1402d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 1412d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Temporary variable used during fill method to check which spans needs to be filled. 1422d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 1432d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar private BitSet mRemainingSpans; 1442d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 1452d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 1462d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * When LayoutManager needs to scroll to a position, it sets this variable and requests a 1472d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * layout which will check this variable and re-layout accordingly. 1482d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 149d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int mPendingScrollPosition = NO_POSITION; 1502d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 1512d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 1522d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Used to keep the offset value when {@link #scrollToPositionWithOffset(int, int)} is 1532d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * called. 1542d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 1556e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar int mPendingScrollPositionOffset = INVALID_OFFSET; 1562d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 1572d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 1582d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Keeps the mapping between the adapter positions and spans. This is necessary to provide 1592d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * a consistent experience when user scrolls the list. 1602d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 1612d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar LazySpanLookup mLazySpanLookup = new LazySpanLookup(); 1622d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 1632d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 1642d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * how we handle gaps in UI. 1652d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 1662d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar private int mGapStrategy = GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS; 1672d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 1682d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 1692d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Saved state needs this information to properly layout on restore. 1702d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 1712d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar private boolean mLastLayoutFromEnd; 1722d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 1732d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 174d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * Saved state and onLayout needs this information to re-layout properly 175d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar */ 176d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar private boolean mLastLayoutRTL; 177d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 178d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar /** 1792d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * SavedState is not handled until a layout happens. This is where we keep it until next 1802d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * layout. 1812d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 1822d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar private SavedState mPendingSavedState; 1832d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 1842d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 185d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * Re-used measurement specs. updated by onLayout. 1862d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 1874143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar private int mFullSizeSpec; 188d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 189d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar /** 190c67491396ca2989bd162bd2b243c45071e4b2e6eYigit Boyar * Re-used rectangle to get child decor offsets. 191c67491396ca2989bd162bd2b243c45071e4b2e6eYigit Boyar */ 192c67491396ca2989bd162bd2b243c45071e4b2e6eYigit Boyar private final Rect mTmpRect = new Rect(); 193c67491396ca2989bd162bd2b243c45071e4b2e6eYigit Boyar 194c67491396ca2989bd162bd2b243c45071e4b2e6eYigit Boyar /** 195d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * Re-used anchor info. 196d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar */ 197d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar private final AnchorInfo mAnchorInfo = new AnchorInfo(); 198d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 199d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar /** 200d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * If a full span item is invalid / or created in reverse direction; it may create gaps in 201d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * the UI. While laying out, if such case is detected, we set this flag. 202d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * <p> 203d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * After scrolling stops, we check this flag and if it is set, re-layout. 204d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar */ 205d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar private boolean mLaidOutInvalidFullSpan = false; 206d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 207d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar /** 208d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * Works the same way as {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)}. 209d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * see {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)} 210d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar */ 211d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar private boolean mSmoothScrollbarEnabled = true; 212d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 213945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik /** 2141e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik * Temporary array used (solely in {@link #collectAdjacentPrefetchPositions}) for stashing and 2151e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik * sorting distances to views being prefetched. 216945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik */ 217945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik private int[] mPrefetchDistances; 218945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 219ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar private final Runnable mCheckForGapsRunnable = new Runnable() { 220d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar @Override 221d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar public void run() { 222d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar checkForGaps(); 223d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 224d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar }; 2252d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 2262d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 2270194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta * Constructor used when layout manager is set in XML by RecyclerView attribute 2280194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta * "layoutManager". Defaults to single column and vertical. 2290194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta */ 230f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar @SuppressWarnings("unused") 2310194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta public StaggeredGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, 2320194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta int defStyleRes) { 2330194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta Properties properties = getProperties(context, attrs, defStyleAttr, defStyleRes); 2340194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta setOrientation(properties.orientation); 2350194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta setSpanCount(properties.spanCount); 2360194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta setReverseLayout(properties.reverseLayout); 2374143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar setAutoMeasureEnabled(mGapStrategy != GAP_HANDLING_NONE); 238f1811618b8c1a58374da7eb6093038d80c0ede52Yigit Boyar mLayoutState = new LayoutState(); 239f1811618b8c1a58374da7eb6093038d80c0ede52Yigit Boyar createOrientationHelpers(); 2400194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta } 2410194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta 2420194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta /** 2432d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Creates a StaggeredGridLayoutManager with given parameters. 2442d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * 2452d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * @param spanCount If orientation is vertical, spanCount is number of columns. If 2462d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * orientation is horizontal, spanCount is number of rows. 2479bea36cf2e318e9b729ddc62d855cd0f93bc3866Yigit Boyar * @param orientation {@link #VERTICAL} or {@link #HORIZONTAL} 2482d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 2492d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public StaggeredGridLayoutManager(int spanCount, int orientation) { 2502d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mOrientation = orientation; 2512d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar setSpanCount(spanCount); 2524143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar setAutoMeasureEnabled(mGapStrategy != GAP_HANDLING_NONE); 253f1811618b8c1a58374da7eb6093038d80c0ede52Yigit Boyar mLayoutState = new LayoutState(); 254f1811618b8c1a58374da7eb6093038d80c0ede52Yigit Boyar createOrientationHelpers(); 255f1811618b8c1a58374da7eb6093038d80c0ede52Yigit Boyar } 256f1811618b8c1a58374da7eb6093038d80c0ede52Yigit Boyar 257f1811618b8c1a58374da7eb6093038d80c0ede52Yigit Boyar private void createOrientationHelpers() { 258f1811618b8c1a58374da7eb6093038d80c0ede52Yigit Boyar mPrimaryOrientation = OrientationHelper.createOrientationHelper(this, mOrientation); 259f1811618b8c1a58374da7eb6093038d80c0ede52Yigit Boyar mSecondaryOrientation = OrientationHelper 260f1811618b8c1a58374da7eb6093038d80c0ede52Yigit Boyar .createOrientationHelper(this, 1 - mOrientation); 2612d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 2622d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 263d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar /** 264d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * Checks for gaps in the UI that may be caused by adapter changes. 265d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * <p> 266d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * When a full span item is laid out in reverse direction, it sets a flag which we check when 267d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * scroll is stopped (or re-layout happens) and re-layout after first valid item. 268d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar */ 2693a500f61a8bdf48904f380f2d4925fe420d18ce7Aurimas Liutikas boolean checkForGaps() { 270ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar if (getChildCount() == 0 || mGapStrategy == GAP_HANDLING_NONE || !isAttachedToWindow()) { 271ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar return false; 272d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 273d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final int minPos, maxPos; 274d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mShouldReverseLayout) { 275d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar minPos = getLastChildPosition(); 276d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar maxPos = getFirstChildPosition(); 277d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else { 278d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar minPos = getFirstChildPosition(); 279d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar maxPos = getLastChildPosition(); 280d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 281d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (minPos == 0) { 282d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar View gapView = hasGapsToFix(); 283d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (gapView != null) { 284d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mLazySpanLookup.clear(); 285d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar requestSimpleAnimationsInNextLayout(); 286d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar requestLayout(); 287ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar return true; 288d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 289d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 290d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (!mLaidOutInvalidFullSpan) { 291ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar return false; 292d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 293d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int invalidGapDir = mShouldReverseLayout ? LAYOUT_START : LAYOUT_END; 294d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final LazySpanLookup.FullSpanItem invalidFsi = mLazySpanLookup 295f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar .getFirstFullSpanItemInRange(minPos, maxPos + 1, invalidGapDir, true); 296d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (invalidFsi == null) { 297d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mLaidOutInvalidFullSpan = false; 298d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mLazySpanLookup.forceInvalidateAfter(maxPos + 1); 299ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar return false; 300d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 301d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final LazySpanLookup.FullSpanItem validFsi = mLazySpanLookup 302d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar .getFirstFullSpanItemInRange(minPos, invalidFsi.mPosition, 303f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar invalidGapDir * -1, true); 304d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (validFsi == null) { 305d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mLazySpanLookup.forceInvalidateAfter(invalidFsi.mPosition); 306d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else { 307d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mLazySpanLookup.forceInvalidateAfter(validFsi.mPosition + 1); 308d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 309d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar requestSimpleAnimationsInNextLayout(); 310d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar requestLayout(); 311ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar return true; 312d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 313d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 3142d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar @Override 3152d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public void onScrollStateChanged(int state) { 316d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (state == RecyclerView.SCROLL_STATE_IDLE) { 317d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar checkForGaps(); 318d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 319d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 320d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 321d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar @Override 322d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) { 323ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar removeCallbacks(mCheckForGapsRunnable); 324d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar for (int i = 0; i < mSpanCount; i++) { 325d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mSpans[i].clear(); 326d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 327a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar // SGLM will require fresh layout call to recover state after detach 328a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar view.requestLayout(); 329d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 330d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 331d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar /** 332d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * Checks for gaps if we've reached to the top of the list. 333d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * <p> 334d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * Intermediate gaps created by full span items are tracked via mLaidOutInvalidFullSpan field. 335d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar */ 336d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar View hasGapsToFix() { 337d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int startChildIndex = 0; 338d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int endChildIndex = getChildCount() - 1; 339d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar BitSet mSpansToCheck = new BitSet(mSpanCount); 340d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mSpansToCheck.set(0, mSpanCount, true); 341d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 342d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final int firstChildIndex, childLimit; 343d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final int preferredSpanDir = mOrientation == VERTICAL && isLayoutRTL() ? 1 : -1; 344d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 345d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mShouldReverseLayout) { 346ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar firstChildIndex = endChildIndex; 347d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar childLimit = startChildIndex - 1; 348d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else { 349d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar firstChildIndex = startChildIndex; 350ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar childLimit = endChildIndex + 1; 351d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 352d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final int nextChildDiff = firstChildIndex < childLimit ? 1 : -1; 353d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar for (int i = firstChildIndex; i != childLimit; i += nextChildDiff) { 354d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar View child = getChildAt(i); 355d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar LayoutParams lp = (LayoutParams) child.getLayoutParams(); 356d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mSpansToCheck.get(lp.mSpan.mIndex)) { 357d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (checkSpanForGap(lp.mSpan)) { 358d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return child; 359d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 360d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mSpansToCheck.clear(lp.mSpan.mIndex); 361d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 362d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (lp.mFullSpan) { 363d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar continue; // quick reject 364d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 365d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 366d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (i + nextChildDiff != childLimit) { 367d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar View nextChild = getChildAt(i + nextChildDiff); 368d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar boolean compareSpans = false; 369d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mShouldReverseLayout) { 370d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // ensure child's end is below nextChild's end 371d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int myEnd = mPrimaryOrientation.getDecoratedEnd(child); 372d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int nextEnd = mPrimaryOrientation.getDecoratedEnd(nextChild); 373d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (myEnd < nextEnd) { 3741e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas return child; //i should have a better position 375d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else if (myEnd == nextEnd) { 376d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar compareSpans = true; 377d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 378d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else { 379d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int myStart = mPrimaryOrientation.getDecoratedStart(child); 380d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int nextStart = mPrimaryOrientation.getDecoratedStart(nextChild); 381d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (myStart > nextStart) { 3821e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas return child; //i should have a better position 383d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else if (myStart == nextStart) { 384d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar compareSpans = true; 385d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 386d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 387d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (compareSpans) { 388d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // equal, check span indices. 389d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar LayoutParams nextLp = (LayoutParams) nextChild.getLayoutParams(); 390d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (lp.mSpan.mIndex - nextLp.mSpan.mIndex < 0 != preferredSpanDir < 0) { 391d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return child; 392d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 393d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 3942d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 3952d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 396d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // everything looks good 397d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return null; 398d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 399d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 400d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar private boolean checkSpanForGap(Span span) { 401d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mShouldReverseLayout) { 402d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (span.getEndLine() < mPrimaryOrientation.getEndAfterPadding()) { 4034143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar // if it is full span, it is OK 4044143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final View endView = span.mViews.get(span.mViews.size() - 1); 4054143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final LayoutParams lp = span.getLayoutParams(endView); 4064143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar return !lp.mFullSpan; 407d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 408d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else if (span.getStartLine() > mPrimaryOrientation.getStartAfterPadding()) { 4094143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar // if it is full span, it is OK 4104143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final View startView = span.mViews.get(0); 4114143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final LayoutParams lp = span.getLayoutParams(startView); 4124143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar return !lp.mFullSpan; 413d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 414d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return false; 4152d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 4162d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 4172d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 4182d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Sets the number of spans for the layout. This will invalidate all of the span assignments 4192d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * for Views. 4202d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * <p> 4212d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Calling this method will automatically result in a new layout request unless the spanCount 4222d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * parameter is equal to current span count. 4232d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * 4242d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * @param spanCount Number of spans to layout 4252d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 4262d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public void setSpanCount(int spanCount) { 4270bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar assertNotInLayoutOrScroll(null); 4282d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (spanCount != mSpanCount) { 4292d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar invalidateSpanAssignments(); 4302d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mSpanCount = spanCount; 4312d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mRemainingSpans = new BitSet(mSpanCount); 4322d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mSpans = new Span[mSpanCount]; 4332d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (int i = 0; i < mSpanCount; i++) { 4342d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mSpans[i] = new Span(i); 4352d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 4362d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar requestLayout(); 4372d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 4382d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 4392d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 4402d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 4412d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Sets the orientation of the layout. StaggeredGridLayoutManager will do its best to keep 442d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * scroll position if this method is called after views are laid out. 4432d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * 444d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * @param orientation {@link #HORIZONTAL} or {@link #VERTICAL} 4452d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 4462d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public void setOrientation(int orientation) { 4472d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (orientation != HORIZONTAL && orientation != VERTICAL) { 4482d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar throw new IllegalArgumentException("invalid orientation."); 4492d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 4500bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar assertNotInLayoutOrScroll(null); 4512d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (orientation == mOrientation) { 4522d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return; 4532d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 4542d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mOrientation = orientation; 455f1811618b8c1a58374da7eb6093038d80c0ede52Yigit Boyar OrientationHelper tmp = mPrimaryOrientation; 456f1811618b8c1a58374da7eb6093038d80c0ede52Yigit Boyar mPrimaryOrientation = mSecondaryOrientation; 457f1811618b8c1a58374da7eb6093038d80c0ede52Yigit Boyar mSecondaryOrientation = tmp; 4582d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar requestLayout(); 4592d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 4602d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 4612d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 4622d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Sets whether LayoutManager should start laying out items from the end of the UI. The order 4632d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * items are traversed is not affected by this call. 4642d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * <p> 465d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * For vertical layout, if it is set to <code>true</code>, first item will be at the bottom of 466d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * the list. 4672d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * <p> 4682d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * For horizontal layouts, it depends on the layout direction. 4692d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * When set to true, If {@link RecyclerView} is LTR, than it will layout from RTL, if 4702d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * {@link RecyclerView}} is RTL, it will layout from LTR. 4712d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * 4722d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * @param reverseLayout Whether layout should be in reverse or not 4732d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 4742d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public void setReverseLayout(boolean reverseLayout) { 4750bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar assertNotInLayoutOrScroll(null); 4762d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mPendingSavedState != null && mPendingSavedState.mReverseLayout != reverseLayout) { 4772d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mPendingSavedState.mReverseLayout = reverseLayout; 4782d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 4792d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mReverseLayout = reverseLayout; 4802d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar requestLayout(); 4812d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 4822d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 4832d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 4842d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Returns the current gap handling strategy for StaggeredGridLayoutManager. 4852d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * <p> 486d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * Staggered grid may have gaps in the layout due to changes in the adapter. To avoid gaps, 487d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * StaggeredGridLayoutManager provides 2 options. Check {@link #GAP_HANDLING_NONE} and 488d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * {@link #GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS} for details. 4892d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * <p> 4902d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * By default, StaggeredGridLayoutManager uses {@link #GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS}. 4912d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * 4922d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * @return Current gap handling strategy. 4932d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * @see #setGapStrategy(int) 4942d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * @see #GAP_HANDLING_NONE 4952d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * @see #GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS 4962d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 4972d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public int getGapStrategy() { 4982d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return mGapStrategy; 4992d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 5002d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 5012d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 5022d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Sets the gap handling strategy for StaggeredGridLayoutManager. If the gapStrategy parameter 5032d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * is different than the current strategy, calling this method will trigger a layout request. 5042d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * 505d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * @param gapStrategy The new gap handling strategy. Should be 506d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * {@link #GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS} or {@link 507d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * #GAP_HANDLING_NONE}. 5082d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * @see #getGapStrategy() 5092d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 5102d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public void setGapStrategy(int gapStrategy) { 5110bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar assertNotInLayoutOrScroll(null); 5122d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (gapStrategy == mGapStrategy) { 5132d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return; 5142d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 5151e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas if (gapStrategy != GAP_HANDLING_NONE 5161e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas && gapStrategy != GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS) { 5172d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar throw new IllegalArgumentException("invalid gap strategy. Must be GAP_HANDLING_NONE " 518d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar + "or GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS"); 5192d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 5202d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mGapStrategy = gapStrategy; 5214143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar setAutoMeasureEnabled(mGapStrategy != GAP_HANDLING_NONE); 5222d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar requestLayout(); 5232d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 5242d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 5250bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar @Override 5260bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar public void assertNotInLayoutOrScroll(String message) { 5270bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar if (mPendingSavedState == null) { 5280bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar super.assertNotInLayoutOrScroll(message); 5290bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar } 5300bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar } 5310bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar 5322d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 5332d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Returns the number of spans laid out by StaggeredGridLayoutManager. 5342d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * 5352d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * @return Number of spans in the layout 5362d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 5372d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public int getSpanCount() { 5382d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return mSpanCount; 5392d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 5402d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 5412d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 5422d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * For consistency, StaggeredGridLayoutManager keeps a mapping between spans and items. 5432d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * <p> 5442d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * If you need to cancel current assignments, you can call this method which will clear all 5452d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * assignments and request a new layout. 5462d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 5472d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public void invalidateSpanAssignments() { 5482d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mLazySpanLookup.clear(); 5492d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar requestLayout(); 5502d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 5512d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 5522d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 5532d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Calculates the views' layout order. (e.g. from end to start or start to end) 5542d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * RTL layout support is applied automatically. So if layout is RTL and 5552d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * {@link #getReverseLayout()} is {@code true}, elements will be laid out starting from left. 5562d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 5572d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar private void resolveShouldLayoutReverse() { 5582d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar // A == B is the same result, but we rather keep it readable 5592d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mOrientation == VERTICAL || !isLayoutRTL()) { 5602d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mShouldReverseLayout = mReverseLayout; 5612d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else { 5622d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mShouldReverseLayout = !mReverseLayout; 5632d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 5642d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 5652d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 566d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar boolean isLayoutRTL() { 5672d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL; 5682d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 5692d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 5702d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 5712d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Returns whether views are laid out in reverse order or not. 5722d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * <p> 5732d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Not that this value is not affected by RecyclerView's layout direction. 5742d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * 5752d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * @return True if layout is reversed, false otherwise 5762d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * @see #setReverseLayout(boolean) 5772d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 5782d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public boolean getReverseLayout() { 5792d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return mReverseLayout; 5802d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 5814143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 5824143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar @Override 5834143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) { 5844143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar // we don't like it to wrap content in our non-scroll direction. 5854143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final int width, height; 5866b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyar final int horizontalPadding = getPaddingLeft() + getPaddingRight(); 5876b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyar final int verticalPadding = getPaddingTop() + getPaddingBottom(); 5884143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (mOrientation == VERTICAL) { 5896b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyar final int usedHeight = childrenBounds.height() + verticalPadding; 5904143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar height = chooseSize(hSpec, usedHeight, getMinimumHeight()); 5916b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyar width = chooseSize(wSpec, mSizePerSpan * mSpanCount + horizontalPadding, 5926b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyar getMinimumWidth()); 5934143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } else { 5946b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyar final int usedWidth = childrenBounds.width() + horizontalPadding; 5954143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar width = chooseSize(wSpec, usedWidth, getMinimumWidth()); 5966b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyar height = chooseSize(hSpec, mSizePerSpan * mSpanCount + verticalPadding, 5976b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyar getMinimumHeight()); 5984143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 5994143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar setMeasuredDimension(width, height); 6004143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 6014143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 6022d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar @Override 6032d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 6044143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar onLayoutChildren(recycler, state, true); 6054143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 6064143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 6074143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 6084143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar private void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state, 6094143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar boolean shouldCheckForGaps) { 610d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final AnchorInfo anchorInfo = mAnchorInfo; 6113d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev if (mPendingSavedState != null || mPendingScrollPosition != NO_POSITION) { 6123d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev if (state.getItemCount() == 0) { 6133d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev removeAndRecycleAllViews(recycler); 6149aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar anchorInfo.reset(); 6153d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev return; 6163d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev } 6173d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev } 6183d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev 6191e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas boolean recalculateAnchor = !anchorInfo.mValid || mPendingScrollPosition != NO_POSITION 6201e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas || mPendingSavedState != null; 62187b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar if (recalculateAnchor) { 6229aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar anchorInfo.reset(); 6239aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar if (mPendingSavedState != null) { 6249aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar applyPendingSavedState(anchorInfo); 6259aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar } else { 6269aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar resolveShouldLayoutReverse(); 6279aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar anchorInfo.mLayoutFromEnd = mShouldReverseLayout; 6289aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar } 6299aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar updateAnchorInfoForLayout(state, anchorInfo); 6309aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar anchorInfo.mValid = true; 6319aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar } 6328cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar if (mPendingSavedState == null && mPendingScrollPosition == NO_POSITION) { 6331e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas if (anchorInfo.mLayoutFromEnd != mLastLayoutFromEnd 6341e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas || isLayoutRTL() != mLastLayoutRTL) { 635d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mLazySpanLookup.clear(); 636d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar anchorInfo.mInvalidateOffsets = true; 637d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 6382d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 639d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 6401e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas if (getChildCount() > 0 && (mPendingSavedState == null 6411e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas || mPendingSavedState.mSpanOffsetsSize < 1)) { 642d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (anchorInfo.mInvalidateOffsets) { 6432d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (int i = 0; i < mSpanCount; i++) { 6442d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar // Scroll to position is set, clear. 6452d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mSpans[i].clear(); 646d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (anchorInfo.mOffset != INVALID_OFFSET) { 647d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mSpans[i].setLine(anchorInfo.mOffset); 6482d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 6492d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 6502d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else { 65187b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar if (recalculateAnchor || mAnchorInfo.mSpanReferenceLines == null) { 65287b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar for (int i = 0; i < mSpanCount; i++) { 65387b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar mSpans[i].cacheReferenceLineAndClear(mShouldReverseLayout, 65487b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar anchorInfo.mOffset); 65587b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar } 65687b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar mAnchorInfo.saveSpanReferenceLines(mSpans); 65787b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar } else { 65887b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar for (int i = 0; i < mSpanCount; i++) { 65987b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar final Span span = mSpans[i]; 66087b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar span.clear(); 66187b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar span.setLine(mAnchorInfo.mSpanReferenceLines[i]); 66287b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar } 6632d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 6642d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 6652d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 6662d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar detachAndScrapAttachedViews(recycler); 6674143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar mLayoutState.mRecycle = false; 668d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mLaidOutInvalidFullSpan = false; 6694143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar updateMeasureSpecs(mSecondaryOrientation.getTotalSpace()); 670e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar updateLayoutState(anchorInfo.mPosition, state); 671d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (anchorInfo.mLayoutFromEnd) { 672d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // Layout start. 673e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar setLayoutStateDirection(LAYOUT_START); 674d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar fill(recycler, mLayoutState, state); 675d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // Layout end. 676e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar setLayoutStateDirection(LAYOUT_END); 677e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar mLayoutState.mCurrentPosition = anchorInfo.mPosition + mLayoutState.mItemDirection; 678d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar fill(recycler, mLayoutState, state); 679d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else { 680d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // Layout end. 681e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar setLayoutStateDirection(LAYOUT_END); 682d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar fill(recycler, mLayoutState, state); 683d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // Layout start. 684e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar setLayoutStateDirection(LAYOUT_START); 685e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar mLayoutState.mCurrentPosition = anchorInfo.mPosition + mLayoutState.mItemDirection; 686d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar fill(recycler, mLayoutState, state); 6872d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 6882d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 6894143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar repositionToWrapContentIfNecessary(); 6904143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 6912d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (getChildCount() > 0) { 6922d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mShouldReverseLayout) { 6932d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar fixEndGap(recycler, state, true); 6942d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar fixStartGap(recycler, state, false); 6952d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else { 6962d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar fixStartGap(recycler, state, true); 6972d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar fixEndGap(recycler, state, false); 6982d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 6992d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 7004143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar boolean hasGaps = false; 7014143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (shouldCheckForGaps && !state.isPreLayout()) { 702ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar final boolean needToCheckForGaps = mGapStrategy != GAP_HANDLING_NONE 703ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar && getChildCount() > 0 704ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar && (mLaidOutInvalidFullSpan || hasGapsToFix() != null); 705ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar if (needToCheckForGaps) { 706ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar removeCallbacks(mCheckForGapsRunnable); 7074143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (checkForGaps()) { 7084143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar hasGaps = true; 7094143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 710d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 7116e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 7129aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar if (state.isPreLayout()) { 7139aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar mAnchorInfo.reset(); 7149aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar } 715d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mLastLayoutFromEnd = anchorInfo.mLayoutFromEnd; 716d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mLastLayoutRTL = isLayoutRTL(); 7174143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (hasGaps) { 7189aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar mAnchorInfo.reset(); 7194143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar onLayoutChildren(recycler, state, false); 7204143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 7214143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 7224143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 7238cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar @Override 7248cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar public void onLayoutCompleted(RecyclerView.State state) { 7258cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar super.onLayoutCompleted(state); 7268cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar mPendingScrollPosition = NO_POSITION; 7278cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar mPendingScrollPositionOffset = INVALID_OFFSET; 7288cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar mPendingSavedState = null; // we don't need this anymore 7299aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar mAnchorInfo.reset(); 7308cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar } 7318cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar 7324143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar private void repositionToWrapContentIfNecessary() { 7334143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (mSecondaryOrientation.getMode() == View.MeasureSpec.EXACTLY) { 7344143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar return; // nothing to do 7354143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 7364143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar float maxSize = 0; 7374143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final int childCount = getChildCount(); 7381e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas for (int i = 0; i < childCount; i++) { 7394143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar View child = getChildAt(i); 7404143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar float size = mSecondaryOrientation.getDecoratedMeasurement(child); 7414143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (size < maxSize) { 7424143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar continue; 7434143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 7444143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar LayoutParams layoutParams = (LayoutParams) child.getLayoutParams(); 7454143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (layoutParams.isFullSpan()) { 7464143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar size = 1f * size / mSpanCount; 7474143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 7484143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar maxSize = Math.max(maxSize, size); 7494143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 7504143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar int before = mSizePerSpan; 7514143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar int desired = Math.round(maxSize * mSpanCount); 7524143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (mSecondaryOrientation.getMode() == View.MeasureSpec.AT_MOST) { 7534143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar desired = Math.min(desired, mSecondaryOrientation.getTotalSpace()); 7544143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 7554143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar updateMeasureSpecs(desired); 7564143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (mSizePerSpan == before) { 7574143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar return; // nothing has changed 7584143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 7591e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas for (int i = 0; i < childCount; i++) { 7604143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar View child = getChildAt(i); 7614143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 7624143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (lp.mFullSpan) { 7634143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar continue; 7644143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 7654143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (isLayoutRTL() && mOrientation == VERTICAL) { 7664143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar int newOffset = -(mSpanCount - 1 - lp.mSpan.mIndex) * mSizePerSpan; 7674143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar int prevOffset = -(mSpanCount - 1 - lp.mSpan.mIndex) * before; 7684143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar child.offsetLeftAndRight(newOffset - prevOffset); 7694143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } else { 7704143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar int newOffset = lp.mSpan.mIndex * mSizePerSpan; 7714143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar int prevOffset = lp.mSpan.mIndex * before; 7724143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (mOrientation == VERTICAL) { 7734143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar child.offsetLeftAndRight(newOffset - prevOffset); 7744143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } else { 7754143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar child.offsetTopAndBottom(newOffset - prevOffset); 7764143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 7774143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 7784143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 7792d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 7802d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 781d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar private void applyPendingSavedState(AnchorInfo anchorInfo) { 782d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (DEBUG) { 783d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar Log.d(TAG, "found saved state: " + mPendingSavedState); 7842d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 785d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mPendingSavedState.mSpanOffsetsSize > 0) { 786d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mPendingSavedState.mSpanOffsetsSize == mSpanCount) { 787d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar for (int i = 0; i < mSpanCount; i++) { 788d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mSpans[i].clear(); 789d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int line = mPendingSavedState.mSpanOffsets[i]; 790d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (line != Span.INVALID_LINE) { 791d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mPendingSavedState.mAnchorLayoutFromEnd) { 792d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar line += mPrimaryOrientation.getEndAfterPadding(); 793d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else { 794d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar line += mPrimaryOrientation.getStartAfterPadding(); 795d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 796d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 797d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mSpans[i].setLine(line); 798d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 799d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else { 800d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mPendingSavedState.invalidateSpanInfo(); 801d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mPendingSavedState.mAnchorPosition = mPendingSavedState.mVisibleAnchorPosition; 802d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 803d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 804d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mLastLayoutRTL = mPendingSavedState.mLastLayoutRTL; 805d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar setReverseLayout(mPendingSavedState.mReverseLayout); 806d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar resolveShouldLayoutReverse(); 8072d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 808d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mPendingSavedState.mAnchorPosition != NO_POSITION) { 809d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mPendingScrollPosition = mPendingSavedState.mAnchorPosition; 810d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar anchorInfo.mLayoutFromEnd = mPendingSavedState.mAnchorLayoutFromEnd; 8112d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else { 812d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar anchorInfo.mLayoutFromEnd = mShouldReverseLayout; 8132d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 814d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mPendingSavedState.mSpanLookupSize > 1) { 815d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mLazySpanLookup.mData = mPendingSavedState.mSpanLookup; 816d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mLazySpanLookup.mFullSpanItems = mPendingSavedState.mFullSpanItems; 817d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 818d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 819d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 820d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar void updateAnchorInfoForLayout(RecyclerView.State state, AnchorInfo anchorInfo) { 821d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (updateAnchorFromPendingData(state, anchorInfo)) { 822d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return; 823d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 824d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (updateAnchorFromChildren(state, anchorInfo)) { 825d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return; 826d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 827d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (DEBUG) { 828d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar Log.d(TAG, "Deciding anchor info from fresh state"); 829d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 830d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar anchorInfo.assignCoordinateFromPadding(); 831d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar anchorInfo.mPosition = 0; 832d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 833d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 834d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar private boolean updateAnchorFromChildren(RecyclerView.State state, AnchorInfo anchorInfo) { 835d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // We don't recycle views out of adapter order. This way, we can rely on the first or 836d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // last child as the anchor position. 837d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // Layout direction may change but we should select the child depending on the latest 838d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // layout direction. Otherwise, we'll choose the wrong child. 839d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar anchorInfo.mPosition = mLastLayoutFromEnd 840d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar ? findLastReferenceChildPosition(state.getItemCount()) 841d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar : findFirstReferenceChildPosition(state.getItemCount()); 842d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar anchorInfo.mOffset = INVALID_OFFSET; 843d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return true; 844d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 845d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 846d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar boolean updateAnchorFromPendingData(RecyclerView.State state, AnchorInfo anchorInfo) { 847d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // Validate scroll position if exists. 848d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (state.isPreLayout() || mPendingScrollPosition == NO_POSITION) { 849d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return false; 850d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 851d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // Validate it. 852d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mPendingScrollPosition < 0 || mPendingScrollPosition >= state.getItemCount()) { 853d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mPendingScrollPosition = NO_POSITION; 854d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mPendingScrollPositionOffset = INVALID_OFFSET; 855d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return false; 856d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 857d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 858d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mPendingSavedState == null || mPendingSavedState.mAnchorPosition == NO_POSITION 859d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar || mPendingSavedState.mSpanOffsetsSize < 1) { 860d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // If item is visible, make it fully visible. 861d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final View child = findViewByPosition(mPendingScrollPosition); 862d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (child != null) { 863d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // Use regular anchor position, offset according to pending offset and target 864d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // child 865d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar anchorInfo.mPosition = mShouldReverseLayout ? getLastChildPosition() 866d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar : getFirstChildPosition(); 867d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mPendingScrollPositionOffset != INVALID_OFFSET) { 868d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (anchorInfo.mLayoutFromEnd) { 8691e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas final int target = mPrimaryOrientation.getEndAfterPadding() 8701e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas - mPendingScrollPositionOffset; 871d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar anchorInfo.mOffset = target - mPrimaryOrientation.getDecoratedEnd(child); 872d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else { 8731e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas final int target = mPrimaryOrientation.getStartAfterPadding() 8741e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas + mPendingScrollPositionOffset; 875d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar anchorInfo.mOffset = target - mPrimaryOrientation.getDecoratedStart(child); 876d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 877d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return true; 878d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 879d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 880d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // no offset provided. Decide according to the child location 881d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final int childSize = mPrimaryOrientation.getDecoratedMeasurement(child); 882d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (childSize > mPrimaryOrientation.getTotalSpace()) { 883d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // Item does not fit. Fix depending on layout direction. 884d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar anchorInfo.mOffset = anchorInfo.mLayoutFromEnd 885d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar ? mPrimaryOrientation.getEndAfterPadding() 886d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar : mPrimaryOrientation.getStartAfterPadding(); 887d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return true; 888d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 889d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 890d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final int startGap = mPrimaryOrientation.getDecoratedStart(child) 891d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar - mPrimaryOrientation.getStartAfterPadding(); 892d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (startGap < 0) { 893d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar anchorInfo.mOffset = -startGap; 894d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return true; 895d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 8961e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas final int endGap = mPrimaryOrientation.getEndAfterPadding() 8971e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas - mPrimaryOrientation.getDecoratedEnd(child); 898d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (endGap < 0) { 899d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar anchorInfo.mOffset = endGap; 900d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return true; 9012d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 902d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // child already visible. just layout as usual 903d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar anchorInfo.mOffset = INVALID_OFFSET; 904d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else { 905d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // Child is not visible. Set anchor coordinate depending on in which direction 906d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // child will be visible. 907d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar anchorInfo.mPosition = mPendingScrollPosition; 908d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mPendingScrollPositionOffset == INVALID_OFFSET) { 909d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final int position = calculateScrollDirectionForPosition( 910d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar anchorInfo.mPosition); 911d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar anchorInfo.mLayoutFromEnd = position == LAYOUT_END; 912d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar anchorInfo.assignCoordinateFromPadding(); 913d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else { 914d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar anchorInfo.assignCoordinateFromPadding(mPendingScrollPositionOffset); 915d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 916d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar anchorInfo.mInvalidateOffsets = true; 9172d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 918d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else { 919d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar anchorInfo.mOffset = INVALID_OFFSET; 920d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar anchorInfo.mPosition = mPendingScrollPosition; 921d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 922d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return true; 923d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 924d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 9254143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar void updateMeasureSpecs(int totalSpace) { 9264143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar mSizePerSpan = totalSpace / mSpanCount; 9274143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar //noinspection ResourceType 928d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mFullSizeSpec = View.MeasureSpec.makeMeasureSpec( 9294143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar totalSpace, mSecondaryOrientation.getMode()); 9302d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 9312d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 9322d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar @Override 9332d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public boolean supportsPredictiveItemAnimations() { 9346e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar return mPendingSavedState == null; 9352d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 9362d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 937333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar /** 938333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * Returns the adapter position of the first visible view for each span. 939333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * <p> 940333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * Note that, this value is not affected by layout orientation or item order traversal. 941333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter, 942333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * not in the layout. 943333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * <p> 944333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * If RecyclerView has item decorators, they will be considered in calculations as well. 945333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * <p> 946333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * StaggeredGridLayoutManager may pre-cache some views that are not necessarily visible. Those 947333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * views are ignored in this method. 948333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * 949333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * @param into An array to put the results into. If you don't provide any, LayoutManager will 950333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * create a new one. 951d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * @return The adapter position of the first visible item in each span. If a span does not have 952d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * any items, {@link RecyclerView#NO_POSITION} is returned for that span. 953333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * @see #findFirstCompletelyVisibleItemPositions(int[]) 954333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * @see #findLastVisibleItemPositions(int[]) 955333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar */ 956333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar public int[] findFirstVisibleItemPositions(int[] into) { 957333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar if (into == null) { 958333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar into = new int[mSpanCount]; 959333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } else if (into.length < mSpanCount) { 960333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar throw new IllegalArgumentException("Provided int[]'s size must be more than or equal" 961333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar + " to span count. Expected:" + mSpanCount + ", array size:" + into.length); 962333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } 963d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar for (int i = 0; i < mSpanCount; i++) { 964333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar into[i] = mSpans[i].findFirstVisibleItemPosition(); 965333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } 966333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar return into; 967333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } 968333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar 969333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar /** 970333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * Returns the adapter position of the first completely visible view for each span. 971333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * <p> 972333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * Note that, this value is not affected by layout orientation or item order traversal. 973333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter, 974333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * not in the layout. 975333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * <p> 976333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * If RecyclerView has item decorators, they will be considered in calculations as well. 977333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * <p> 978333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * StaggeredGridLayoutManager may pre-cache some views that are not necessarily visible. Those 979333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * views are ignored in this method. 980333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * 981333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * @param into An array to put the results into. If you don't provide any, LayoutManager will 982333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * create a new one. 983d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * @return The adapter position of the first fully visible item in each span. If a span does 984d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * not have any items, {@link RecyclerView#NO_POSITION} is returned for that span. 985333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * @see #findFirstVisibleItemPositions(int[]) 986333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * @see #findLastCompletelyVisibleItemPositions(int[]) 987333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar */ 988333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar public int[] findFirstCompletelyVisibleItemPositions(int[] into) { 989333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar if (into == null) { 990333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar into = new int[mSpanCount]; 991333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } else if (into.length < mSpanCount) { 992333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar throw new IllegalArgumentException("Provided int[]'s size must be more than or equal" 993333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar + " to span count. Expected:" + mSpanCount + ", array size:" + into.length); 994333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } 995d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar for (int i = 0; i < mSpanCount; i++) { 996333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar into[i] = mSpans[i].findFirstCompletelyVisibleItemPosition(); 997333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } 998333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar return into; 999333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } 1000333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar 1001333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar /** 1002333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * Returns the adapter position of the last visible view for each span. 1003333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * <p> 1004333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * Note that, this value is not affected by layout orientation or item order traversal. 1005333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter, 1006333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * not in the layout. 1007333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * <p> 1008333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * If RecyclerView has item decorators, they will be considered in calculations as well. 1009333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * <p> 1010333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * StaggeredGridLayoutManager may pre-cache some views that are not necessarily visible. Those 1011333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * views are ignored in this method. 1012333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * 1013333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * @param into An array to put the results into. If you don't provide any, LayoutManager will 1014333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * create a new one. 1015d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * @return The adapter position of the last visible item in each span. If a span does not have 1016d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * any items, {@link RecyclerView#NO_POSITION} is returned for that span. 1017333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * @see #findLastCompletelyVisibleItemPositions(int[]) 1018333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * @see #findFirstVisibleItemPositions(int[]) 1019333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar */ 1020333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar public int[] findLastVisibleItemPositions(int[] into) { 1021333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar if (into == null) { 1022333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar into = new int[mSpanCount]; 1023333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } else if (into.length < mSpanCount) { 1024333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar throw new IllegalArgumentException("Provided int[]'s size must be more than or equal" 1025333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar + " to span count. Expected:" + mSpanCount + ", array size:" + into.length); 1026333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } 1027d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar for (int i = 0; i < mSpanCount; i++) { 1028333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar into[i] = mSpans[i].findLastVisibleItemPosition(); 1029333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } 1030333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar return into; 1031333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } 1032333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar 1033333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar /** 1034333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * Returns the adapter position of the last completely visible view for each span. 1035333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * <p> 1036333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * Note that, this value is not affected by layout orientation or item order traversal. 1037333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter, 1038333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * not in the layout. 1039333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * <p> 1040333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * If RecyclerView has item decorators, they will be considered in calculations as well. 1041333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * <p> 1042333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * StaggeredGridLayoutManager may pre-cache some views that are not necessarily visible. Those 1043333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * views are ignored in this method. 1044333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * 1045333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * @param into An array to put the results into. If you don't provide any, LayoutManager will 1046333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * create a new one. 1047d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * @return The adapter position of the last fully visible item in each span. If a span does not 1048d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * have any items, {@link RecyclerView#NO_POSITION} is returned for that span. 1049333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * @see #findFirstCompletelyVisibleItemPositions(int[]) 1050333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * @see #findLastVisibleItemPositions(int[]) 1051333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar */ 1052333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar public int[] findLastCompletelyVisibleItemPositions(int[] into) { 1053333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar if (into == null) { 1054333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar into = new int[mSpanCount]; 1055333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } else if (into.length < mSpanCount) { 1056333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar throw new IllegalArgumentException("Provided int[]'s size must be more than or equal" 1057333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar + " to span count. Expected:" + mSpanCount + ", array size:" + into.length); 1058333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } 1059d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar for (int i = 0; i < mSpanCount; i++) { 1060333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar into[i] = mSpans[i].findLastCompletelyVisibleItemPosition(); 1061333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } 1062333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar return into; 1063333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } 1064333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar 1065d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar @Override 1066d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar public int computeHorizontalScrollOffset(RecyclerView.State state) { 1067d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return computeScrollOffset(state); 1068d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1069d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 1070d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar private int computeScrollOffset(RecyclerView.State state) { 1071d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (getChildCount() == 0) { 1072d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return 0; 1073d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1074d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return ScrollbarHelper.computeScrollOffset(state, mPrimaryOrientation, 1075e5874e666791a58d21eed482fb90e917445da873Chris Craik findFirstVisibleItemClosestToStart(!mSmoothScrollbarEnabled), 1076e5874e666791a58d21eed482fb90e917445da873Chris Craik findFirstVisibleItemClosestToEnd(!mSmoothScrollbarEnabled), 1077d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar this, mSmoothScrollbarEnabled, mShouldReverseLayout); 1078d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1079d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 1080d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar @Override 1081d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar public int computeVerticalScrollOffset(RecyclerView.State state) { 1082d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return computeScrollOffset(state); 1083d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1084d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 1085d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar @Override 1086d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar public int computeHorizontalScrollExtent(RecyclerView.State state) { 1087d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return computeScrollExtent(state); 1088d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1089d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 1090d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar private int computeScrollExtent(RecyclerView.State state) { 1091d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (getChildCount() == 0) { 1092d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return 0; 1093d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1094d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return ScrollbarHelper.computeScrollExtent(state, mPrimaryOrientation, 1095e5874e666791a58d21eed482fb90e917445da873Chris Craik findFirstVisibleItemClosestToStart(!mSmoothScrollbarEnabled), 1096e5874e666791a58d21eed482fb90e917445da873Chris Craik findFirstVisibleItemClosestToEnd(!mSmoothScrollbarEnabled), 1097d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar this, mSmoothScrollbarEnabled); 1098d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1099d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 1100d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar @Override 1101d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar public int computeVerticalScrollExtent(RecyclerView.State state) { 1102d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return computeScrollExtent(state); 1103d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1104d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 1105d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar @Override 1106d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar public int computeHorizontalScrollRange(RecyclerView.State state) { 1107d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return computeScrollRange(state); 1108d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1109d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 1110d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar private int computeScrollRange(RecyclerView.State state) { 1111d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (getChildCount() == 0) { 1112d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return 0; 1113d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1114d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return ScrollbarHelper.computeScrollRange(state, mPrimaryOrientation, 1115e5874e666791a58d21eed482fb90e917445da873Chris Craik findFirstVisibleItemClosestToStart(!mSmoothScrollbarEnabled), 1116e5874e666791a58d21eed482fb90e917445da873Chris Craik findFirstVisibleItemClosestToEnd(!mSmoothScrollbarEnabled), 1117d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar this, mSmoothScrollbarEnabled); 1118d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1119d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 1120d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar @Override 1121d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar public int computeVerticalScrollRange(RecyclerView.State state) { 1122d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return computeScrollRange(state); 1123d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1124d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 11254143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar private void measureChildWithDecorationsAndMargin(View child, LayoutParams lp, 11264143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar boolean alreadyMeasured) { 1127d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (lp.mFullSpan) { 1128d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mOrientation == VERTICAL) { 112942e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar measureChildWithDecorationsAndMargin(child, mFullSizeSpec, 11304143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar getChildMeasureSpec(getHeight(), getHeightMode(), 0, lp.height, true), 11314143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar alreadyMeasured); 1132d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else { 113342e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar measureChildWithDecorationsAndMargin(child, 11344143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar getChildMeasureSpec(getWidth(), getWidthMode(), 0, lp.width, true), 11354143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar mFullSizeSpec, alreadyMeasured); 1136d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1137d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else { 113842e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar if (mOrientation == VERTICAL) { 11394143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar measureChildWithDecorationsAndMargin(child, 11404143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar getChildMeasureSpec(mSizePerSpan, getWidthMode(), 0, lp.width, false), 11414143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar getChildMeasureSpec(getHeight(), getHeightMode(), 0, lp.height, true), 11424143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar alreadyMeasured); 114342e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar } else { 114442e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar measureChildWithDecorationsAndMargin(child, 11454143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar getChildMeasureSpec(getWidth(), getWidthMode(), 0, lp.width, true), 11464143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar getChildMeasureSpec(mSizePerSpan, getHeightMode(), 0, lp.height, false), 11474143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar alreadyMeasured); 114842e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar } 114942e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar } 115042e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar } 115142e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar 11522d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar private void measureChildWithDecorationsAndMargin(View child, int widthSpec, 11534143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar int heightSpec, boolean alreadyMeasured) { 1154c67491396ca2989bd162bd2b243c45071e4b2e6eYigit Boyar calculateItemDecorationsForChild(child, mTmpRect); 11552d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar LayoutParams lp = (LayoutParams) child.getLayoutParams(); 1156c67491396ca2989bd162bd2b243c45071e4b2e6eYigit Boyar widthSpec = updateSpecWithExtra(widthSpec, lp.leftMargin + mTmpRect.left, 1157c67491396ca2989bd162bd2b243c45071e4b2e6eYigit Boyar lp.rightMargin + mTmpRect.right); 1158c67491396ca2989bd162bd2b243c45071e4b2e6eYigit Boyar heightSpec = updateSpecWithExtra(heightSpec, lp.topMargin + mTmpRect.top, 1159c67491396ca2989bd162bd2b243c45071e4b2e6eYigit Boyar lp.bottomMargin + mTmpRect.bottom); 11604143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final boolean measure = alreadyMeasured 11614143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar ? shouldReMeasureChild(child, widthSpec, heightSpec, lp) 11624143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar : shouldMeasureChild(child, widthSpec, heightSpec, lp); 11634143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (measure) { 11644143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar child.measure(widthSpec, heightSpec); 11654143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 11664143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 11672d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 11682d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 11692d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar private int updateSpecWithExtra(int spec, int startInset, int endInset) { 11702d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (startInset == 0 && endInset == 0) { 11712d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return spec; 11722d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 11732d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int mode = View.MeasureSpec.getMode(spec); 11742d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mode == View.MeasureSpec.AT_MOST || mode == View.MeasureSpec.EXACTLY) { 11752d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return View.MeasureSpec.makeMeasureSpec( 1176121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar Math.max(0, View.MeasureSpec.getSize(spec) - startInset - endInset), mode); 11772d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 11782d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return spec; 11792d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 11802d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 11812d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar @Override 11822d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public void onRestoreInstanceState(Parcelable state) { 11832d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (state instanceof SavedState) { 11842d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mPendingSavedState = (SavedState) state; 11852d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar requestLayout(); 11862d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else if (DEBUG) { 11872d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar Log.d(TAG, "invalid saved state class"); 11882d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 11892d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 11902d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 11912d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar @Override 11922d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public Parcelable onSaveInstanceState() { 11932d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mPendingSavedState != null) { 11942d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return new SavedState(mPendingSavedState); 11952d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 11962d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar SavedState state = new SavedState(); 11972d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar state.mReverseLayout = mReverseLayout; 11982d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar state.mAnchorLayoutFromEnd = mLastLayoutFromEnd; 1199d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar state.mLastLayoutRTL = mLastLayoutRTL; 12002d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 12012d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mLazySpanLookup != null && mLazySpanLookup.mData != null) { 12022d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar state.mSpanLookup = mLazySpanLookup.mData; 12032d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar state.mSpanLookupSize = state.mSpanLookup.length; 1204d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar state.mFullSpanItems = mLazySpanLookup.mFullSpanItems; 12052d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else { 12062d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar state.mSpanLookupSize = 0; 12072d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 12082d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 12092d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (getChildCount() > 0) { 12102d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar state.mAnchorPosition = mLastLayoutFromEnd ? getLastChildPosition() 12112d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar : getFirstChildPosition(); 1212333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar state.mVisibleAnchorPosition = findFirstVisibleItemPositionInt(); 1213b8403301bbec29129730f6cce3fe2fa3ee8e1e0bYigit Boyar state.mSpanOffsetsSize = mSpanCount; 12142d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar state.mSpanOffsets = new int[mSpanCount]; 12152d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (int i = 0; i < mSpanCount; i++) { 1216d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int line; 1217d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mLastLayoutFromEnd) { 1218d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar line = mSpans[i].getEndLine(Span.INVALID_LINE); 1219d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (line != Span.INVALID_LINE) { 1220d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar line -= mPrimaryOrientation.getEndAfterPadding(); 1221d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1222d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else { 1223d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar line = mSpans[i].getStartLine(Span.INVALID_LINE); 1224d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (line != Span.INVALID_LINE) { 1225d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar line -= mPrimaryOrientation.getStartAfterPadding(); 1226d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1227d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1228d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar state.mSpanOffsets[i] = line; 12292d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 12302d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else { 1231d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar state.mAnchorPosition = NO_POSITION; 1232d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar state.mVisibleAnchorPosition = NO_POSITION; 1233b8403301bbec29129730f6cce3fe2fa3ee8e1e0bYigit Boyar state.mSpanOffsetsSize = 0; 12342d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 12352d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (DEBUG) { 12362d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar Log.d(TAG, "saved state:\n" + state); 12372d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 12382d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return state; 12392d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 12402d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 1241a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar @Override 1242a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler, 1243a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) { 1244a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar ViewGroup.LayoutParams lp = host.getLayoutParams(); 1245a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (!(lp instanceof LayoutParams)) { 1246a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar super.onInitializeAccessibilityNodeInfoForItem(host, info); 1247a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return; 1248a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 1249a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar LayoutParams sglp = (LayoutParams) lp; 1250a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (mOrientation == HORIZONTAL) { 1251a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain( 1252a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar sglp.getSpanIndex(), sglp.mFullSpan ? mSpanCount : 1, 1253a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar -1, -1, 1254a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar sglp.mFullSpan, false)); 1255a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } else { // VERTICAL 1256a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain( 1257a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar -1, -1, 1258a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar sglp.getSpanIndex(), sglp.mFullSpan ? mSpanCount : 1, 1259a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar sglp.mFullSpan, false)); 1260a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 1261a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 1262a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar 1263a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar @Override 1264a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 1265a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar super.onInitializeAccessibilityEvent(event); 1266a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (getChildCount() > 0) { 1267e5874e666791a58d21eed482fb90e917445da873Chris Craik final View start = findFirstVisibleItemClosestToStart(false); 1268e5874e666791a58d21eed482fb90e917445da873Chris Craik final View end = findFirstVisibleItemClosestToEnd(false); 1269a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (start == null || end == null) { 1270a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return; 1271a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 1272a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar final int startPos = getPosition(start); 1273a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar final int endPos = getPosition(end); 1274a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (startPos < endPos) { 127514d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas event.setFromIndex(startPos); 127614d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas event.setToIndex(endPos); 1277a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } else { 127814d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas event.setFromIndex(endPos); 127914d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas event.setToIndex(startPos); 1280a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 1281a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 1282a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 1283a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar 1284333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar /** 1285333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar * Finds the first fully visible child to be used as an anchor child if span count changes when 12866490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar * state is restored. If no children is fully visible, returns a partially visible child instead 12876490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar * of returning null. 1288333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar */ 1289333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar int findFirstVisibleItemPositionInt() { 1290e5874e666791a58d21eed482fb90e917445da873Chris Craik final View first = mShouldReverseLayout ? findFirstVisibleItemClosestToEnd(true) : 1291e5874e666791a58d21eed482fb90e917445da873Chris Craik findFirstVisibleItemClosestToStart(true); 1292d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return first == null ? NO_POSITION : getPosition(first); 1293d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1294d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 1295a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar @Override 1296a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar public int getRowCountForAccessibility(RecyclerView.Recycler recycler, 1297a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar RecyclerView.State state) { 1298a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (mOrientation == HORIZONTAL) { 1299a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return mSpanCount; 1300a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 1301a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return super.getRowCountForAccessibility(recycler, state); 1302a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 1303a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar 1304a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar @Override 1305a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar public int getColumnCountForAccessibility(RecyclerView.Recycler recycler, 1306a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar RecyclerView.State state) { 1307a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (mOrientation == VERTICAL) { 1308a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return mSpanCount; 1309a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 1310a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return super.getColumnCountForAccessibility(recycler, state); 1311a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 1312a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar 1313006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar /** 1314006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar * This is for internal use. Not necessarily the child closest to start but the first child 1315006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar * we find that matches the criteria. 1316006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar * This method does not do any sorting based on child's start coordinate, instead, it uses 1317006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar * children order. 1318006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar */ 1319e5874e666791a58d21eed482fb90e917445da873Chris Craik View findFirstVisibleItemClosestToStart(boolean fullyVisible) { 1320333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar final int boundsStart = mPrimaryOrientation.getStartAfterPadding(); 1321333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar final int boundsEnd = mPrimaryOrientation.getEndAfterPadding(); 1322d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final int limit = getChildCount(); 13236490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar View partiallyVisible = null; 132442e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar for (int i = 0; i < limit; i++) { 1325333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar final View child = getChildAt(i); 1326006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar final int childStart = mPrimaryOrientation.getDecoratedStart(child); 1327006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar final int childEnd = mPrimaryOrientation.getDecoratedEnd(child); 13281e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas if (childEnd <= boundsStart || childStart >= boundsEnd) { 1329006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar continue; // not visible at all 1330006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar } 1331006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar if (childStart >= boundsStart || !fullyVisible) { 1332006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar // when checking for start, it is enough even if part of the child's top is visible 1333006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar // as long as fully visible is not requested. 1334006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar return child; 1335006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar } 1336e5874e666791a58d21eed482fb90e917445da873Chris Craik if (partiallyVisible == null) { 1337006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar partiallyVisible = child; 1338d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1339d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 13406490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar return partiallyVisible; 1341d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1342d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 1343006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar /** 1344006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar * This is for internal use. Not necessarily the child closest to bottom but the first child 1345006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar * we find that matches the criteria. 1346006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar * This method does not do any sorting based on child's end coordinate, instead, it uses 1347006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar * children order. 1348006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar */ 1349e5874e666791a58d21eed482fb90e917445da873Chris Craik View findFirstVisibleItemClosestToEnd(boolean fullyVisible) { 1350d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final int boundsStart = mPrimaryOrientation.getStartAfterPadding(); 1351d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final int boundsEnd = mPrimaryOrientation.getEndAfterPadding(); 13526490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar View partiallyVisible = null; 135342e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar for (int i = getChildCount() - 1; i >= 0; i--) { 1354d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final View child = getChildAt(i); 1355006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar final int childStart = mPrimaryOrientation.getDecoratedStart(child); 1356006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar final int childEnd = mPrimaryOrientation.getDecoratedEnd(child); 13571e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas if (childEnd <= boundsStart || childStart >= boundsEnd) { 1358006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar continue; // not visible at all 1359006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar } 1360006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar if (childEnd <= boundsEnd || !fullyVisible) { 1361006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar // when checking for end, it is enough even if part of the child's bottom is visible 1362006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar // as long as fully visible is not requested. 1363006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar return child; 1364006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar } 1365e5874e666791a58d21eed482fb90e917445da873Chris Craik if (partiallyVisible == null) { 1366006eb2ce2077b3374d2e16d44b726552a5e88bf6Yigit Boyar partiallyVisible = child; 1367333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } 1368333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } 13696490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar return partiallyVisible; 1370333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } 1371333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar 13722d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar private void fixEndGap(RecyclerView.Recycler recycler, RecyclerView.State state, 13732d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar boolean canOffsetChildren) { 13744143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final int maxEndLine = getMaxEnd(Integer.MIN_VALUE); 13754143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (maxEndLine == Integer.MIN_VALUE) { 13764143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar return; 13774143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 13782d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int gap = mPrimaryOrientation.getEndAfterPadding() - maxEndLine; 13792d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int fixOffset; 13802d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (gap > 0) { 13812d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar fixOffset = -scrollBy(-gap, recycler, state); 13822d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else { 13832d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return; // nothing to fix 13842d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 13852d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar gap -= fixOffset; 13862d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (canOffsetChildren && gap > 0) { 13872d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mPrimaryOrientation.offsetChildren(gap); 13882d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 13892d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 13902d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 13912d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar private void fixStartGap(RecyclerView.Recycler recycler, RecyclerView.State state, 13922d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar boolean canOffsetChildren) { 13934143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final int minStartLine = getMinStart(Integer.MAX_VALUE); 13944143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (minStartLine == Integer.MAX_VALUE) { 13954143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar return; 13964143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 13972d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int gap = minStartLine - mPrimaryOrientation.getStartAfterPadding(); 13982d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int fixOffset; 13992d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (gap > 0) { 14002d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar fixOffset = scrollBy(gap, recycler, state); 14012d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else { 14022d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return; // nothing to fix 14032d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 14042d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar gap -= fixOffset; 14052d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (canOffsetChildren && gap > 0) { 14062d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mPrimaryOrientation.offsetChildren(-gap); 14072d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 14082d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 14092d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 1410e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar private void updateLayoutState(int anchorPosition, RecyclerView.State state) { 14112d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mLayoutState.mAvailable = 0; 14122d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mLayoutState.mCurrentPosition = anchorPosition; 1413e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar int startExtra = 0; 1414e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar int endExtra = 0; 14152d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (isSmoothScrolling()) { 14162d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int targetPos = state.getTargetScrollPosition(); 1417e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar if (targetPos != NO_POSITION) { 1418e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar if (mShouldReverseLayout == targetPos < anchorPosition) { 1419e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar endExtra = mPrimaryOrientation.getTotalSpace(); 1420e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar } else { 1421e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar startExtra = mPrimaryOrientation.getTotalSpace(); 1422e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar } 14232d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 14242d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 14252d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 1426e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar // Line of the furthest row. 1427e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar final boolean clipToPadding = getClipToPadding(); 1428e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar if (clipToPadding) { 1429e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar mLayoutState.mStartLine = mPrimaryOrientation.getStartAfterPadding() - startExtra; 1430e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar mLayoutState.mEndLine = mPrimaryOrientation.getEndAfterPadding() + endExtra; 14312d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else { 1432e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar mLayoutState.mEndLine = mPrimaryOrientation.getEnd() + endExtra; 1433e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar mLayoutState.mStartLine = -startExtra; 14342d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 1435f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar mLayoutState.mStopInFocusable = false; 14364143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar mLayoutState.mRecycle = true; 14371e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas mLayoutState.mInfinite = mPrimaryOrientation.getMode() == View.MeasureSpec.UNSPECIFIED 14381e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas && mPrimaryOrientation.getEnd() == 0; 1439e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar } 1440e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar 1441e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar private void setLayoutStateDirection(int direction) { 1442e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar mLayoutState.mLayoutDirection = direction; 14431e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas mLayoutState.mItemDirection = (mShouldReverseLayout == (direction == LAYOUT_START)) 14441e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas ? ITEM_DIRECTION_TAIL : ITEM_DIRECTION_HEAD; 14452d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 14462d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 14472d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar @Override 14482d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public void offsetChildrenHorizontal(int dx) { 14492d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar super.offsetChildrenHorizontal(dx); 14502d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (int i = 0; i < mSpanCount; i++) { 14512d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mSpans[i].onOffset(dx); 14522d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 14532d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 14542d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 14552d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar @Override 14562d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public void offsetChildrenVertical(int dy) { 14572d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar super.offsetChildrenVertical(dy); 14582d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (int i = 0; i < mSpanCount; i++) { 14592d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mSpans[i].onOffset(dy); 14602d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 14612d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 14622d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 14632d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar @Override 14642d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) { 1465d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar handleUpdate(positionStart, itemCount, AdapterHelper.UpdateOp.REMOVE); 14662d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 14672d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 14682d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar @Override 14692d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) { 1470d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar handleUpdate(positionStart, itemCount, AdapterHelper.UpdateOp.ADD); 1471d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1472d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 1473d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar @Override 1474d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar public void onItemsChanged(RecyclerView recyclerView) { 1475d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mLazySpanLookup.clear(); 1476d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar requestLayout(); 1477d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1478d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 1479d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar @Override 1480d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) { 1481d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar handleUpdate(from, to, AdapterHelper.UpdateOp.MOVE); 1482d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1483d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 1484d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar @Override 148521b345f101abc385496f42d250e580d21f1287b6Dake Gu public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount, 148621b345f101abc385496f42d250e580d21f1287b6Dake Gu Object payload) { 1487d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar handleUpdate(positionStart, itemCount, AdapterHelper.UpdateOp.UPDATE); 14882d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 14892d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 14902d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 14912d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Checks whether it should invalidate span assignments in response to an adapter change. 14922d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 1493d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar private void handleUpdate(int positionStart, int itemCountOrToPosition, int cmd) { 14942d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int minPosition = mShouldReverseLayout ? getLastChildPosition() : getFirstChildPosition(); 14951e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas final int affectedRangeEnd; // exclusive 14961e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas final int affectedRangeStart; // inclusive 1497f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar 1498f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar if (cmd == AdapterHelper.UpdateOp.MOVE) { 1499f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar if (positionStart < itemCountOrToPosition) { 1500f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar affectedRangeEnd = itemCountOrToPosition + 1; 1501f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar affectedRangeStart = positionStart; 1502f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar } else { 1503f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar affectedRangeEnd = positionStart + 1; 1504f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar affectedRangeStart = itemCountOrToPosition; 1505f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar } 1506f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar } else { 1507f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar affectedRangeStart = positionStart; 1508f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar affectedRangeEnd = positionStart + itemCountOrToPosition; 1509f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar } 1510f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar 1511f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar mLazySpanLookup.invalidateAfter(affectedRangeStart); 1512d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar switch (cmd) { 1513d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar case AdapterHelper.UpdateOp.ADD: 1514d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mLazySpanLookup.offsetForAddition(positionStart, itemCountOrToPosition); 1515d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar break; 1516d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar case AdapterHelper.UpdateOp.REMOVE: 1517d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mLazySpanLookup.offsetForRemoval(positionStart, itemCountOrToPosition); 1518d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar break; 1519d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar case AdapterHelper.UpdateOp.MOVE: 1520d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // TODO optimize 1521d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mLazySpanLookup.offsetForRemoval(positionStart, 1); 1522d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mLazySpanLookup.offsetForAddition(itemCountOrToPosition, 1); 1523d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar break; 1524d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1525d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 1526f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar if (affectedRangeEnd <= minPosition) { 1527d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return; 15282d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 1529f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar 15302d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int maxPosition = mShouldReverseLayout ? getFirstChildPosition() : getLastChildPosition(); 1531f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar if (affectedRangeStart <= maxPosition) { 15322d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar requestLayout(); 15332d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 15342d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 15352d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 15362d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar private int fill(RecyclerView.Recycler recycler, LayoutState layoutState, 15372d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar RecyclerView.State state) { 15382d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mRemainingSpans.set(0, mSpanCount, true); 15392d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar // The target position we are trying to reach. 15402d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int targetLine; 1541e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar 15422d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar // Line of the furthest row. 15434143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (mLayoutState.mInfinite) { 15444143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (layoutState.mLayoutDirection == LAYOUT_END) { 15454143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar targetLine = Integer.MAX_VALUE; 15464143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } else { // LAYOUT_START 15474143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar targetLine = Integer.MIN_VALUE; 15484143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 15494143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } else { 15504143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (layoutState.mLayoutDirection == LAYOUT_END) { 15514143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar targetLine = layoutState.mEndLine + layoutState.mAvailable; 15524143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } else { // LAYOUT_START 15534143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar targetLine = layoutState.mStartLine - layoutState.mAvailable; 15544143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 15552d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 1556e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar 1557d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar updateAllRemainingSpans(layoutState.mLayoutDirection, targetLine); 1558e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar if (DEBUG) { 15591e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas Log.d(TAG, "FILLING targetLine: " + targetLine + "," 15601e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas + "remaining spans:" + mRemainingSpans + ", state: " + layoutState); 1561e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar } 15622d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 1563d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // the default coordinate to add new view. 1564d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final int defaultNewViewLine = mShouldReverseLayout 1565d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar ? mPrimaryOrientation.getEndAfterPadding() 1566d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar : mPrimaryOrientation.getStartAfterPadding(); 1567e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar boolean added = false; 15684143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar while (layoutState.hasMore(state) 15694143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar && (mLayoutState.mInfinite || !mRemainingSpans.isEmpty())) { 15702d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar View view = layoutState.next(recycler); 15712d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar LayoutParams lp = ((LayoutParams) view.getLayoutParams()); 1572115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar final int position = lp.getViewLayoutPosition(); 15732d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int spanIndex = mLazySpanLookup.getSpan(position); 15742d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar Span currentSpan; 1575f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar final boolean assignSpan = spanIndex == LayoutParams.INVALID_SPAN_ID; 1576d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (assignSpan) { 1577d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar currentSpan = lp.mFullSpan ? mSpans[0] : getNextSpan(layoutState); 15782d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mLazySpanLookup.setSpan(position, currentSpan); 1579d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (DEBUG) { 1580d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar Log.d(TAG, "assigned " + currentSpan.mIndex + " for " + position); 1581d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 15822d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else { 1583d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (DEBUG) { 1584d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar Log.d(TAG, "using " + spanIndex + " for pos " + position); 1585d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 15862d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar currentSpan = mSpans[spanIndex]; 15872d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 1588ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar // assign span before measuring so that item decorators can get updated span index 1589ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar lp.mSpan = currentSpan; 1590ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar if (layoutState.mLayoutDirection == LAYOUT_END) { 1591ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar addView(view); 1592ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar } else { 1593ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar addView(view, 0); 1594ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar } 15954143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar measureChildWithDecorationsAndMargin(view, lp, false); 1596ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar 15972d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int start; 15982d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int end; 15992d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (layoutState.mLayoutDirection == LAYOUT_END) { 1600d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar start = lp.mFullSpan ? getMaxEnd(defaultNewViewLine) 1601d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar : currentSpan.getEndLine(defaultNewViewLine); 16022d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar end = start + mPrimaryOrientation.getDecoratedMeasurement(view); 1603d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (assignSpan && lp.mFullSpan) { 1604d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar LazySpanLookup.FullSpanItem fullSpanItem; 1605d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar fullSpanItem = createFullSpanItemFromEnd(start); 1606d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar fullSpanItem.mGapDir = LAYOUT_START; 1607d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar fullSpanItem.mPosition = position; 1608d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mLazySpanLookup.addFullSpanItem(fullSpanItem); 16092d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 16102d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else { 1611d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar end = lp.mFullSpan ? getMinStart(defaultNewViewLine) 1612d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar : currentSpan.getStartLine(defaultNewViewLine); 16132d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar start = end - mPrimaryOrientation.getDecoratedMeasurement(view); 1614d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (assignSpan && lp.mFullSpan) { 1615d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar LazySpanLookup.FullSpanItem fullSpanItem; 1616d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar fullSpanItem = createFullSpanItemFromStart(end); 1617d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar fullSpanItem.mGapDir = LAYOUT_END; 1618d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar fullSpanItem.mPosition = position; 1619d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mLazySpanLookup.addFullSpanItem(fullSpanItem); 16202d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 16212d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 16222d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 1623d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // check if this item may create gaps in the future 1624f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar if (lp.mFullSpan && layoutState.mItemDirection == ITEM_DIRECTION_HEAD) { 1625f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar if (assignSpan) { 1626f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar mLaidOutInvalidFullSpan = true; 1627f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar } else { 1628f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar final boolean hasInvalidGap; 1629f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar if (layoutState.mLayoutDirection == LAYOUT_END) { 1630f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar hasInvalidGap = !areAllEndsEqual(); 1631f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar } else { // layoutState.mLayoutDirection == LAYOUT_START 1632f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar hasInvalidGap = !areAllStartsEqual(); 1633f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar } 1634f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar if (hasInvalidGap) { 1635f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar final LazySpanLookup.FullSpanItem fullSpanItem = mLazySpanLookup 1636f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar .getFullSpanItem(position); 1637f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar if (fullSpanItem != null) { 1638f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar fullSpanItem.mHasUnwantedGapAfter = true; 1639f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar } 1640f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar mLaidOutInvalidFullSpan = true; 1641f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar } 1642f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar } 16432d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 1644d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar attachViewToSpans(view, lp, layoutState); 16454143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final int otherStart; 16464143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final int otherEnd; 16474143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (isLayoutRTL() && mOrientation == VERTICAL) { 16484143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar otherEnd = lp.mFullSpan ? mSecondaryOrientation.getEndAfterPadding() : 16494143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar mSecondaryOrientation.getEndAfterPadding() 16504143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar - (mSpanCount - 1 - currentSpan.mIndex) * mSizePerSpan; 16514143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar otherStart = otherEnd - mSecondaryOrientation.getDecoratedMeasurement(view); 16524143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } else { 16534143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar otherStart = lp.mFullSpan ? mSecondaryOrientation.getStartAfterPadding() 16541e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas : currentSpan.mIndex * mSizePerSpan 16551e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas + mSecondaryOrientation.getStartAfterPadding(); 16564143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar otherEnd = otherStart + mSecondaryOrientation.getDecoratedMeasurement(view); 16574143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 16584143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 16592d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mOrientation == VERTICAL) { 16602d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar layoutDecoratedWithMargins(view, otherStart, start, otherEnd, end); 16612d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else { 16622d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar layoutDecoratedWithMargins(view, start, otherStart, end, otherEnd); 16632d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 1664d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 16652d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (lp.mFullSpan) { 1666d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar updateAllRemainingSpans(mLayoutState.mLayoutDirection, targetLine); 16672d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else { 16682d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar updateRemainingSpans(currentSpan, mLayoutState.mLayoutDirection, targetLine); 16692d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 1670e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar recycle(recycler, mLayoutState); 1671b174744db6a70f384d44caeb43e10854652e81b9Keyvan Amiri if (mLayoutState.mStopInFocusable && view.hasFocusable()) { 1672f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar if (lp.mFullSpan) { 1673f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar mRemainingSpans.clear(); 1674f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } else { 1675f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar mRemainingSpans.set(currentSpan.mIndex, false); 1676f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 1677f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 1678e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar added = true; 16792d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 1680e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar if (!added) { 1681e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar recycle(recycler, mLayoutState); 16822d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 1683e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar final int diff; 16842d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mLayoutState.mLayoutDirection == LAYOUT_START) { 16852d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int minStart = getMinStart(mPrimaryOrientation.getStartAfterPadding()); 1686e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar diff = mPrimaryOrientation.getStartAfterPadding() - minStart; 16872d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else { 1688e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar final int maxEnd = getMaxEnd(mPrimaryOrientation.getEndAfterPadding()); 1689e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar diff = maxEnd - mPrimaryOrientation.getEndAfterPadding(); 16902d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 1691e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar return diff > 0 ? Math.min(layoutState.mAvailable, diff) : 0; 16922d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 16932d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 1694d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar private LazySpanLookup.FullSpanItem createFullSpanItemFromEnd(int newItemTop) { 1695d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar LazySpanLookup.FullSpanItem fsi = new LazySpanLookup.FullSpanItem(); 1696d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar fsi.mGapPerSpan = new int[mSpanCount]; 1697d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar for (int i = 0; i < mSpanCount; i++) { 1698d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar fsi.mGapPerSpan[i] = newItemTop - mSpans[i].getEndLine(newItemTop); 1699d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1700d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return fsi; 1701d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1702d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 1703d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar private LazySpanLookup.FullSpanItem createFullSpanItemFromStart(int newItemBottom) { 1704d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar LazySpanLookup.FullSpanItem fsi = new LazySpanLookup.FullSpanItem(); 1705d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar fsi.mGapPerSpan = new int[mSpanCount]; 1706d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar for (int i = 0; i < mSpanCount; i++) { 1707d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar fsi.mGapPerSpan[i] = mSpans[i].getStartLine(newItemBottom) - newItemBottom; 1708d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1709d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return fsi; 1710d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1711d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 1712d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar private void attachViewToSpans(View view, LayoutParams lp, LayoutState layoutState) { 1713d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (layoutState.mLayoutDirection == LayoutState.LAYOUT_END) { 1714d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (lp.mFullSpan) { 1715d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar appendViewToAllSpans(view); 1716d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else { 1717d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar lp.mSpan.appendToSpan(view); 1718d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1719d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else { 1720d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (lp.mFullSpan) { 1721d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar prependViewToAllSpans(view); 1722d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else { 1723d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar lp.mSpan.prependToSpan(view); 1724d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1725d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1726d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1727d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 1728e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar private void recycle(RecyclerView.Recycler recycler, LayoutState layoutState) { 17294143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (!layoutState.mRecycle || layoutState.mInfinite) { 1730f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar return; 1731f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 1732e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar if (layoutState.mAvailable == 0) { 1733e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar // easy, recycle line is still valid 1734e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar if (layoutState.mLayoutDirection == LAYOUT_START) { 1735e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar recycleFromEnd(recycler, layoutState.mEndLine); 1736e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar } else { 1737e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar recycleFromStart(recycler, layoutState.mStartLine); 1738e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar } 1739d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else { 1740e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar // scrolling case, recycle line can be shifted by how much space we could cover 1741e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar // by adding new views 1742e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar if (layoutState.mLayoutDirection == LAYOUT_START) { 1743e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar // calculate recycle line 1744e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar int scrolled = layoutState.mStartLine - getMaxStart(layoutState.mStartLine); 1745e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar final int line; 1746e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar if (scrolled < 0) { 1747e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar line = layoutState.mEndLine; 1748e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar } else { 1749e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar line = layoutState.mEndLine - Math.min(scrolled, layoutState.mAvailable); 1750e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar } 1751e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar recycleFromEnd(recycler, line); 1752e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar } else { 1753e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar // calculate recycle line 1754e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar int scrolled = getMinEnd(layoutState.mEndLine) - layoutState.mEndLine; 1755e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar final int line; 1756e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar if (scrolled < 0) { 1757e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar line = layoutState.mStartLine; 1758e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar } else { 1759e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar line = layoutState.mStartLine + Math.min(scrolled, layoutState.mAvailable); 1760e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar } 1761e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar recycleFromStart(recycler, line); 1762e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar } 1763d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1764e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar 1765d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1766d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 1767d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar private void appendViewToAllSpans(View view) { 1768d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // traverse in reverse so that we end up assigning full span items to 0 1769d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar for (int i = mSpanCount - 1; i >= 0; i--) { 1770d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mSpans[i].appendToSpan(view); 1771d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1772d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1773d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 1774d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar private void prependViewToAllSpans(View view) { 1775d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // traverse in reverse so that we end up assigning full span items to 0 1776d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar for (int i = mSpanCount - 1; i >= 0; i--) { 1777d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mSpans[i].prependToSpan(view); 1778d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1779d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1780d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 1781d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar private void updateAllRemainingSpans(int layoutDir, int targetLine) { 1782d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar for (int i = 0; i < mSpanCount; i++) { 1783d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mSpans[i].mViews.isEmpty()) { 1784d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar continue; 1785d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1786d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar updateRemainingSpans(mSpans[i], layoutDir, targetLine); 1787d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1788d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1789d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 17902d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar private void updateRemainingSpans(Span span, int layoutDir, int targetLine) { 17912d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int deletedSize = span.getDeletedSize(); 17922d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (layoutDir == LAYOUT_START) { 17932d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int line = span.getStartLine(); 1794e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar if (line + deletedSize <= targetLine) { 17952d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mRemainingSpans.set(span.mIndex, false); 17962d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 17972d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else { 17982d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int line = span.getEndLine(); 1799e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar if (line - deletedSize >= targetLine) { 18002d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mRemainingSpans.set(span.mIndex, false); 18012d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 18022d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 18032d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 18042d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 18052d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar private int getMaxStart(int def) { 18062d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int maxStart = mSpans[0].getStartLine(def); 18072d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (int i = 1; i < mSpanCount; i++) { 18082d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int spanStart = mSpans[i].getStartLine(def); 18092d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (spanStart > maxStart) { 18102d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar maxStart = spanStart; 18112d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 18122d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 18132d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return maxStart; 18142d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 18152d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 18162d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar private int getMinStart(int def) { 18172d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int minStart = mSpans[0].getStartLine(def); 18182d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (int i = 1; i < mSpanCount; i++) { 18192d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int spanStart = mSpans[i].getStartLine(def); 18202d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (spanStart < minStart) { 18212d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar minStart = spanStart; 18222d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 18232d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 18242d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return minStart; 18252d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 18262d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 1827f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar boolean areAllEndsEqual() { 1828f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar int end = mSpans[0].getEndLine(Span.INVALID_LINE); 1829f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar for (int i = 1; i < mSpanCount; i++) { 1830f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar if (mSpans[i].getEndLine(Span.INVALID_LINE) != end) { 1831f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar return false; 1832f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar } 1833f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar } 1834f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar return true; 1835f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar } 1836f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar 1837f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar boolean areAllStartsEqual() { 1838f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar int start = mSpans[0].getStartLine(Span.INVALID_LINE); 1839f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar for (int i = 1; i < mSpanCount; i++) { 1840f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar if (mSpans[i].getStartLine(Span.INVALID_LINE) != start) { 1841f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar return false; 1842f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar } 1843f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar } 1844f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar return true; 1845f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar } 1846f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar 18472d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar private int getMaxEnd(int def) { 18482d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int maxEnd = mSpans[0].getEndLine(def); 18492d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (int i = 1; i < mSpanCount; i++) { 18502d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int spanEnd = mSpans[i].getEndLine(def); 18512d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (spanEnd > maxEnd) { 18522d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar maxEnd = spanEnd; 18532d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 18542d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 18552d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return maxEnd; 18562d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 18572d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 18582d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar private int getMinEnd(int def) { 18592d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int minEnd = mSpans[0].getEndLine(def); 18602d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (int i = 1; i < mSpanCount; i++) { 18612d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int spanEnd = mSpans[i].getEndLine(def); 18622d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (spanEnd < minEnd) { 18632d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar minEnd = spanEnd; 18642d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 18652d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 18662d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return minEnd; 18672d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 18682d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 18692d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar private void recycleFromStart(RecyclerView.Recycler recycler, int line) { 18702d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar while (getChildCount() > 0) { 18712d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar View child = getChildAt(0); 18721e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas if (mPrimaryOrientation.getDecoratedEnd(child) <= line 18731e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas && mPrimaryOrientation.getTransformedEndWithDecoration(child) <= line) { 18742d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar LayoutParams lp = (LayoutParams) child.getLayoutParams(); 1875e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar // Don't recycle the last View in a span not to lose span's start/end lines 18762d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (lp.mFullSpan) { 18772d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (int j = 0; j < mSpanCount; j++) { 1878e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar if (mSpans[j].mViews.size() == 1) { 1879e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar return; 1880e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar } 1881e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar } 1882e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar for (int j = 0; j < mSpanCount; j++) { 18832d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mSpans[j].popStart(); 18842d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 18852d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else { 1886e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar if (lp.mSpan.mViews.size() == 1) { 1887e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar return; 1888e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar } 18892d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar lp.mSpan.popStart(); 18902d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 18912d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar removeAndRecycleView(child, recycler); 18922d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else { 18931e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas return; // done 18942d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 18952d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 18962d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 18972d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 18982d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar private void recycleFromEnd(RecyclerView.Recycler recycler, int line) { 18992d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int childCount = getChildCount(); 19002d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int i; 19012d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (i = childCount - 1; i >= 0; i--) { 19022d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar View child = getChildAt(i); 19031e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas if (mPrimaryOrientation.getDecoratedStart(child) >= line 19041e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas && mPrimaryOrientation.getTransformedStartWithDecoration(child) >= line) { 19052d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar LayoutParams lp = (LayoutParams) child.getLayoutParams(); 1906e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar // Don't recycle the last View in a span not to lose span's start/end lines 19072d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (lp.mFullSpan) { 19082d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (int j = 0; j < mSpanCount; j++) { 1909e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar if (mSpans[j].mViews.size() == 1) { 1910e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar return; 1911e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar } 1912e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar } 1913e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar for (int j = 0; j < mSpanCount; j++) { 19142d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mSpans[j].popEnd(); 19152d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 19162d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else { 1917e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar if (lp.mSpan.mViews.size() == 1) { 1918e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar return; 1919e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar } 19202d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar lp.mSpan.popEnd(); 19212d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 19222d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar removeAndRecycleView(child, recycler); 19232d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else { 19241e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas return; // done 19252d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 19262d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 19272d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 19282d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 19292d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 1930d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * @return True if last span is the first one we want to fill 1931d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar */ 1932d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar private boolean preferLastSpan(int layoutDir) { 1933d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mOrientation == HORIZONTAL) { 1934d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return (layoutDir == LAYOUT_START) != mShouldReverseLayout; 1935d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1936d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return ((layoutDir == LAYOUT_START) == mShouldReverseLayout) == isLayoutRTL(); 1937d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 1938d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 1939d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar /** 19402d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Finds the span for the next view. 19412d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 19422d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar private Span getNextSpan(LayoutState layoutState) { 1943d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final boolean preferLastSpan = preferLastSpan(layoutState.mLayoutDirection); 1944d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final int startIndex, endIndex, diff; 1945d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (preferLastSpan) { 1946d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar startIndex = mSpanCount - 1; 1947d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar endIndex = -1; 1948d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar diff = -1; 1949d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else { 1950d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar startIndex = 0; 1951d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar endIndex = mSpanCount; 1952d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar diff = 1; 1953d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 19542d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (layoutState.mLayoutDirection == LAYOUT_END) { 1955d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar Span min = null; 1956d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int minLine = Integer.MAX_VALUE; 19572d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int defaultLine = mPrimaryOrientation.getStartAfterPadding(); 1958d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar for (int i = startIndex; i != endIndex; i += diff) { 19592d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final Span other = mSpans[i]; 1960d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int otherLine = other.getEndLine(defaultLine); 1961d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (otherLine < minLine) { 19622d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar min = other; 19632d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar minLine = otherLine; 19642d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 19652d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 19662d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return min; 19672d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else { 1968d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar Span max = null; 1969d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int maxLine = Integer.MIN_VALUE; 19702d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int defaultLine = mPrimaryOrientation.getEndAfterPadding(); 1971d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar for (int i = startIndex; i != endIndex; i += diff) { 19722d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final Span other = mSpans[i]; 1973d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int otherLine = other.getStartLine(defaultLine); 1974d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (otherLine > maxLine) { 19752d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar max = other; 19762d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar maxLine = otherLine; 19772d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 19782d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 19792d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return max; 19802d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 19812d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 19822d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 19832d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar @Override 19842d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public boolean canScrollVertically() { 19852d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return mOrientation == VERTICAL; 19862d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 19872d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 19882d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar @Override 19892d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public boolean canScrollHorizontally() { 19902d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return mOrientation == HORIZONTAL; 19912d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 19922d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 19932d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar @Override 19942d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, 19952d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar RecyclerView.State state) { 19962d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return scrollBy(dx, recycler, state); 19972d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 19982d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 19992d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar @Override 20002d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, 20012d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar RecyclerView.State state) { 20022d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return scrollBy(dy, recycler, state); 20032d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 20042d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 20052d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar private int calculateScrollDirectionForPosition(int position) { 20062d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (getChildCount() == 0) { 20072d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return mShouldReverseLayout ? LAYOUT_END : LAYOUT_START; 20082d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 20092d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int firstChildPos = getFirstChildPosition(); 20102d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return position < firstChildPos != mShouldReverseLayout ? LAYOUT_START : LAYOUT_END; 20112d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 20122d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 20132d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar @Override 2014c587f7dba5a337169e854e235da59f595255d6ccAga Madurska public PointF computeScrollVectorForPosition(int targetPosition) { 2015c587f7dba5a337169e854e235da59f595255d6ccAga Madurska final int direction = calculateScrollDirectionForPosition(targetPosition); 2016c587f7dba5a337169e854e235da59f595255d6ccAga Madurska PointF outVector = new PointF(); 2017c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (direction == 0) { 2018c587f7dba5a337169e854e235da59f595255d6ccAga Madurska return null; 2019c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 2020c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (mOrientation == HORIZONTAL) { 2021c587f7dba5a337169e854e235da59f595255d6ccAga Madurska outVector.x = direction; 2022c587f7dba5a337169e854e235da59f595255d6ccAga Madurska outVector.y = 0; 2023c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } else { 2024c587f7dba5a337169e854e235da59f595255d6ccAga Madurska outVector.x = 0; 2025c587f7dba5a337169e854e235da59f595255d6ccAga Madurska outVector.y = direction; 2026c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 2027c587f7dba5a337169e854e235da59f595255d6ccAga Madurska return outVector; 2028c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 2029c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 2030c587f7dba5a337169e854e235da59f595255d6ccAga Madurska @Override 20312d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, 20322d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int position) { 2033c587f7dba5a337169e854e235da59f595255d6ccAga Madurska LinearSmoothScroller scroller = new LinearSmoothScroller(recyclerView.getContext()); 20342d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar scroller.setTargetPosition(position); 20352d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar startSmoothScroll(scroller); 20362d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 20372d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 20382d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar @Override 20392d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public void scrollToPosition(int position) { 20402d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mPendingSavedState != null && mPendingSavedState.mAnchorPosition != position) { 20412d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mPendingSavedState.invalidateAnchorPositionInfo(); 20422d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 20432d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mPendingScrollPosition = position; 20442d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mPendingScrollPositionOffset = INVALID_OFFSET; 20452d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar requestLayout(); 20462d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 20472d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 20482d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 20492d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Scroll to the specified adapter position with the given offset from layout start. 20502d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * <p> 20512d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Note that scroll position change will not be reflected until the next layout call. 20522d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * <p> 20532d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * If you are just trying to make a position visible, use {@link #scrollToPosition(int)}. 20542d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * 20552d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * @param position Index (starting at 0) of the reference item. 20562d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * @param offset The distance (in pixels) between the start edge of the item view and 20572d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * start edge of the RecyclerView. 20582d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * @see #setReverseLayout(boolean) 20592d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * @see #scrollToPosition(int) 20602d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 20612d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public void scrollToPositionWithOffset(int position, int offset) { 20622d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mPendingSavedState != null) { 20632d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mPendingSavedState.invalidateAnchorPositionInfo(); 20642d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 20652d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mPendingScrollPosition = position; 20662d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mPendingScrollPositionOffset = offset; 20672d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar requestLayout(); 20682d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 20692d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 2070945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik /** @hide */ 20711ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik @Override 20721e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state, 20733104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik LayoutPrefetchRegistry layoutPrefetchRegistry) { 2074945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik /* This method uses the simplifying assumption that the next N items (where N = span count) 2075945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * will be assigned, one-to-one, to spans, where ordering is based on which span extends 2076945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * least beyond the viewport. 2077945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * 2078945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * While this simplified model will be incorrect in some cases, it's difficult to know 2079945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * item heights, or whether individual items will be full span prior to construction. 2080945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * 2081945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * While this greedy estimation approach may underestimate the distance to prefetch items, 2082945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * it's very unlikely to overestimate them, so distances can be conservatively used to know 2083945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * the soonest (in terms of scroll distance) a prefetched view may come on screen. 2084945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik */ 20851ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik int delta = (mOrientation == HORIZONTAL) ? dx : dy; 20861ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik if (getChildCount() == 0 || delta == 0) { 20871ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik // can't support this scroll, so don't bother prefetching 2088945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik return; 20891ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik } 20901ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik prepareLayoutStateForDelta(delta, state); 2091945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 2092945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik // build sorted list of distances to end of each span (though we don't care which is which) 2093945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik if (mPrefetchDistances == null || mPrefetchDistances.length < mSpanCount) { 2094945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mPrefetchDistances = new int[mSpanCount]; 2095945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 2096695d9b8629fa048795298359d4845f0747364fafChris Craik 2097695d9b8629fa048795298359d4845f0747364fafChris Craik int itemPrefetchCount = 0; 2098945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik for (int i = 0; i < mSpanCount; i++) { 2099695d9b8629fa048795298359d4845f0747364fafChris Craik // compute number of pixels past the edge of the viewport that the current span extends 2100695d9b8629fa048795298359d4845f0747364fafChris Craik int distance = mLayoutState.mItemDirection == LAYOUT_START 2101945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik ? mLayoutState.mStartLine - mSpans[i].getStartLine(mLayoutState.mStartLine) 2102945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik : mSpans[i].getEndLine(mLayoutState.mEndLine) - mLayoutState.mEndLine; 2103695d9b8629fa048795298359d4845f0747364fafChris Craik if (distance >= 0) { 2104695d9b8629fa048795298359d4845f0747364fafChris Craik // span extends to the edge, so prefetch next item 2105695d9b8629fa048795298359d4845f0747364fafChris Craik mPrefetchDistances[itemPrefetchCount] = distance; 2106695d9b8629fa048795298359d4845f0747364fafChris Craik itemPrefetchCount++; 2107695d9b8629fa048795298359d4845f0747364fafChris Craik } 2108945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 2109695d9b8629fa048795298359d4845f0747364fafChris Craik Arrays.sort(mPrefetchDistances, 0, itemPrefetchCount); 2110945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 2111945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik // then assign them in order to the next N views (where N = span count) 2112695d9b8629fa048795298359d4845f0747364fafChris Craik for (int i = 0; i < itemPrefetchCount && mLayoutState.hasMore(state); i++) { 21131e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas layoutPrefetchRegistry.addPosition(mLayoutState.mCurrentPosition, 21141e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas mPrefetchDistances[i]); 21151ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik mLayoutState.mCurrentPosition += mLayoutState.mItemDirection; 21161ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik } 21171ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik } 21181ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik 21191ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik void prepareLayoutStateForDelta(int delta, RecyclerView.State state) { 21202d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int referenceChildPosition; 2121e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar final int layoutDir; 21221ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik if (delta > 0) { // layout towards end 2123e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar layoutDir = LAYOUT_END; 21242d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar referenceChildPosition = getLastChildPosition(); 21252d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else { 2126e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar layoutDir = LAYOUT_START; 21272d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar referenceChildPosition = getFirstChildPosition(); 21282d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 21294143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar mLayoutState.mRecycle = true; 2130e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar updateLayoutState(referenceChildPosition, state); 2131e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar setLayoutStateDirection(layoutDir); 21322d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mLayoutState.mCurrentPosition = referenceChildPosition + mLayoutState.mItemDirection; 2133945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mLayoutState.mAvailable = Math.abs(delta); 21341ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik } 21351ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik 21361ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik int scrollBy(int dt, RecyclerView.Recycler recycler, RecyclerView.State state) { 21371ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik if (getChildCount() == 0 || dt == 0) { 21381ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik return 0; 21391ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik } 21401ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik 21411ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik prepareLayoutStateForDelta(dt, state); 21422d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int consumed = fill(recycler, mLayoutState, state); 21431ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik final int available = mLayoutState.mAvailable; 21442d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int totalScroll; 21451ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik if (available < consumed) { 21462d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar totalScroll = dt; 21472d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else if (dt < 0) { 21482d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar totalScroll = -consumed; 21492d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else { // dt > 0 21502d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar totalScroll = consumed; 21512d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 21522d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (DEBUG) { 21532d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar Log.d(TAG, "asked " + dt + " scrolled" + totalScroll); 21542d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 21552d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 2156d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mPrimaryOrientation.offsetChildren(-totalScroll); 21572d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar // always reset this if we scroll for a proper save instance state 21582d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mLastLayoutFromEnd = mShouldReverseLayout; 215987b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar mLayoutState.mAvailable = 0; 216087b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar recycle(recycler, mLayoutState); 21612d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return totalScroll; 21622d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 21632d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 2164945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik int getLastChildPosition() { 21652d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int childCount = getChildCount(); 21662d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return childCount == 0 ? 0 : getPosition(getChildAt(childCount - 1)); 21672d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 21682d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 2169945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik int getFirstChildPosition() { 21702d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int childCount = getChildCount(); 21712d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return childCount == 0 ? 0 : getPosition(getChildAt(0)); 21722d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 21732d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 2174719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar /** 2175719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar * Finds the first View that can be used as an anchor View. 2176719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar * 2177719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar * @return Position of the View or 0 if it cannot find any such View. 2178719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar */ 2179719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar private int findFirstReferenceChildPosition(int itemCount) { 2180719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar final int limit = getChildCount(); 2181719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar for (int i = 0; i < limit; i++) { 2182719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar final View view = getChildAt(i); 2183719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar final int position = getPosition(view); 2184719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar if (position >= 0 && position < itemCount) { 2185719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar return position; 2186719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar } 2187719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar } 2188719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar return 0; 2189719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar } 2190719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar 2191719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar /** 2192719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar * Finds the last View that can be used as an anchor View. 2193719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar * 2194719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar * @return Position of the View or 0 if it cannot find any such View. 2195719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar */ 2196719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar private int findLastReferenceChildPosition(int itemCount) { 2197719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar for (int i = getChildCount() - 1; i >= 0; i--) { 2198719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar final View view = getChildAt(i); 2199719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar final int position = getPosition(view); 2200719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar if (position >= 0 && position < itemCount) { 2201719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar return position; 2202719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar } 2203719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar } 2204719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar return 0; 2205719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar } 2206719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar 2207f1811618b8c1a58374da7eb6093038d80c0ede52Yigit Boyar @SuppressWarnings("deprecation") 22082d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar @Override 22092d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public RecyclerView.LayoutParams generateDefaultLayoutParams() { 22104143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (mOrientation == HORIZONTAL) { 22114143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 2212e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas ViewGroup.LayoutParams.MATCH_PARENT); 22134143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } else { 2214e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas return new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 22154143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar ViewGroup.LayoutParams.WRAP_CONTENT); 22164143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 22172d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 22182d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 22192d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar @Override 22202d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public RecyclerView.LayoutParams generateLayoutParams(Context c, AttributeSet attrs) { 22212d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return new LayoutParams(c, attrs); 22222d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 22232d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 22242d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar @Override 22252d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public RecyclerView.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { 22262d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (lp instanceof ViewGroup.MarginLayoutParams) { 22272d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return new LayoutParams((ViewGroup.MarginLayoutParams) lp); 22282d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else { 22292d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return new LayoutParams(lp); 22302d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 22312d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 22322d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 22332d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar @Override 22342d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public boolean checkLayoutParams(RecyclerView.LayoutParams lp) { 22352d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return lp instanceof LayoutParams; 22362d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 22372d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 22382d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public int getOrientation() { 22392d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return mOrientation; 22402d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 22412d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 2242f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar @Nullable 2243f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar @Override 2244f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar public View onFocusSearchFailed(View focused, int direction, RecyclerView.Recycler recycler, 2245f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar RecyclerView.State state) { 2246f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar if (getChildCount() == 0) { 2247f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar return null; 2248f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 2249f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar 2250bc61003e6a10872c23f0de155456cb2fbeef31a7Yigit Boyar final View directChild = findContainingItemView(focused); 2251f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar if (directChild == null) { 2252f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar return null; 2253f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 2254f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar 2255f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar resolveShouldLayoutReverse(); 2256f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar final int layoutDir = convertFocusDirectionToLayoutDirection(direction); 2257f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar if (layoutDir == LayoutState.INVALID_LAYOUT) { 2258f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar return null; 2259f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 2260bc61003e6a10872c23f0de155456cb2fbeef31a7Yigit Boyar LayoutParams prevFocusLayoutParams = (LayoutParams) directChild.getLayoutParams(); 2261f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar boolean prevFocusFullSpan = prevFocusLayoutParams.mFullSpan; 2262f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar final Span prevFocusSpan = prevFocusLayoutParams.mSpan; 2263f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar final int referenceChildPosition; 2264f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar if (layoutDir == LAYOUT_END) { // layout towards end 2265f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar referenceChildPosition = getLastChildPosition(); 2266f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } else { 2267f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar referenceChildPosition = getFirstChildPosition(); 2268f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 2269f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar updateLayoutState(referenceChildPosition, state); 2270f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar setLayoutStateDirection(layoutDir); 2271f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar 2272f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar mLayoutState.mCurrentPosition = referenceChildPosition + mLayoutState.mItemDirection; 2273f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar mLayoutState.mAvailable = (int) (MAX_SCROLL_FACTOR * mPrimaryOrientation.getTotalSpace()); 2274f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar mLayoutState.mStopInFocusable = true; 22754143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar mLayoutState.mRecycle = false; 2276f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar fill(recycler, mLayoutState, state); 2277f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar mLastLayoutFromEnd = mShouldReverseLayout; 2278f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar if (!prevFocusFullSpan) { 2279f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar View view = prevFocusSpan.getFocusableViewAfter(referenceChildPosition, layoutDir); 2280bc61003e6a10872c23f0de155456cb2fbeef31a7Yigit Boyar if (view != null && view != directChild) { 2281f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar return view; 2282f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 2283f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 22849c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri 2285f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar // either could not find from the desired span or prev view is full span. 2286f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar // traverse all spans 2287f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar if (preferLastSpan(layoutDir)) { 22881e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas for (int i = mSpanCount - 1; i >= 0; i--) { 2289f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar View view = mSpans[i].getFocusableViewAfter(referenceChildPosition, layoutDir); 2290bc61003e6a10872c23f0de155456cb2fbeef31a7Yigit Boyar if (view != null && view != directChild) { 2291f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar return view; 2292f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 2293f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 2294f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } else { 22951e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas for (int i = 0; i < mSpanCount; i++) { 2296f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar View view = mSpans[i].getFocusableViewAfter(referenceChildPosition, layoutDir); 2297bc61003e6a10872c23f0de155456cb2fbeef31a7Yigit Boyar if (view != null && view != directChild) { 2298f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar return view; 2299f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 2300f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 2301f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 23029c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri 23039c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // Could not find any focusable views from any of the existing spans. Now start the search 23049c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // to find the best unfocusable candidate to become visible on the screen next. The search 23059c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // is done in the same fashion: first, check the views in the desired span and if no 23069c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // candidate is found, traverse the views in all the remaining spans. 23079c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri boolean shouldSearchFromStart = !mReverseLayout == (layoutDir == LayoutState.LAYOUT_START); 23089c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri View unfocusableCandidate = null; 23099c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri if (!prevFocusFullSpan) { 23109c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri unfocusableCandidate = findViewByPosition(shouldSearchFromStart 23119c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri ? prevFocusSpan.findFirstPartiallyVisibleItemPosition() : 23129c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri prevFocusSpan.findLastPartiallyVisibleItemPosition()); 23139c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri if (unfocusableCandidate != null && unfocusableCandidate != directChild) { 23149c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return unfocusableCandidate; 23159c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 23169c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 23179c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri 23189c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri if (preferLastSpan(layoutDir)) { 23199c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri for (int i = mSpanCount - 1; i >= 0; i--) { 23209c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri if (i == prevFocusSpan.mIndex) { 23219c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri continue; 23229c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 23239c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri unfocusableCandidate = findViewByPosition(shouldSearchFromStart 23249c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri ? mSpans[i].findFirstPartiallyVisibleItemPosition() : 23259c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri mSpans[i].findLastPartiallyVisibleItemPosition()); 23269c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri if (unfocusableCandidate != null && unfocusableCandidate != directChild) { 23279c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return unfocusableCandidate; 23289c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 23299c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 23309c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } else { 23319c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri for (int i = 0; i < mSpanCount; i++) { 23329c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri unfocusableCandidate = findViewByPosition(shouldSearchFromStart 23339c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri ? mSpans[i].findFirstPartiallyVisibleItemPosition() : 23349c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri mSpans[i].findLastPartiallyVisibleItemPosition()); 23359c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri if (unfocusableCandidate != null && unfocusableCandidate != directChild) { 23369c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return unfocusableCandidate; 23379c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 23389c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 23399c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 2340f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar return null; 2341f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 2342f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar 2343f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar /** 2344f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar * Converts a focusDirection to orientation. 2345f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar * 2346f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar * @param focusDirection One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN}, 2347f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, 2348f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar * {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD} 2349f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar * or 0 for not applicable 2350f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar * @return {@link LayoutState#LAYOUT_START} or {@link LayoutState#LAYOUT_END} if focus direction 2351f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar * is applicable to current state, {@link LayoutState#INVALID_LAYOUT} otherwise. 2352f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar */ 2353f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar private int convertFocusDirectionToLayoutDirection(int focusDirection) { 2354f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar switch (focusDirection) { 2355f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar case View.FOCUS_BACKWARD: 2356d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar if (mOrientation == VERTICAL) { 2357d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar return LayoutState.LAYOUT_START; 2358d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar } else if (isLayoutRTL()) { 2359d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar return LayoutState.LAYOUT_END; 2360d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar } else { 2361d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar return LayoutState.LAYOUT_START; 2362d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar } 2363f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar case View.FOCUS_FORWARD: 2364d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar if (mOrientation == VERTICAL) { 2365d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar return LayoutState.LAYOUT_END; 2366d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar } else if (isLayoutRTL()) { 2367d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar return LayoutState.LAYOUT_START; 2368d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar } else { 2369d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar return LayoutState.LAYOUT_END; 2370d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar } 2371f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar case View.FOCUS_UP: 2372f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar return mOrientation == VERTICAL ? LayoutState.LAYOUT_START 2373f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar : LayoutState.INVALID_LAYOUT; 2374f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar case View.FOCUS_DOWN: 2375f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar return mOrientation == VERTICAL ? LayoutState.LAYOUT_END 2376f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar : LayoutState.INVALID_LAYOUT; 2377f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar case View.FOCUS_LEFT: 2378f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar return mOrientation == HORIZONTAL ? LayoutState.LAYOUT_START 2379f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar : LayoutState.INVALID_LAYOUT; 2380f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar case View.FOCUS_RIGHT: 2381f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar return mOrientation == HORIZONTAL ? LayoutState.LAYOUT_END 2382f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar : LayoutState.INVALID_LAYOUT; 2383f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar default: 2384f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar if (DEBUG) { 2385f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar Log.d(TAG, "Unknown focus request:" + focusDirection); 2386f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 2387f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar return LayoutState.INVALID_LAYOUT; 2388f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 2389f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar 2390f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 23912d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 23922d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 23932d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * LayoutParams used by StaggeredGridLayoutManager. 239442e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar * <p> 239542e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar * Note that if the orientation is {@link #VERTICAL}, the width parameter is ignored and if the 239642e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar * orientation is {@link #HORIZONTAL} the height parameter is ignored because child view is 239742e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar * expected to fill all of the space given to it. 23982d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 23992d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public static class LayoutParams extends RecyclerView.LayoutParams { 24002d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 24012d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 24022d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Span Id for Views that are not laid out yet. 24032d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 24042d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public static final int INVALID_SPAN_ID = -1; 24052d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 24062d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar // Package scope to be able to access from tests. 24072d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar Span mSpan; 24082d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 24092d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar boolean mFullSpan; 24102d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 24112d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public LayoutParams(Context c, AttributeSet attrs) { 24122d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar super(c, attrs); 24132d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 24142d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 24152d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public LayoutParams(int width, int height) { 24162d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar super(width, height); 24172d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 24182d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 24192d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public LayoutParams(ViewGroup.MarginLayoutParams source) { 24202d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar super(source); 24212d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 24222d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 24232d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public LayoutParams(ViewGroup.LayoutParams source) { 24242d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar super(source); 24252d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 24262d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 24272d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public LayoutParams(RecyclerView.LayoutParams source) { 24282d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar super(source); 24292d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 24302d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 24312d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 24322d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * When set to true, the item will layout using all span area. That means, if orientation 24332d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * is vertical, the view will have full width; if orientation is horizontal, the view will 24342d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * have full height. 24352d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * 24362d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * @param fullSpan True if this item should traverse all spans. 24377499c6e5b853057aa81bfc20c39e6fa188805c55Yigit Boyar * @see #isFullSpan() 24382d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 24392d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public void setFullSpan(boolean fullSpan) { 24402d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mFullSpan = fullSpan; 24412d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 24422d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 24432d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 24447499c6e5b853057aa81bfc20c39e6fa188805c55Yigit Boyar * Returns whether this View occupies all available spans or just one. 24457499c6e5b853057aa81bfc20c39e6fa188805c55Yigit Boyar * 24467499c6e5b853057aa81bfc20c39e6fa188805c55Yigit Boyar * @return True if the View occupies all spans or false otherwise. 24477499c6e5b853057aa81bfc20c39e6fa188805c55Yigit Boyar * @see #setFullSpan(boolean) 24487499c6e5b853057aa81bfc20c39e6fa188805c55Yigit Boyar */ 24497499c6e5b853057aa81bfc20c39e6fa188805c55Yigit Boyar public boolean isFullSpan() { 24507499c6e5b853057aa81bfc20c39e6fa188805c55Yigit Boyar return mFullSpan; 24517499c6e5b853057aa81bfc20c39e6fa188805c55Yigit Boyar } 24527499c6e5b853057aa81bfc20c39e6fa188805c55Yigit Boyar 24537499c6e5b853057aa81bfc20c39e6fa188805c55Yigit Boyar /** 24542d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * Returns the Span index to which this View is assigned. 24552d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * 24562d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * @return The Span index of the View. If View is not yet assigned to any span, returns 24572d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * {@link #INVALID_SPAN_ID}. 24582d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 24592d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public final int getSpanIndex() { 24602d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mSpan == null) { 24612d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return INVALID_SPAN_ID; 24622d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 24632d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return mSpan.mIndex; 24642d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 24652d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 24662d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 24672d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar // Package scoped to access from tests. 24682d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar class Span { 24692d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 24706b4d950d0d1e26165a1e643a2fd1fe4e283786f1Yigit Boyar static final int INVALID_LINE = Integer.MIN_VALUE; 24713a500f61a8bdf48904f380f2d4925fe420d18ce7Aurimas Liutikas ArrayList<View> mViews = new ArrayList<>(); 24722d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int mCachedStart = INVALID_LINE; 24732d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int mCachedEnd = INVALID_LINE; 24742d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int mDeletedSize = 0; 24752d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int mIndex; 24762d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 24773a500f61a8bdf48904f380f2d4925fe420d18ce7Aurimas Liutikas Span(int index) { 24782d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mIndex = index; 24792d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 24802d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 24812d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int getStartLine(int def) { 24822d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mCachedStart != INVALID_LINE) { 24832d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return mCachedStart; 24842d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 24852d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mViews.size() == 0) { 24862d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return def; 24872d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 2488d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar calculateCachedStart(); 24892d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return mCachedStart; 24902d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 24912d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 2492d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar void calculateCachedStart() { 2493d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final View startView = mViews.get(0); 2494d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final LayoutParams lp = getLayoutParams(startView); 2495d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mCachedStart = mPrimaryOrientation.getDecoratedStart(startView); 2496d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (lp.mFullSpan) { 2497d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar LazySpanLookup.FullSpanItem fsi = mLazySpanLookup 2498115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar .getFullSpanItem(lp.getViewLayoutPosition()); 2499d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (fsi != null && fsi.mGapDir == LAYOUT_START) { 2500d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mCachedStart -= fsi.getGapForSpan(mIndex); 2501d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2502d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2503d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2504d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 25052d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar // Use this one when default value does not make sense and not having a value means a bug. 25062d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int getStartLine() { 25072d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mCachedStart != INVALID_LINE) { 25082d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return mCachedStart; 25092d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 2510d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar calculateCachedStart(); 25112d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return mCachedStart; 25122d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 25132d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 25142d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int getEndLine(int def) { 25152d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mCachedEnd != INVALID_LINE) { 25162d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return mCachedEnd; 25172d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 25182d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int size = mViews.size(); 25192d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (size == 0) { 25202d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return def; 25212d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 2522d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar calculateCachedEnd(); 25232d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return mCachedEnd; 25242d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 25252d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 2526d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar void calculateCachedEnd() { 2527d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final View endView = mViews.get(mViews.size() - 1); 2528d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final LayoutParams lp = getLayoutParams(endView); 2529d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mCachedEnd = mPrimaryOrientation.getDecoratedEnd(endView); 2530d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (lp.mFullSpan) { 2531d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar LazySpanLookup.FullSpanItem fsi = mLazySpanLookup 2532115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar .getFullSpanItem(lp.getViewLayoutPosition()); 2533d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (fsi != null && fsi.mGapDir == LAYOUT_END) { 2534d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mCachedEnd += fsi.getGapForSpan(mIndex); 2535d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2536d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2537d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2538d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 25392d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar // Use this one when default value does not make sense and not having a value means a bug. 25402d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int getEndLine() { 25412d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mCachedEnd != INVALID_LINE) { 25422d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return mCachedEnd; 25432d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 2544d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar calculateCachedEnd(); 25452d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return mCachedEnd; 25462d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 25472d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 25482d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar void prependToSpan(View view) { 25492d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar LayoutParams lp = getLayoutParams(view); 25502d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar lp.mSpan = this; 25512d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mViews.add(0, view); 25522d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mCachedStart = INVALID_LINE; 25532d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mViews.size() == 1) { 25542d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mCachedEnd = INVALID_LINE; 25552d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 25567c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen if (lp.isItemRemoved() || lp.isItemChanged()) { 25572d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mDeletedSize += mPrimaryOrientation.getDecoratedMeasurement(view); 25582d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 25592d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 25602d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 25612d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar void appendToSpan(View view) { 25622d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar LayoutParams lp = getLayoutParams(view); 25632d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar lp.mSpan = this; 25642d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mViews.add(view); 25652d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mCachedEnd = INVALID_LINE; 25662d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mViews.size() == 1) { 25672d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mCachedStart = INVALID_LINE; 25682d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 25697c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen if (lp.isItemRemoved() || lp.isItemChanged()) { 25702d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mDeletedSize += mPrimaryOrientation.getDecoratedMeasurement(view); 25712d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 25722d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 25732d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 25742d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar // Useful method to preserve positions on a re-layout. 25752d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar void cacheReferenceLineAndClear(boolean reverseLayout, int offset) { 25762d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int reference; 25772d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (reverseLayout) { 25782d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar reference = getEndLine(INVALID_LINE); 25792d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else { 25802d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar reference = getStartLine(INVALID_LINE); 25812d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 25822d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar clear(); 25832d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (reference == INVALID_LINE) { 25842d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return; 25852d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 25861e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas if ((reverseLayout && reference < mPrimaryOrientation.getEndAfterPadding()) 25871e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas || (!reverseLayout && reference > mPrimaryOrientation.getStartAfterPadding())) { 2588d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return; 2589d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 25902d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (offset != INVALID_OFFSET) { 25912d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar reference += offset; 25922d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 25932d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mCachedStart = mCachedEnd = reference; 25942d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 25952d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 25962d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar void clear() { 25972d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mViews.clear(); 25982d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar invalidateCache(); 25992d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mDeletedSize = 0; 26002d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 26012d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 26022d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar void invalidateCache() { 26032d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mCachedStart = INVALID_LINE; 26042d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mCachedEnd = INVALID_LINE; 26052d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 26062d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 26072d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar void setLine(int line) { 26082d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mCachedEnd = mCachedStart = line; 26092d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 26102d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 26112d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar void popEnd() { 26122d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int size = mViews.size(); 26132d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar View end = mViews.remove(size - 1); 26142d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final LayoutParams lp = getLayoutParams(end); 26152d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar lp.mSpan = null; 26167c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen if (lp.isItemRemoved() || lp.isItemChanged()) { 26172d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mDeletedSize -= mPrimaryOrientation.getDecoratedMeasurement(end); 26182d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 26192d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (size == 1) { 26202d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mCachedStart = INVALID_LINE; 26212d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 26222d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mCachedEnd = INVALID_LINE; 26232d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 26242d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 26252d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar void popStart() { 26262d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar View start = mViews.remove(0); 26272d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final LayoutParams lp = getLayoutParams(start); 26282d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar lp.mSpan = null; 26292d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mViews.size() == 0) { 26302d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mCachedEnd = INVALID_LINE; 26312d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 26327c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen if (lp.isItemRemoved() || lp.isItemChanged()) { 26332d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mDeletedSize -= mPrimaryOrientation.getDecoratedMeasurement(start); 26342d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 26352d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mCachedStart = INVALID_LINE; 26362d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 26372d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 26382d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public int getDeletedSize() { 26392d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return mDeletedSize; 26402d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 26412d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 26422d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar LayoutParams getLayoutParams(View view) { 26432d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return (LayoutParams) view.getLayoutParams(); 26442d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 26452d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 26462d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar void onOffset(int dt) { 26472d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mCachedStart != INVALID_LINE) { 26482d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mCachedStart += dt; 26492d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 26502d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mCachedEnd != INVALID_LINE) { 26512d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mCachedEnd += dt; 26522d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 26532d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 26542d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 2655333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar public int findFirstVisibleItemPosition() { 2656333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar return mReverseLayout 265742e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar ? findOneVisibleChild(mViews.size() - 1, -1, false) 2658333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar : findOneVisibleChild(0, mViews.size(), false); 2659333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } 2660333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar 26619c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri public int findFirstPartiallyVisibleItemPosition() { 26629c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return mReverseLayout 26639c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri ? findOnePartiallyVisibleChild(mViews.size() - 1, -1, true) 26649c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri : findOnePartiallyVisibleChild(0, mViews.size(), true); 26659c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 26669c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri 2667333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar public int findFirstCompletelyVisibleItemPosition() { 2668333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar return mReverseLayout 266942e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar ? findOneVisibleChild(mViews.size() - 1, -1, true) 2670333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar : findOneVisibleChild(0, mViews.size(), true); 2671333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } 2672333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar 2673333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar public int findLastVisibleItemPosition() { 2674333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar return mReverseLayout 2675333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar ? findOneVisibleChild(0, mViews.size(), false) 267642e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar : findOneVisibleChild(mViews.size() - 1, -1, false); 2677333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } 2678333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar 26799c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri public int findLastPartiallyVisibleItemPosition() { 26809c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return mReverseLayout 26819c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri ? findOnePartiallyVisibleChild(0, mViews.size(), true) 26829c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri : findOnePartiallyVisibleChild(mViews.size() - 1, -1, true); 26839c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 26849c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri 2685333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar public int findLastCompletelyVisibleItemPosition() { 2686333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar return mReverseLayout 2687333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar ? findOneVisibleChild(0, mViews.size(), true) 268842e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar : findOneVisibleChild(mViews.size() - 1, -1, true); 2689333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } 2690333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar 26919c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri /** 26929c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * Returns the first view within this span that is partially or fully visible. Partially 26939c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * visible refers to a view that overlaps but is not fully contained within RV's padded 26949c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * bounded area. This view returned can be defined to have an area of overlap strictly 26959c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * greater than zero if acceptEndPointInclusion is false. If true, the view's endpoint 26969c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * inclusion is enough to consider it partially visible. The latter case can then refer to 26979c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * an out-of-bounds view positioned right at the top (or bottom) boundaries of RV's padded 26989c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * area. This is used e.g. inside 26999c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * {@link #onFocusSearchFailed(View, int, RecyclerView.Recycler, RecyclerView.State)} for 27009c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * calculating the next unfocusable child to become visible on the screen. 27019c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * @param fromIndex The child position index to start the search from. 27029c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * @param toIndex The child position index to end the search at. 27039c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * @param completelyVisible True if we have to only consider completely visible views, 27049c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * false otherwise. 27059c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * @param acceptCompletelyVisible True if we can consider both partially or fully visible 27069c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * views, false, if only a partially visible child should be 27079c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * returned. 27089c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * @param acceptEndPointInclusion If the view's endpoint intersection with RV's padded 27099c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * bounded area is enough to consider it partially visible, 27109c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * false otherwise 27119c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * @return The adapter position of the first view that's either partially or fully visible. 27129c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * {@link RecyclerView#NO_POSITION} if no such view is found. 27139c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri */ 27149c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri int findOnePartiallyOrCompletelyVisibleChild(int fromIndex, int toIndex, 27159c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri boolean completelyVisible, 27169c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri boolean acceptCompletelyVisible, 27179c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri boolean acceptEndPointInclusion) { 2718333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar final int start = mPrimaryOrientation.getStartAfterPadding(); 2719333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar final int end = mPrimaryOrientation.getEndAfterPadding(); 2720333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar final int next = toIndex > fromIndex ? 1 : -1; 2721d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar for (int i = fromIndex; i != toIndex; i += next) { 2722333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar final View child = mViews.get(i); 2723333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar final int childStart = mPrimaryOrientation.getDecoratedStart(child); 2724333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar final int childEnd = mPrimaryOrientation.getDecoratedEnd(child); 27259c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri boolean childStartInclusion = acceptEndPointInclusion ? (childStart <= end) 27269c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri : (childStart < end); 27279c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri boolean childEndInclusion = acceptEndPointInclusion ? (childEnd >= start) 27289c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri : (childEnd > start); 27299c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri if (childStartInclusion && childEndInclusion) { 27309c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri if (completelyVisible && acceptCompletelyVisible) { 27319c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // the child has to be completely visible to be returned. 2732333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar if (childStart >= start && childEnd <= end) { 2733333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar return getPosition(child); 2734333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } 27359c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } else if (acceptCompletelyVisible) { 27369c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // can return either a partially or completely visible child. 27379c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return getPosition(child); 27389c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } else if (childStart < start || childEnd > end) { 27399c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // should return a partially visible child if exists and a completely 27409c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // visible child is not acceptable in this case. 2741333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar return getPosition(child); 2742333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } 2743333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } 2744333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } 2745d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return NO_POSITION; 2746333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar } 2747f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar 27489c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri int findOneVisibleChild(int fromIndex, int toIndex, boolean completelyVisible) { 27499c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return findOnePartiallyOrCompletelyVisibleChild(fromIndex, toIndex, completelyVisible, 27509c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri true, false); 27519c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 27529c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri 27539c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri int findOnePartiallyVisibleChild(int fromIndex, int toIndex, 27549c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri boolean acceptEndPointInclusion) { 27559c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return findOnePartiallyOrCompletelyVisibleChild(fromIndex, toIndex, false, false, 27569c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri acceptEndPointInclusion); 27579c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 27589c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri 2759f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar /** 2760f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar * Depending on the layout direction, returns the View that is after the given position. 2761f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar */ 2762f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar public View getFocusableViewAfter(int referenceChildPosition, int layoutDir) { 2763f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar View candidate = null; 2764f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar if (layoutDir == LAYOUT_START) { 2765f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar final int limit = mViews.size(); 2766f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar for (int i = 0; i < limit; i++) { 2767f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar final View view = mViews.get(i); 27689c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri if ((mReverseLayout && getPosition(view) <= referenceChildPosition) 27699c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri || (!mReverseLayout && getPosition(view) >= referenceChildPosition)) { 27709c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri break; 27719c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 2772b174744db6a70f384d44caeb43e10854652e81b9Keyvan Amiri if (view.hasFocusable()) { 2773f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar candidate = view; 2774f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } else { 2775f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar break; 2776f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 2777f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 2778f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } else { 2779f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar for (int i = mViews.size() - 1; i >= 0; i--) { 2780f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar final View view = mViews.get(i); 27819c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri if ((mReverseLayout && getPosition(view) >= referenceChildPosition) 27829c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri || (!mReverseLayout && getPosition(view) <= referenceChildPosition)) { 27839c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri break; 27849c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 2785b174744db6a70f384d44caeb43e10854652e81b9Keyvan Amiri if (view.hasFocusable()) { 2786f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar candidate = view; 2787f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } else { 2788f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar break; 2789f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 2790f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 2791f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 2792f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar return candidate; 2793f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 27942d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 27952d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 27962d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar /** 27972d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * An array of mappings from adapter position to span. 27982d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar * This only grows when a write happens and it grows up to the size of the adapter. 27992d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar */ 28002d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar static class LazySpanLookup { 28012d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 28022d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar private static final int MIN_SIZE = 10; 28032d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int[] mData; 2804d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar List<FullSpanItem> mFullSpanItems; 2805d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 2806d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 2807d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar /** 2808d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * Invalidates everything after this position, including full span information 2809d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar */ 2810d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int forceInvalidateAfter(int position) { 2811d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mFullSpanItems != null) { 2812d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar for (int i = mFullSpanItems.size() - 1; i >= 0; i--) { 2813d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar FullSpanItem fsi = mFullSpanItems.get(i); 2814d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (fsi.mPosition >= position) { 2815d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mFullSpanItems.remove(i); 2816d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2817d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2818d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2819d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return invalidateAfter(position); 2820d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 28212d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 2822d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar /** 2823d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * returns end position for invalidation. 2824d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar */ 2825d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int invalidateAfter(int position) { 28262d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mData == null) { 2827d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return RecyclerView.NO_POSITION; 28282d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 28292d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (position >= mData.length) { 2830d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return RecyclerView.NO_POSITION; 2831d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2832d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int endPosition = invalidateFullSpansAfter(position); 2833d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (endPosition == RecyclerView.NO_POSITION) { 2834d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar Arrays.fill(mData, position, mData.length, LayoutParams.INVALID_SPAN_ID); 2835d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return mData.length; 2836d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else { 2837d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // just invalidate items in between 2838d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar Arrays.fill(mData, position, endPosition + 1, LayoutParams.INVALID_SPAN_ID); 2839d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return endPosition + 1; 28402d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 28412d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 28422d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 28432d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int getSpan(int position) { 28442d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mData == null || position >= mData.length) { 28452d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return LayoutParams.INVALID_SPAN_ID; 28462d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else { 28472d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return mData[position]; 28482d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 28492d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 28502d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 28512d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar void setSpan(int position, Span span) { 28522d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar ensureSize(position); 28532d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mData[position] = span.mIndex; 28542d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 28552d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 28562d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int sizeForPosition(int position) { 28572d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int len = mData.length; 28582d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar while (len <= position) { 28592d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar len *= 2; 28602d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 28612d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return len; 28622d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 28632d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 28642d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar void ensureSize(int position) { 28652d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mData == null) { 28662d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mData = new int[Math.max(position, MIN_SIZE) + 1]; 28672d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar Arrays.fill(mData, LayoutParams.INVALID_SPAN_ID); 28682d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } else if (position >= mData.length) { 28692d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int[] old = mData; 28702d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mData = new int[sizeForPosition(position)]; 28712d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar System.arraycopy(old, 0, mData, 0, old.length); 28722d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar Arrays.fill(mData, old.length, mData.length, LayoutParams.INVALID_SPAN_ID); 28732d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 28742d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 28752d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 28762d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar void clear() { 28772d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mData != null) { 28782d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar Arrays.fill(mData, LayoutParams.INVALID_SPAN_ID); 28792d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 2880d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mFullSpanItems = null; 28812d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 28822d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 28832d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar void offsetForRemoval(int positionStart, int itemCount) { 2884d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mData == null || positionStart >= mData.length) { 2885d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return; 2886d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 28872d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar ensureSize(positionStart + itemCount); 28882d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar System.arraycopy(mData, positionStart + itemCount, mData, positionStart, 28892d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mData.length - positionStart - itemCount); 28902d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar Arrays.fill(mData, mData.length - itemCount, mData.length, 28912d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar LayoutParams.INVALID_SPAN_ID); 2892d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar offsetFullSpansForRemoval(positionStart, itemCount); 2893d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2894d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 2895d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar private void offsetFullSpansForRemoval(int positionStart, int itemCount) { 2896d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mFullSpanItems == null) { 2897d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return; 2898d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2899d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final int end = positionStart + itemCount; 2900d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar for (int i = mFullSpanItems.size() - 1; i >= 0; i--) { 2901d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar FullSpanItem fsi = mFullSpanItems.get(i); 2902d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (fsi.mPosition < positionStart) { 2903d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar continue; 2904d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2905d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (fsi.mPosition < end) { 2906d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mFullSpanItems.remove(i); 2907d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else { 2908d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar fsi.mPosition -= itemCount; 2909d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2910d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 29112d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 29122d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 29132d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar void offsetForAddition(int positionStart, int itemCount) { 2914d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mData == null || positionStart >= mData.length) { 2915d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return; 2916d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 29172d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar ensureSize(positionStart + itemCount); 29182d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar System.arraycopy(mData, positionStart, mData, positionStart + itemCount, 29192d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mData.length - positionStart - itemCount); 29202d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar Arrays.fill(mData, positionStart, positionStart + itemCount, 29212d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar LayoutParams.INVALID_SPAN_ID); 2922d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar offsetFullSpansForAddition(positionStart, itemCount); 2923d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2924d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 2925d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar private void offsetFullSpansForAddition(int positionStart, int itemCount) { 2926d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mFullSpanItems == null) { 2927d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return; 2928d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2929d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar for (int i = mFullSpanItems.size() - 1; i >= 0; i--) { 2930d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar FullSpanItem fsi = mFullSpanItems.get(i); 2931d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (fsi.mPosition < positionStart) { 2932d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar continue; 2933d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2934d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar fsi.mPosition += itemCount; 2935d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2936d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2937d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 2938d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar /** 2939d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * Returns when invalidation should end. e.g. hitting a full span position. 2940d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * Returned position SHOULD BE invalidated. 2941d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar */ 2942d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar private int invalidateFullSpansAfter(int position) { 2943d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mFullSpanItems == null) { 2944d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return RecyclerView.NO_POSITION; 2945d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2946d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final FullSpanItem item = getFullSpanItem(position); 2947d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // if there is an fsi at this position, get rid of it. 2948d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (item != null) { 2949d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mFullSpanItems.remove(item); 2950d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2951d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int nextFsiIndex = -1; 2952d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final int count = mFullSpanItems.size(); 2953d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar for (int i = 0; i < count; i++) { 2954d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar FullSpanItem fsi = mFullSpanItems.get(i); 2955d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (fsi.mPosition >= position) { 2956d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar nextFsiIndex = i; 2957d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar break; 2958d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2959d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2960d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (nextFsiIndex != -1) { 2961d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar FullSpanItem fsi = mFullSpanItems.get(nextFsiIndex); 2962d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mFullSpanItems.remove(nextFsiIndex); 2963d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return fsi.mPosition; 2964d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2965d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return RecyclerView.NO_POSITION; 2966d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2967d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 2968d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar public void addFullSpanItem(FullSpanItem fullSpanItem) { 2969d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mFullSpanItems == null) { 2970f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar mFullSpanItems = new ArrayList<>(); 2971d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2972d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final int size = mFullSpanItems.size(); 2973d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar for (int i = 0; i < size; i++) { 2974d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar FullSpanItem other = mFullSpanItems.get(i); 2975d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (other.mPosition == fullSpanItem.mPosition) { 2976d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (DEBUG) { 2977d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar throw new IllegalStateException("two fsis for same position"); 2978d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else { 2979d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mFullSpanItems.remove(i); 2980d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2981d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2982d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (other.mPosition >= fullSpanItem.mPosition) { 2983d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mFullSpanItems.add(i, fullSpanItem); 2984d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return; 2985d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2986d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2987d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar // if it is not added to a position. 2988d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mFullSpanItems.add(fullSpanItem); 2989d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2990d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 2991d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar public FullSpanItem getFullSpanItem(int position) { 2992d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mFullSpanItems == null) { 2993d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return null; 2994d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 2995d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar for (int i = mFullSpanItems.size() - 1; i >= 0; i--) { 2996d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar final FullSpanItem fsi = mFullSpanItems.get(i); 2997d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (fsi.mPosition == position) { 2998d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return fsi; 2999d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 3000d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 3001d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return null; 3002d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 3003d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 3004d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar /** 3005d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * @param minPos inclusive 3006d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * @param maxPos exclusive 3007d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * @param gapDir if not 0, returns FSIs on in that direction 3008f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar * @param hasUnwantedGapAfter If true, when full span item has unwanted gaps, it will be 3009f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar * returned even if its gap direction does not match. 3010d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar */ 3011f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar public FullSpanItem getFirstFullSpanItemInRange(int minPos, int maxPos, int gapDir, 3012f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar boolean hasUnwantedGapAfter) { 3013d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mFullSpanItems == null) { 3014d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return null; 3015d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 3016f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar final int limit = mFullSpanItems.size(); 3017f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar for (int i = 0; i < limit; i++) { 3018d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar FullSpanItem fsi = mFullSpanItems.get(i); 3019d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (fsi.mPosition >= maxPos) { 3020d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return null; 3021d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 3022f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar if (fsi.mPosition >= minPos 30231e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas && (gapDir == 0 || fsi.mGapDir == gapDir 30241e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas || (hasUnwantedGapAfter && fsi.mHasUnwantedGapAfter))) { 3025d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return fsi; 3026d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 3027d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 3028d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return null; 3029d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 3030d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 3031d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar /** 3032d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * We keep information about full span items because they may create gaps in the UI. 3033d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar */ 3034d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar static class FullSpanItem implements Parcelable { 3035d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 3036d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int mPosition; 3037d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int mGapDir; 3038d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int[] mGapPerSpan; 3039f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar // A full span may be laid out in primary direction but may have gaps due to 3040f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar // invalidation of views after it. This is recorded during a reverse scroll and if 3041f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar // view is still on the screen after scroll stops, we have to recalculate layout 3042f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar boolean mHasUnwantedGapAfter; 3043d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 30441e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas FullSpanItem(Parcel in) { 3045d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mPosition = in.readInt(); 3046d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mGapDir = in.readInt(); 3047f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar mHasUnwantedGapAfter = in.readInt() == 1; 3048d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int spanCount = in.readInt(); 3049d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (spanCount > 0) { 3050d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mGapPerSpan = new int[spanCount]; 3051d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar in.readIntArray(mGapPerSpan); 3052d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 3053d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 3054d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 30551e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas FullSpanItem() { 3056d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 3057d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 3058d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int getGapForSpan(int spanIndex) { 3059d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return mGapPerSpan == null ? 0 : mGapPerSpan[spanIndex]; 3060d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 3061d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 3062d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar @Override 3063d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar public int describeContents() { 3064d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return 0; 3065d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 3066d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 3067d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar @Override 3068d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar public void writeToParcel(Parcel dest, int flags) { 3069d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar dest.writeInt(mPosition); 3070d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar dest.writeInt(mGapDir); 3071f009f639980abd669fd533e5bc6ed42f3d4ed9ffYigit Boyar dest.writeInt(mHasUnwantedGapAfter ? 1 : 0); 3072d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mGapPerSpan != null && mGapPerSpan.length > 0) { 3073d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar dest.writeInt(mGapPerSpan.length); 3074d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar dest.writeIntArray(mGapPerSpan); 3075d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else { 3076d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar dest.writeInt(0); 3077d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 3078d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 3079d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 3080d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar @Override 3081d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar public String toString() { 30821e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas return "FullSpanItem{" 30831e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas + "mPosition=" + mPosition 30841e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas + ", mGapDir=" + mGapDir 30851e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas + ", mHasUnwantedGapAfter=" + mHasUnwantedGapAfter 30861e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas + ", mGapPerSpan=" + Arrays.toString(mGapPerSpan) 30871e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas + '}'; 30881e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas } 30891e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas 30901e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas public static final Parcelable.Creator<FullSpanItem> CREATOR = 30911e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas new Parcelable.Creator<FullSpanItem>() { 30921e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas @Override 30931e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas public FullSpanItem createFromParcel(Parcel in) { 30941e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas return new FullSpanItem(in); 30951e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas } 3096d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 30971e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas @Override 30981e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas public FullSpanItem[] newArray(int size) { 30991e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas return new FullSpanItem[size]; 31001e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas } 31011e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas }; 31022d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 31032d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 31042d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 3105ceb1d0ab9e22f5a48b72e9850f713be60311c516Yigit Boyar /** 3106ceb1d0ab9e22f5a48b72e9850f713be60311c516Yigit Boyar * @hide 3107ceb1d0ab9e22f5a48b72e9850f713be60311c516Yigit Boyar */ 31088e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas @RestrictTo(LIBRARY_GROUP) 3109ceb1d0ab9e22f5a48b72e9850f713be60311c516Yigit Boyar public static class SavedState implements Parcelable { 31102d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 31112d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int mAnchorPosition; 3112d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int mVisibleAnchorPosition; // Replacement for span info when spans are invalidated 3113b8403301bbec29129730f6cce3fe2fa3ee8e1e0bYigit Boyar int mSpanOffsetsSize; 31142d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int[] mSpanOffsets; 31152d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int mSpanLookupSize; 31162d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int[] mSpanLookup; 3117d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar List<LazySpanLookup.FullSpanItem> mFullSpanItems; 31182d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar boolean mReverseLayout; 31192d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar boolean mAnchorLayoutFromEnd; 3120d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar boolean mLastLayoutRTL; 31212d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 31222d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public SavedState() { 31232d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 31242d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 31252d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar SavedState(Parcel in) { 31262d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mAnchorPosition = in.readInt(); 3127333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar mVisibleAnchorPosition = in.readInt(); 3128b8403301bbec29129730f6cce3fe2fa3ee8e1e0bYigit Boyar mSpanOffsetsSize = in.readInt(); 3129b8403301bbec29129730f6cce3fe2fa3ee8e1e0bYigit Boyar if (mSpanOffsetsSize > 0) { 3130b8403301bbec29129730f6cce3fe2fa3ee8e1e0bYigit Boyar mSpanOffsets = new int[mSpanOffsetsSize]; 31312d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar in.readIntArray(mSpanOffsets); 31322d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 31332d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 31342d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mSpanLookupSize = in.readInt(); 31352d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mSpanLookupSize > 0) { 31362d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mSpanLookup = new int[mSpanLookupSize]; 31372d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar in.readIntArray(mSpanLookup); 31382d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 31392d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mReverseLayout = in.readInt() == 1; 31402d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mAnchorLayoutFromEnd = in.readInt() == 1; 3141d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mLastLayoutRTL = in.readInt() == 1; 3142f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar //noinspection unchecked 3143d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mFullSpanItems = in.readArrayList( 3144d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar LazySpanLookup.FullSpanItem.class.getClassLoader()); 31452d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 31462d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 31472d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public SavedState(SavedState other) { 3148b8403301bbec29129730f6cce3fe2fa3ee8e1e0bYigit Boyar mSpanOffsetsSize = other.mSpanOffsetsSize; 31492d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mAnchorPosition = other.mAnchorPosition; 3150333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar mVisibleAnchorPosition = other.mVisibleAnchorPosition; 31512d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mSpanOffsets = other.mSpanOffsets; 31522d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mSpanLookupSize = other.mSpanLookupSize; 31532d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mSpanLookup = other.mSpanLookup; 31542d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mReverseLayout = other.mReverseLayout; 31552d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mAnchorLayoutFromEnd = other.mAnchorLayoutFromEnd; 3156d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mLastLayoutRTL = other.mLastLayoutRTL; 3157d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mFullSpanItems = other.mFullSpanItems; 31582d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 31592d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 31602d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar void invalidateSpanInfo() { 31612d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mSpanOffsets = null; 3162b8403301bbec29129730f6cce3fe2fa3ee8e1e0bYigit Boyar mSpanOffsetsSize = 0; 31632d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mSpanLookupSize = 0; 31642d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mSpanLookup = null; 3165d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mFullSpanItems = null; 31662d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 31672d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 31682d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar void invalidateAnchorPositionInfo() { 31692d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mSpanOffsets = null; 3170b8403301bbec29129730f6cce3fe2fa3ee8e1e0bYigit Boyar mSpanOffsetsSize = 0; 3171d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mAnchorPosition = NO_POSITION; 3172d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mVisibleAnchorPosition = NO_POSITION; 31732d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 31742d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 31752d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar @Override 31762d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public int describeContents() { 31772d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar return 0; 31782d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 31792d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 31802d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar @Override 31812d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public void writeToParcel(Parcel dest, int flags) { 31822d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar dest.writeInt(mAnchorPosition); 3183333382aecf6822cb1ec484e8d3e4e822c9bd0c8dYigit Boyar dest.writeInt(mVisibleAnchorPosition); 3184b8403301bbec29129730f6cce3fe2fa3ee8e1e0bYigit Boyar dest.writeInt(mSpanOffsetsSize); 3185b8403301bbec29129730f6cce3fe2fa3ee8e1e0bYigit Boyar if (mSpanOffsetsSize > 0) { 31862d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar dest.writeIntArray(mSpanOffsets); 31872d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 31882d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar dest.writeInt(mSpanLookupSize); 31892d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar if (mSpanLookupSize > 0) { 31902d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar dest.writeIntArray(mSpanLookup); 31912d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 31922d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar dest.writeInt(mReverseLayout ? 1 : 0); 31932d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar dest.writeInt(mAnchorLayoutFromEnd ? 1 : 0); 3194d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar dest.writeInt(mLastLayoutRTL ? 1 : 0); 3195d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar dest.writeList(mFullSpanItems); 31962d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 31972d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 31981e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas public static final Parcelable.Creator<SavedState> CREATOR = 31991e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas new Parcelable.Creator<SavedState>() { 32001e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas @Override 32011e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas public SavedState createFromParcel(Parcel in) { 32021e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas return new SavedState(in); 32031e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas } 32042d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 32051e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas @Override 32061e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas public SavedState[] newArray(int size) { 32071e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas return new SavedState[size]; 32081e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas } 32091e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas }; 32102d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 3211d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 3212d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar /** 3213d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * Data class to hold the information about an anchor position which is used in onLayout call. 3214d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar */ 32159aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar class AnchorInfo { 3216d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 3217d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int mPosition; 3218d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar int mOffset; 3219d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar boolean mLayoutFromEnd; 3220d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar boolean mInvalidateOffsets; 32219aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar boolean mValid; 322287b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar // this is where we save span reference lines in case we need to re-use them for multi-pass 322387b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar // measure steps 322487b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar int[] mSpanReferenceLines; 32259aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar 32261e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas AnchorInfo() { 32279aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar reset(); 32289aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar } 3229d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 3230d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar void reset() { 3231d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mPosition = NO_POSITION; 3232d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mOffset = INVALID_OFFSET; 3233d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mLayoutFromEnd = false; 3234d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mInvalidateOffsets = false; 32359aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar mValid = false; 323687b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar if (mSpanReferenceLines != null) { 323787b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar Arrays.fill(mSpanReferenceLines, -1); 323887b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar } 323987b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar } 324087b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar 324187b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar void saveSpanReferenceLines(Span[] spans) { 324287b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar int spanCount = spans.length; 324387b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar if (mSpanReferenceLines == null || mSpanReferenceLines.length < spanCount) { 324487b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar mSpanReferenceLines = new int[mSpans.length]; 324587b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar } 324687b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar for (int i = 0; i < spanCount; i++) { 324787b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar // does not matter start or end since this is only recorded when span is reset 324887b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar mSpanReferenceLines[i] = spans[i].getStartLine(Span.INVALID_LINE); 324987b20c0e2004e884422b37fae6202c83cc24a8d9Yigit Boyar } 3250d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 3251d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 3252d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar void assignCoordinateFromPadding() { 3253d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mOffset = mLayoutFromEnd ? mPrimaryOrientation.getEndAfterPadding() 3254d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar : mPrimaryOrientation.getStartAfterPadding(); 3255d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 3256d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar 3257d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar void assignCoordinateFromPadding(int addedDistance) { 3258d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mLayoutFromEnd) { 3259d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mOffset = mPrimaryOrientation.getEndAfterPadding() - addedDistance; 3260d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } else { 3261d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar mOffset = mPrimaryOrientation.getStartAfterPadding() + addedDistance; 3262d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 3263d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 3264d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 32652d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar} 3266