17149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas/* 27149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Copyright (C) 2017 The Android Open Source Project 37149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 47149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Licensed under the Apache License, Version 2.0 (the "License"); 57149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * you may not use this file except in compliance with the License. 67149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * You may obtain a copy of the License at 77149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 87149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * http://www.apache.org/licenses/LICENSE-2.0 97149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Unless required by applicable law or agreed to in writing, software 117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * distributed under the License is distributed on an "AS IS" BASIS, 127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * See the License for the specific language governing permissions and 147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * limitations under the License. 157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikaspackage com.android.internal.widget; 187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport static com.android.internal.widget.RecyclerView.NO_POSITION; 207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport android.content.Context; 227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport android.graphics.PointF; 237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport android.os.Parcel; 247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport android.os.Parcelable; 257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport android.util.AttributeSet; 267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport android.util.Log; 277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport android.view.View; 287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport android.view.ViewGroup; 297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport android.view.accessibility.AccessibilityEvent; 307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport com.android.internal.widget.RecyclerView.LayoutParams; 327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport com.android.internal.widget.helper.ItemTouchHelper; 337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport java.util.List; 357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas/** 377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * A {@link com.android.internal.widget.RecyclerView.LayoutManager} implementation which provides 387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * similar functionality to {@link android.widget.ListView}. 397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikaspublic class LinearLayoutManager extends RecyclerView.LayoutManager implements 417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas ItemTouchHelper.ViewDropHandler, RecyclerView.SmoothScroller.ScrollVectorProvider { 427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private static final String TAG = "LinearLayoutManager"; 447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas static final boolean DEBUG = false; 467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public static final int HORIZONTAL = OrientationHelper.HORIZONTAL; 487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public static final int VERTICAL = OrientationHelper.VERTICAL; 507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public static final int INVALID_OFFSET = Integer.MIN_VALUE; 527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * While trying to find next view to focus, LayoutManager will not try to scroll more 567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * than this factor times the total space of the list. If layout is vertical, total space is the 577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * height minus padding, if layout is horizontal, total space is the width minus padding. 587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private static final float MAX_SCROLL_FACTOR = 1 / 3f; 607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL} 647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int mOrientation; 667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Helper class that keeps temporary layout state. 697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * It does not keep state after layout is complete but we still keep a reference to re-use 707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * the same object. 717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private LayoutState mLayoutState; 737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Many calculations are made depending on orientation. To keep it clean, this interface 767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * helps {@link LinearLayoutManager} make those decisions. 777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Based on {@link #mOrientation}, an implementation is lazily created in 787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * {@link #ensureLayoutState} method. 797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas OrientationHelper mOrientationHelper; 817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * We need to track this so that we can ignore current position when it changes. 847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private boolean mLastStackFromEnd; 867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Defines if layout should be calculated from end to start. 907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @see #mShouldReverseLayout 927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private boolean mReverseLayout = false; 947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * This keeps the final value for how LayoutManager should start laying out views. 977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * It is calculated by checking {@link #getReverseLayout()} and View's layout direction. 987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * {@link #onLayoutChildren(RecyclerView.Recycler, RecyclerView.State)} is run. 997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 1007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas boolean mShouldReverseLayout = false; 1017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 1037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Works the same way as {@link android.widget.AbsListView#setStackFromBottom(boolean)} and 1047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * it supports both orientations. 1057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * see {@link android.widget.AbsListView#setStackFromBottom(boolean)} 1067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 1077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private boolean mStackFromEnd = false; 1087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 1107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Works the same way as {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)}. 1117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * see {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)} 1127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 1137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private boolean mSmoothScrollbarEnabled = true; 1147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 1167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * When LayoutManager needs to scroll to a position, it sets this variable and requests a 1177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * layout which will check this variable and re-layout accordingly. 1187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 1197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int mPendingScrollPosition = NO_POSITION; 1207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 1227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Used to keep the offset value when {@link #scrollToPositionWithOffset(int, int)} is 1237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * called. 1247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 1257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int mPendingScrollPositionOffset = INVALID_OFFSET; 1267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private boolean mRecycleChildrenOnDetach; 1287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas SavedState mPendingSavedState = null; 1307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 1327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Re-used variable to keep anchor information on re-layout. 1337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Anchor position and coordinate defines the reference point for LLM while doing a layout. 1347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * */ 1357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final AnchorInfo mAnchorInfo = new AnchorInfo(); 1367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 1387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Stashed to avoid allocation, currently only used in #fill() 1397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 1407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private final LayoutChunkResult mLayoutChunkResult = new LayoutChunkResult(); 1417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 1437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Number of items to prefetch when first coming on screen with new data. 1447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 1457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private int mInitialItemPrefetchCount = 2; 1467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 1487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Creates a vertical LinearLayoutManager 1497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 1507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param context Current context, will be used to access resources. 1517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 1527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public LinearLayoutManager(Context context) { 1537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas this(context, VERTICAL, false); 1547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 1577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param context Current context, will be used to access resources. 1587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link 1597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * #VERTICAL}. 1607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param reverseLayout When set to true, layouts from end to start. 1617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 1627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public LinearLayoutManager(Context context, int orientation, boolean reverseLayout) { 1637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas setOrientation(orientation); 1647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas setReverseLayout(reverseLayout); 1657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas setAutoMeasureEnabled(true); 1667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 1697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Constructor used when layout manager is set in XML by RecyclerView attribute 1707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * "layoutManager". Defaults to vertical orientation. 1717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 1727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public LinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, 1737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int defStyleRes) { 1747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Properties properties = getProperties(context, attrs, defStyleAttr, defStyleRes); 1757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas setOrientation(properties.orientation); 1767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas setReverseLayout(properties.reverseLayout); 1777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas setStackFromEnd(properties.stackFromEnd); 1787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas setAutoMeasureEnabled(true); 1797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 1827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * {@inheritDoc} 1837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 1847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 1857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public LayoutParams generateDefaultLayoutParams() { 1867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 1877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas ViewGroup.LayoutParams.WRAP_CONTENT); 1887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 1917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Returns whether LayoutManager will recycle its children when it is detached from 1927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * RecyclerView. 1937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 1947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return true if LayoutManager will recycle its children when it is detached from 1957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * RecyclerView. 1967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 1977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public boolean getRecycleChildrenOnDetach() { 1987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return mRecycleChildrenOnDetach; 1997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 2017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 2027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Set whether LayoutManager will recycle its children when it is detached from 2037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * RecyclerView. 2047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p> 2057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * If you are using a {@link RecyclerView.RecycledViewPool}, it might be a good idea to set 2067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * this flag to <code>true</code> so that views will be available to other RecyclerViews 2077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * immediately. 2087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p> 2097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Note that, setting this flag will result in a performance drop if RecyclerView 2107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * is restored. 2117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 2127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param recycleChildrenOnDetach Whether children should be recycled in detach or not. 2137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 2147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void setRecycleChildrenOnDetach(boolean recycleChildrenOnDetach) { 2157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mRecycleChildrenOnDetach = recycleChildrenOnDetach; 2167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 2187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 2197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) { 2207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas super.onDetachedFromWindow(view, recycler); 2217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mRecycleChildrenOnDetach) { 2227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas removeAndRecycleAllViews(recycler); 2237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas recycler.clear(); 2247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 2277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 2287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 2297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas super.onInitializeAccessibilityEvent(event); 2307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (getChildCount() > 0) { 2317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas event.setFromIndex(findFirstVisibleItemPosition()); 2327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas event.setToIndex(findLastVisibleItemPosition()); 2337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 2367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 2377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public Parcelable onSaveInstanceState() { 2387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mPendingSavedState != null) { 2397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return new SavedState(mPendingSavedState); 2407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas SavedState state = new SavedState(); 2427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (getChildCount() > 0) { 2437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas ensureLayoutState(); 2447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas boolean didLayoutFromEnd = mLastStackFromEnd ^ mShouldReverseLayout; 2457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas state.mAnchorLayoutFromEnd = didLayoutFromEnd; 2467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (didLayoutFromEnd) { 2477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final View refChild = getChildClosestToEnd(); 2487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas state.mAnchorOffset = mOrientationHelper.getEndAfterPadding() 2497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas - mOrientationHelper.getDecoratedEnd(refChild); 2507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas state.mAnchorPosition = getPosition(refChild); 2517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 2527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final View refChild = getChildClosestToStart(); 2537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas state.mAnchorPosition = getPosition(refChild); 2547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas state.mAnchorOffset = mOrientationHelper.getDecoratedStart(refChild) 2557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas - mOrientationHelper.getStartAfterPadding(); 2567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 2587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas state.invalidateAnchor(); 2597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return state; 2617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 2637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 2647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void onRestoreInstanceState(Parcelable state) { 2657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (state instanceof SavedState) { 2667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mPendingSavedState = (SavedState) state; 2677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas requestLayout(); 2687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (DEBUG) { 2697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Log.d(TAG, "loaded saved state"); 2707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else if (DEBUG) { 2727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Log.d(TAG, "invalid saved state class"); 2737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 2767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 2777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return true if {@link #getOrientation()} is {@link #HORIZONTAL} 2787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 2797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 2807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public boolean canScrollHorizontally() { 2817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return mOrientation == HORIZONTAL; 2827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 2847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 2857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return true if {@link #getOrientation()} is {@link #VERTICAL} 2867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 2877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 2887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public boolean canScrollVertically() { 2897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return mOrientation == VERTICAL; 2907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 2927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 2937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Compatibility support for {@link android.widget.AbsListView#setStackFromBottom(boolean)} 2947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 2957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void setStackFromEnd(boolean stackFromEnd) { 2967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas assertNotInLayoutOrScroll(null); 2977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mStackFromEnd == stackFromEnd) { 2987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return; 2997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mStackFromEnd = stackFromEnd; 3017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas requestLayout(); 3027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 3047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public boolean getStackFromEnd() { 3057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return mStackFromEnd; 3067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 3087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 3097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Returns the current orientation of the layout. 3107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 3117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return Current orientation, either {@link #HORIZONTAL} or {@link #VERTICAL} 3127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @see #setOrientation(int) 3137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 3147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public int getOrientation() { 3157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return mOrientation; 3167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 3187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 3197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Sets the orientation of the layout. {@link com.android.internal.widget.LinearLayoutManager} 3207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * will do its best to keep scroll position. 3217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 3227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param orientation {@link #HORIZONTAL} or {@link #VERTICAL} 3237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 3247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void setOrientation(int orientation) { 3257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (orientation != HORIZONTAL && orientation != VERTICAL) { 3267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas throw new IllegalArgumentException("invalid orientation:" + orientation); 3277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas assertNotInLayoutOrScroll(null); 3297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (orientation == mOrientation) { 3307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return; 3317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mOrientation = orientation; 3337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mOrientationHelper = null; 3347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas requestLayout(); 3357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 3377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 3387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Calculates the view layout order. (e.g. from end to start or start to end) 3397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * RTL layout support is applied automatically. So if layout is RTL and 3407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * {@link #getReverseLayout()} is {@code true}, elements will be laid out starting from left. 3417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 3427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private void resolveShouldLayoutReverse() { 3437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // A == B is the same result, but we rather keep it readable 3447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mOrientation == VERTICAL || !isLayoutRTL()) { 3457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mShouldReverseLayout = mReverseLayout; 3467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 3477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mShouldReverseLayout = !mReverseLayout; 3487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 3517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 3527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Returns if views are laid out from the opposite direction of the layout. 3537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 3547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return If layout is reversed or not. 3557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @see #setReverseLayout(boolean) 3567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 3577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public boolean getReverseLayout() { 3587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return mReverseLayout; 3597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 3617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 3627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Used to reverse item traversal and layout order. 3637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * This behaves similar to the layout change for RTL views. When set to true, first item is 3647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * laid out at the end of the UI, second item is laid out before it etc. 3657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 3667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * For horizontal layouts, it depends on the layout direction. 3677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * When set to true, If {@link com.android.internal.widget.RecyclerView} is LTR, than it will 3687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * layout from RTL, if {@link com.android.internal.widget.RecyclerView}} is RTL, it will layout 3697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * from LTR. 3707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 3717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * If you are looking for the exact same behavior of 3727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * {@link android.widget.AbsListView#setStackFromBottom(boolean)}, use 3737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * {@link #setStackFromEnd(boolean)} 3747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 3757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void setReverseLayout(boolean reverseLayout) { 3767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas assertNotInLayoutOrScroll(null); 3777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (reverseLayout == mReverseLayout) { 3787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return; 3797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mReverseLayout = reverseLayout; 3817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas requestLayout(); 3827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 3847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 3857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * {@inheritDoc} 3867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 3877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 3887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public View findViewByPosition(int position) { 3897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int childCount = getChildCount(); 3907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (childCount == 0) { 3917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return null; 3927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int firstChild = getPosition(getChildAt(0)); 3947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int viewPosition = position - firstChild; 3957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (viewPosition >= 0 && viewPosition < childCount) { 3967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final View child = getChildAt(viewPosition); 3977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (getPosition(child) == position) { 3987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return child; // in pre-layout, this may not match 3997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 4007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 4017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // fallback to traversal. This might be necessary in pre-layout. 4027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return super.findViewByPosition(position); 4037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 4047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 4057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 4067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p>Returns the amount of extra space that should be laid out by LayoutManager.</p> 4077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 4087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p>By default, {@link com.android.internal.widget.LinearLayoutManager} lays out 1 extra page 4097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * of items while smooth scrolling and 0 otherwise. You can override this method to implement 4107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * your custom layout pre-cache logic.</p> 4117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 4127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p><strong>Note:</strong>Laying out invisible elements generally comes with significant 4137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * performance cost. It's typically only desirable in places like smooth scrolling to an unknown 4147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * location, where 1) the extra content helps LinearLayoutManager know in advance when its 4157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * target is approaching, so it can decelerate early and smoothly and 2) while motion is 4167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * continuous.</p> 4177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 4187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p>Extending the extra layout space is especially expensive if done while the user may change 4197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * scrolling direction. Changing direction will cause the extra layout space to swap to the 4207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * opposite side of the viewport, incurring many rebinds/recycles, unless the cache is large 4217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * enough to handle it.</p> 4227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 4237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return The extra space that should be laid out (in pixels). 4247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 4257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas protected int getExtraLayoutSpace(RecyclerView.State state) { 4267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (state.hasTargetScrollPosition()) { 4277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return mOrientationHelper.getTotalSpace(); 4287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 4297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return 0; 4307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 4317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 4327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 4337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 4347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, 4357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int position) { 4367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas LinearSmoothScroller linearSmoothScroller = 4377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas new LinearSmoothScroller(recyclerView.getContext()); 4387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas linearSmoothScroller.setTargetPosition(position); 4397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas startSmoothScroll(linearSmoothScroller); 4407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 4417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 4427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 4437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public PointF computeScrollVectorForPosition(int targetPosition) { 4447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (getChildCount() == 0) { 4457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return null; 4467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 4477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int firstChildPos = getPosition(getChildAt(0)); 4487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int direction = targetPosition < firstChildPos != mShouldReverseLayout ? -1 : 1; 4497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mOrientation == HORIZONTAL) { 4507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return new PointF(direction, 0); 4517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 4527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return new PointF(0, direction); 4537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 4547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 4557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 4567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 4577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * {@inheritDoc} 4587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 4597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 4607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 4617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // layout algorithm: 4627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // 1) by checking children and other variables, find an anchor coordinate and an anchor 4637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // item position. 4647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // 2) fill towards start, stacking from bottom 4657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // 3) fill towards end, stacking from top 4667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // 4) scroll to fulfill requirements like stack from bottom. 4677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // create layout state 4687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (DEBUG) { 4697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Log.d(TAG, "is pre layout:" + state.isPreLayout()); 4707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 4717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mPendingSavedState != null || mPendingScrollPosition != NO_POSITION) { 4727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (state.getItemCount() == 0) { 4737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas removeAndRecycleAllViews(recycler); 4747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return; 4757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 4767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 4777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) { 4787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mPendingScrollPosition = mPendingSavedState.mAnchorPosition; 4797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 4807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 4817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas ensureLayoutState(); 4827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mRecycle = false; 4837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // resolve layout direction 4847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas resolveShouldLayoutReverse(); 4857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 4867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (!mAnchorInfo.mValid || mPendingScrollPosition != NO_POSITION 4877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas || mPendingSavedState != null) { 4887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mAnchorInfo.reset(); 4897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mAnchorInfo.mLayoutFromEnd = mShouldReverseLayout ^ mStackFromEnd; 4907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // calculate anchor position and coordinate 4917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas updateAnchorInfoForLayout(recycler, state, mAnchorInfo); 4927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mAnchorInfo.mValid = true; 4937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 4947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (DEBUG) { 4957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Log.d(TAG, "Anchor info:" + mAnchorInfo); 4967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 4977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 4987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // LLM may decide to layout items for "extra" pixels to account for scrolling target, 4997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // caching or predictive animations. 5007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int extraForStart; 5017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int extraForEnd; 5027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int extra = getExtraLayoutSpace(state); 5037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // If the previous scroll delta was less than zero, the extra space should be laid out 5047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // at the start. Otherwise, it should be at the end. 5057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mLayoutState.mLastScrollDelta >= 0) { 5067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas extraForEnd = extra; 5077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas extraForStart = 0; 5087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 5097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas extraForStart = extra; 5107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas extraForEnd = 0; 5117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 5127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas extraForStart += mOrientationHelper.getStartAfterPadding(); 5137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas extraForEnd += mOrientationHelper.getEndPadding(); 5147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (state.isPreLayout() && mPendingScrollPosition != NO_POSITION 5157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas && mPendingScrollPositionOffset != INVALID_OFFSET) { 5167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // if the child is visible and we are going to move it around, we should layout 5177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // extra items in the opposite direction to make sure new items animate nicely 5187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // instead of just fading in 5197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final View existing = findViewByPosition(mPendingScrollPosition); 5207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (existing != null) { 5217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int current; 5227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int upcomingOffset; 5237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mShouldReverseLayout) { 5247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas current = mOrientationHelper.getEndAfterPadding() 5257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas - mOrientationHelper.getDecoratedEnd(existing); 5267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas upcomingOffset = current - mPendingScrollPositionOffset; 5277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 5287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas current = mOrientationHelper.getDecoratedStart(existing) 5297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas - mOrientationHelper.getStartAfterPadding(); 5307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas upcomingOffset = mPendingScrollPositionOffset - current; 5317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 5327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (upcomingOffset > 0) { 5337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas extraForStart += upcomingOffset; 5347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 5357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas extraForEnd -= upcomingOffset; 5367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 5377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 5387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 5397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int startOffset; 5407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int endOffset; 5417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int firstLayoutDirection; 5427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mAnchorInfo.mLayoutFromEnd) { 5437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL 5447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas : LayoutState.ITEM_DIRECTION_HEAD; 5457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 5467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD 5477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas : LayoutState.ITEM_DIRECTION_TAIL; 5487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 5497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 5507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas onAnchorReady(recycler, state, mAnchorInfo, firstLayoutDirection); 5517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas detachAndScrapAttachedViews(recycler); 5527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mInfinite = resolveIsInfinite(); 5537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mIsPreLayout = state.isPreLayout(); 5547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mAnchorInfo.mLayoutFromEnd) { 5557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // fill towards start 5567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas updateLayoutStateToFillStart(mAnchorInfo); 5577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mExtra = extraForStart; 5587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas fill(recycler, mLayoutState, state, false); 5597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas startOffset = mLayoutState.mOffset; 5607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int firstElement = mLayoutState.mCurrentPosition; 5617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mLayoutState.mAvailable > 0) { 5627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas extraForEnd += mLayoutState.mAvailable; 5637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 5647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // fill towards end 5657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas updateLayoutStateToFillEnd(mAnchorInfo); 5667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mExtra = extraForEnd; 5677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mCurrentPosition += mLayoutState.mItemDirection; 5687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas fill(recycler, mLayoutState, state, false); 5697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas endOffset = mLayoutState.mOffset; 5707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 5717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mLayoutState.mAvailable > 0) { 5727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // end could not consume all. add more items towards start 5737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas extraForStart = mLayoutState.mAvailable; 5747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas updateLayoutStateToFillStart(firstElement, startOffset); 5757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mExtra = extraForStart; 5767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas fill(recycler, mLayoutState, state, false); 5777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas startOffset = mLayoutState.mOffset; 5787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 5797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 5807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // fill towards end 5817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas updateLayoutStateToFillEnd(mAnchorInfo); 5827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mExtra = extraForEnd; 5837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas fill(recycler, mLayoutState, state, false); 5847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas endOffset = mLayoutState.mOffset; 5857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int lastElement = mLayoutState.mCurrentPosition; 5867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mLayoutState.mAvailable > 0) { 5877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas extraForStart += mLayoutState.mAvailable; 5887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 5897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // fill towards start 5907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas updateLayoutStateToFillStart(mAnchorInfo); 5917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mExtra = extraForStart; 5927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mCurrentPosition += mLayoutState.mItemDirection; 5937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas fill(recycler, mLayoutState, state, false); 5947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas startOffset = mLayoutState.mOffset; 5957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 5967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mLayoutState.mAvailable > 0) { 5977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas extraForEnd = mLayoutState.mAvailable; 5987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // start could not consume all it should. add more items towards end 5997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas updateLayoutStateToFillEnd(lastElement, endOffset); 6007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mExtra = extraForEnd; 6017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas fill(recycler, mLayoutState, state, false); 6027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas endOffset = mLayoutState.mOffset; 6037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 6047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 6057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 6067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // changes may cause gaps on the UI, try to fix them. 6077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // TODO we can probably avoid this if neither stackFromEnd/reverseLayout/RTL values have 6087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // changed 6097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (getChildCount() > 0) { 6107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // because layout from end may be changed by scroll to position 6117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // we re-calculate it. 6127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // find which side we should check for gaps. 6137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mShouldReverseLayout ^ mStackFromEnd) { 6147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int fixOffset = fixLayoutEndGap(endOffset, recycler, state, true); 6157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas startOffset += fixOffset; 6167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas endOffset += fixOffset; 6177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas fixOffset = fixLayoutStartGap(startOffset, recycler, state, false); 6187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas startOffset += fixOffset; 6197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas endOffset += fixOffset; 6207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 6217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int fixOffset = fixLayoutStartGap(startOffset, recycler, state, true); 6227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas startOffset += fixOffset; 6237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas endOffset += fixOffset; 6247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas fixOffset = fixLayoutEndGap(endOffset, recycler, state, false); 6257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas startOffset += fixOffset; 6267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas endOffset += fixOffset; 6277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 6287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 6297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas layoutForPredictiveAnimations(recycler, state, startOffset, endOffset); 6307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (!state.isPreLayout()) { 6317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mOrientationHelper.onLayoutComplete(); 6327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 6337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mAnchorInfo.reset(); 6347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 6357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLastStackFromEnd = mStackFromEnd; 6367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (DEBUG) { 6377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas validateChildOrder(); 6387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 6397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 6407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 6417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 6427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void onLayoutCompleted(RecyclerView.State state) { 6437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas super.onLayoutCompleted(state); 6447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mPendingSavedState = null; // we don't need this anymore 6457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mPendingScrollPosition = NO_POSITION; 6467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mPendingScrollPositionOffset = INVALID_OFFSET; 6477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mAnchorInfo.reset(); 6487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 6497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 6507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 6517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Method called when Anchor position is decided. Extending class can setup accordingly or 6527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * even update anchor info if necessary. 6537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param recycler The recycler for the layout 6547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param state The layout state 6557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param anchorInfo The mutable POJO that keeps the position and offset. 6567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param firstLayoutItemDirection The direction of the first layout filling in terms of adapter 6577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * indices. 6587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 6597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas void onAnchorReady(RecyclerView.Recycler recycler, RecyclerView.State state, 6607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas AnchorInfo anchorInfo, int firstLayoutItemDirection) { 6617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 6627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 6637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 6647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * If necessary, layouts new items for predictive animations 6657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 6667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private void layoutForPredictiveAnimations(RecyclerView.Recycler recycler, 6677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas RecyclerView.State state, int startOffset, int endOffset) { 6687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // If there are scrap children that we did not layout, we need to find where they did go 6697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // and layout them accordingly so that animations can work as expected. 6707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // This case may happen if new views are added or an existing view expands and pushes 6717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // another view out of bounds. 6727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (!state.willRunPredictiveAnimations() || getChildCount() == 0 || state.isPreLayout() 6737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas || !supportsPredictiveItemAnimations()) { 6747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return; 6757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 6767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // to make the logic simpler, we calculate the size of children and call fill. 6777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int scrapExtraStart = 0, scrapExtraEnd = 0; 6787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final List<RecyclerView.ViewHolder> scrapList = recycler.getScrapList(); 6797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int scrapSize = scrapList.size(); 6807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int firstChildPos = getPosition(getChildAt(0)); 6817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas for (int i = 0; i < scrapSize; i++) { 6827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas RecyclerView.ViewHolder scrap = scrapList.get(i); 6837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (scrap.isRemoved()) { 6847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas continue; 6857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 6867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int position = scrap.getLayoutPosition(); 6877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int direction = position < firstChildPos != mShouldReverseLayout 6887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas ? LayoutState.LAYOUT_START : LayoutState.LAYOUT_END; 6897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (direction == LayoutState.LAYOUT_START) { 6907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas scrapExtraStart += mOrientationHelper.getDecoratedMeasurement(scrap.itemView); 6917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 6927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas scrapExtraEnd += mOrientationHelper.getDecoratedMeasurement(scrap.itemView); 6937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 6947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 6957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 6967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (DEBUG) { 6977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Log.d(TAG, "for unused scrap, decided to add " + scrapExtraStart 6987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas + " towards start and " + scrapExtraEnd + " towards end"); 6997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 7007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mScrapList = scrapList; 7017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (scrapExtraStart > 0) { 7027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas View anchor = getChildClosestToStart(); 7037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas updateLayoutStateToFillStart(getPosition(anchor), startOffset); 7047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mExtra = scrapExtraStart; 7057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mAvailable = 0; 7067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.assignPositionFromScrapList(); 7077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas fill(recycler, mLayoutState, state, false); 7087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 7097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 7107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (scrapExtraEnd > 0) { 7117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas View anchor = getChildClosestToEnd(); 7127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas updateLayoutStateToFillEnd(getPosition(anchor), endOffset); 7137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mExtra = scrapExtraEnd; 7147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mAvailable = 0; 7157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.assignPositionFromScrapList(); 7167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas fill(recycler, mLayoutState, state, false); 7177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 7187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mScrapList = null; 7197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 7207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 7217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private void updateAnchorInfoForLayout(RecyclerView.Recycler recycler, RecyclerView.State state, 7227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas AnchorInfo anchorInfo) { 7237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (updateAnchorFromPendingData(state, anchorInfo)) { 7247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (DEBUG) { 7257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Log.d(TAG, "updated anchor info from pending information"); 7267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 7277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return; 7287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 7297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 7307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (updateAnchorFromChildren(recycler, state, anchorInfo)) { 7317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (DEBUG) { 7327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Log.d(TAG, "updated anchor info from existing children"); 7337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 7347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return; 7357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 7367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (DEBUG) { 7377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Log.d(TAG, "deciding anchor info for fresh state"); 7387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 7397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas anchorInfo.assignCoordinateFromPadding(); 7407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas anchorInfo.mPosition = mStackFromEnd ? state.getItemCount() - 1 : 0; 7417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 7427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 7437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 7447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Finds an anchor child from existing Views. Most of the time, this is the view closest to 7457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * start or end that has a valid position (e.g. not removed). 7467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p> 7477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * If a child has focus, it is given priority. 7487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 7497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private boolean updateAnchorFromChildren(RecyclerView.Recycler recycler, 7507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas RecyclerView.State state, AnchorInfo anchorInfo) { 7517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (getChildCount() == 0) { 7527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return false; 7537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 7547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final View focused = getFocusedChild(); 7557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (focused != null && anchorInfo.isViewValidAsAnchor(focused, state)) { 7567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas anchorInfo.assignFromViewAndKeepVisibleRect(focused); 7577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return true; 7587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 7597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mLastStackFromEnd != mStackFromEnd) { 7607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return false; 7617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 7627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas View referenceChild = anchorInfo.mLayoutFromEnd 7637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas ? findReferenceChildClosestToEnd(recycler, state) 7647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas : findReferenceChildClosestToStart(recycler, state); 7657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (referenceChild != null) { 7667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas anchorInfo.assignFromView(referenceChild); 7677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // If all visible views are removed in 1 pass, reference child might be out of bounds. 7687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // If that is the case, offset it back to 0 so that we use these pre-layout children. 7697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (!state.isPreLayout() && supportsPredictiveItemAnimations()) { 7707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // validate this child is at least partially visible. if not, offset it to start 7717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final boolean notVisible = 7727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mOrientationHelper.getDecoratedStart(referenceChild) >= mOrientationHelper 7737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas .getEndAfterPadding() 7747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas || mOrientationHelper.getDecoratedEnd(referenceChild) 7757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas < mOrientationHelper.getStartAfterPadding(); 7767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (notVisible) { 7777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas anchorInfo.mCoordinate = anchorInfo.mLayoutFromEnd 7787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas ? mOrientationHelper.getEndAfterPadding() 7797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas : mOrientationHelper.getStartAfterPadding(); 7807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 7817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 7827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return true; 7837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 7847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return false; 7857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 7867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 7877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 7887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * If there is a pending scroll position or saved states, updates the anchor info from that 7897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * data and returns true 7907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 7917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private boolean updateAnchorFromPendingData(RecyclerView.State state, AnchorInfo anchorInfo) { 7927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (state.isPreLayout() || mPendingScrollPosition == NO_POSITION) { 7937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return false; 7947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 7957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // validate scroll position 7967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mPendingScrollPosition < 0 || mPendingScrollPosition >= state.getItemCount()) { 7977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mPendingScrollPosition = NO_POSITION; 7987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mPendingScrollPositionOffset = INVALID_OFFSET; 7997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (DEBUG) { 8007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Log.e(TAG, "ignoring invalid scroll position " + mPendingScrollPosition); 8017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 8027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return false; 8037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 8047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 8057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // if child is visible, try to make it a reference child and ensure it is fully visible. 8067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // if child is not visible, align it depending on its virtual position. 8077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas anchorInfo.mPosition = mPendingScrollPosition; 8087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) { 8097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // Anchor offset depends on how that child was laid out. Here, we update it 8107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // according to our current view bounds 8117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas anchorInfo.mLayoutFromEnd = mPendingSavedState.mAnchorLayoutFromEnd; 8127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (anchorInfo.mLayoutFromEnd) { 8137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding() 8147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas - mPendingSavedState.mAnchorOffset; 8157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 8167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding() 8177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas + mPendingSavedState.mAnchorOffset; 8187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 8197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return true; 8207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 8217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 8227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mPendingScrollPositionOffset == INVALID_OFFSET) { 8237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas View child = findViewByPosition(mPendingScrollPosition); 8247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (child != null) { 8257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int childSize = mOrientationHelper.getDecoratedMeasurement(child); 8267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (childSize > mOrientationHelper.getTotalSpace()) { 8277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // item does not fit. fix depending on layout direction 8287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas anchorInfo.assignCoordinateFromPadding(); 8297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return true; 8307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 8317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int startGap = mOrientationHelper.getDecoratedStart(child) 8327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas - mOrientationHelper.getStartAfterPadding(); 8337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (startGap < 0) { 8347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding(); 8357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas anchorInfo.mLayoutFromEnd = false; 8367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return true; 8377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 8387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int endGap = mOrientationHelper.getEndAfterPadding() 8397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas - mOrientationHelper.getDecoratedEnd(child); 8407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (endGap < 0) { 8417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding(); 8427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas anchorInfo.mLayoutFromEnd = true; 8437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return true; 8447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 8457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas anchorInfo.mCoordinate = anchorInfo.mLayoutFromEnd 8467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas ? (mOrientationHelper.getDecoratedEnd(child) + mOrientationHelper 8477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas .getTotalSpaceChange()) 8487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas : mOrientationHelper.getDecoratedStart(child); 8497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { // item is not visible. 8507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (getChildCount() > 0) { 8517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // get position of any child, does not matter 8527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int pos = getPosition(getChildAt(0)); 8537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas anchorInfo.mLayoutFromEnd = mPendingScrollPosition < pos 8547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas == mShouldReverseLayout; 8557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 8567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas anchorInfo.assignCoordinateFromPadding(); 8577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 8587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return true; 8597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 8607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // override layout from end values for consistency 8617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas anchorInfo.mLayoutFromEnd = mShouldReverseLayout; 8627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // if this changes, we should update prepareForDrop as well 8637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mShouldReverseLayout) { 8647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding() 8657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas - mPendingScrollPositionOffset; 8667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 8677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding() 8687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas + mPendingScrollPositionOffset; 8697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 8707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return true; 8717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 8727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 8737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 8747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return The final offset amount for children 8757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 8767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private int fixLayoutEndGap(int endOffset, RecyclerView.Recycler recycler, 8777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas RecyclerView.State state, boolean canOffsetChildren) { 8787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int gap = mOrientationHelper.getEndAfterPadding() - endOffset; 8797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int fixOffset = 0; 8807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (gap > 0) { 8817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas fixOffset = -scrollBy(-gap, recycler, state); 8827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 8837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return 0; // nothing to fix 8847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 8857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // move offset according to scroll amount 8867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas endOffset += fixOffset; 8877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (canOffsetChildren) { 8887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // re-calculate gap, see if we could fix it 8897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas gap = mOrientationHelper.getEndAfterPadding() - endOffset; 8907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (gap > 0) { 8917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mOrientationHelper.offsetChildren(gap); 8927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return gap + fixOffset; 8937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 8947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 8957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return fixOffset; 8967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 8977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 8987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 8997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return The final offset amount for children 9007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 9017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private int fixLayoutStartGap(int startOffset, RecyclerView.Recycler recycler, 9027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas RecyclerView.State state, boolean canOffsetChildren) { 9037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int gap = startOffset - mOrientationHelper.getStartAfterPadding(); 9047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int fixOffset = 0; 9057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (gap > 0) { 9067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // check if we should fix this gap. 9077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas fixOffset = -scrollBy(gap, recycler, state); 9087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 9097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return 0; // nothing to fix 9107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 9117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas startOffset += fixOffset; 9127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (canOffsetChildren) { 9137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // re-calculate gap, see if we could fix it 9147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas gap = startOffset - mOrientationHelper.getStartAfterPadding(); 9157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (gap > 0) { 9167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mOrientationHelper.offsetChildren(-gap); 9177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return fixOffset - gap; 9187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 9197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 9207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return fixOffset; 9217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 9227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 9237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private void updateLayoutStateToFillEnd(AnchorInfo anchorInfo) { 9247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas updateLayoutStateToFillEnd(anchorInfo.mPosition, anchorInfo.mCoordinate); 9257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 9267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 9277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private void updateLayoutStateToFillEnd(int itemPosition, int offset) { 9287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mAvailable = mOrientationHelper.getEndAfterPadding() - offset; 9297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD : 9307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas LayoutState.ITEM_DIRECTION_TAIL; 9317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mCurrentPosition = itemPosition; 9327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mLayoutDirection = LayoutState.LAYOUT_END; 9337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mOffset = offset; 9347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mScrollingOffset = LayoutState.SCROLLING_OFFSET_NaN; 9357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 9367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 9377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private void updateLayoutStateToFillStart(AnchorInfo anchorInfo) { 9387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas updateLayoutStateToFillStart(anchorInfo.mPosition, anchorInfo.mCoordinate); 9397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 9407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 9417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private void updateLayoutStateToFillStart(int itemPosition, int offset) { 9427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mAvailable = offset - mOrientationHelper.getStartAfterPadding(); 9437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mCurrentPosition = itemPosition; 9447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL : 9457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas LayoutState.ITEM_DIRECTION_HEAD; 9467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mLayoutDirection = LayoutState.LAYOUT_START; 9477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mOffset = offset; 9487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mScrollingOffset = LayoutState.SCROLLING_OFFSET_NaN; 9497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 9507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 9517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 9527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas protected boolean isLayoutRTL() { 9537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; 9547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 9557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 9567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas void ensureLayoutState() { 9577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mLayoutState == null) { 9587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState = createLayoutState(); 9597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 9607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mOrientationHelper == null) { 9617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mOrientationHelper = OrientationHelper.createOrientationHelper(this, mOrientation); 9627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 9637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 9647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 9657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 9667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Test overrides this to plug some tracking and verification. 9677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 9687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return A new LayoutState 9697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 9707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas LayoutState createLayoutState() { 9717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return new LayoutState(); 9727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 9737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 9747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 9757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p>Scroll the RecyclerView to make the position visible.</p> 9767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 9777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p>RecyclerView will scroll the minimum amount that is necessary to make the 9787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * target position visible. If you are looking for a similar behavior to 9797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * {@link android.widget.ListView#setSelection(int)} or 9807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * {@link android.widget.ListView#setSelectionFromTop(int, int)}, use 9817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * {@link #scrollToPositionWithOffset(int, int)}.</p> 9827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 9837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p>Note that scroll position change will not be reflected until the next layout call.</p> 9847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 9857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param position Scroll to this adapter position 9867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @see #scrollToPositionWithOffset(int, int) 9877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 9887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 9897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void scrollToPosition(int position) { 9907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mPendingScrollPosition = position; 9917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mPendingScrollPositionOffset = INVALID_OFFSET; 9927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mPendingSavedState != null) { 9937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mPendingSavedState.invalidateAnchor(); 9947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 9957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas requestLayout(); 9967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 9977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 9987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 9997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Scroll to the specified adapter position with the given offset from resolved layout 10007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * start. Resolved layout start depends on {@link #getReverseLayout()}, 10017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * {@link View#getLayoutDirection()} and {@link #getStackFromEnd()}. 10027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p> 10037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * For example, if layout is {@link #VERTICAL} and {@link #getStackFromEnd()} is true, calling 10047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <code>scrollToPositionWithOffset(10, 20)</code> will layout such that 10057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <code>item[10]</code>'s bottom is 20 pixels above the RecyclerView's bottom. 10067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p> 10077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Note that scroll position change will not be reflected until the next layout call. 10087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p> 10097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * If you are just trying to make a position visible, use {@link #scrollToPosition(int)}. 10107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 10117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param position Index (starting at 0) of the reference item. 10127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param offset The distance (in pixels) between the start edge of the item view and 10137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * start edge of the RecyclerView. 10147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @see #setReverseLayout(boolean) 10157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @see #scrollToPosition(int) 10167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 10177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void scrollToPositionWithOffset(int position, int offset) { 10187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mPendingScrollPosition = position; 10197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mPendingScrollPositionOffset = offset; 10207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mPendingSavedState != null) { 10217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mPendingSavedState.invalidateAnchor(); 10227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 10237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas requestLayout(); 10247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 10257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 10267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 10277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 10287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * {@inheritDoc} 10297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 10307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 10317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, 10327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas RecyclerView.State state) { 10337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mOrientation == VERTICAL) { 10347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return 0; 10357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 10367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return scrollBy(dx, recycler, state); 10377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 10387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 10397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 10407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * {@inheritDoc} 10417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 10427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 10437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, 10447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas RecyclerView.State state) { 10457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mOrientation == HORIZONTAL) { 10467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return 0; 10477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 10487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return scrollBy(dy, recycler, state); 10497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 10507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 10517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 10527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public int computeHorizontalScrollOffset(RecyclerView.State state) { 10537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return computeScrollOffset(state); 10547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 10557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 10567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 10577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public int computeVerticalScrollOffset(RecyclerView.State state) { 10587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return computeScrollOffset(state); 10597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 10607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 10617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 10627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public int computeHorizontalScrollExtent(RecyclerView.State state) { 10637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return computeScrollExtent(state); 10647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 10657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 10667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 10677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public int computeVerticalScrollExtent(RecyclerView.State state) { 10687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return computeScrollExtent(state); 10697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 10707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 10717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 10727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public int computeHorizontalScrollRange(RecyclerView.State state) { 10737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return computeScrollRange(state); 10747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 10757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 10767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 10777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public int computeVerticalScrollRange(RecyclerView.State state) { 10787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return computeScrollRange(state); 10797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 10807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 10817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private int computeScrollOffset(RecyclerView.State state) { 10827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (getChildCount() == 0) { 10837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return 0; 10847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 10857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas ensureLayoutState(); 10867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return ScrollbarHelper.computeScrollOffset(state, mOrientationHelper, 10877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true), 10887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true), 10897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas this, mSmoothScrollbarEnabled, mShouldReverseLayout); 10907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 10917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 10927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private int computeScrollExtent(RecyclerView.State state) { 10937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (getChildCount() == 0) { 10947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return 0; 10957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 10967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas ensureLayoutState(); 10977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return ScrollbarHelper.computeScrollExtent(state, mOrientationHelper, 10987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true), 10997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true), 11007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas this, mSmoothScrollbarEnabled); 11017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 11027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 11037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private int computeScrollRange(RecyclerView.State state) { 11047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (getChildCount() == 0) { 11057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return 0; 11067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 11077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas ensureLayoutState(); 11087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return ScrollbarHelper.computeScrollRange(state, mOrientationHelper, 11097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true), 11107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true), 11117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas this, mSmoothScrollbarEnabled); 11127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 11137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 11147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 11157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * When smooth scrollbar is enabled, the position and size of the scrollbar thumb is computed 11167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * based on the number of visible pixels in the visible items. This however assumes that all 11177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * list items have similar or equal widths or heights (depending on list orientation). 11187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * If you use a list in which items have different dimensions, the scrollbar will change 11197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * appearance as the user scrolls through the list. To avoid this issue, you need to disable 11207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * this property. 11217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 11227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * When smooth scrollbar is disabled, the position and size of the scrollbar thumb is based 11237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * solely on the number of items in the adapter and the position of the visible items inside 11247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * the adapter. This provides a stable scrollbar as the user navigates through a list of items 11257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * with varying widths / heights. 11267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 11277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param enabled Whether or not to enable smooth scrollbar. 11287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 11297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @see #setSmoothScrollbarEnabled(boolean) 11307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 11317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void setSmoothScrollbarEnabled(boolean enabled) { 11327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mSmoothScrollbarEnabled = enabled; 11337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 11347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 11357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 11367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Returns the current state of the smooth scrollbar feature. It is enabled by default. 11377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 11387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return True if smooth scrollbar is enabled, false otherwise. 11397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 11407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @see #setSmoothScrollbarEnabled(boolean) 11417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 11427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public boolean isSmoothScrollbarEnabled() { 11437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return mSmoothScrollbarEnabled; 11447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 11457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 11467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private void updateLayoutState(int layoutDirection, int requiredSpace, 11477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas boolean canUseExistingSpace, RecyclerView.State state) { 11487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // If parent provides a hint, don't measure unlimited. 11497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mInfinite = resolveIsInfinite(); 11507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mExtra = getExtraLayoutSpace(state); 11517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mLayoutDirection = layoutDirection; 11527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int scrollingOffset; 11537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (layoutDirection == LayoutState.LAYOUT_END) { 11547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mExtra += mOrientationHelper.getEndPadding(); 11557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // get the first child in the direction we are going 11567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final View child = getChildClosestToEnd(); 11577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // the direction in which we are traversing children 11587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD 11597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas : LayoutState.ITEM_DIRECTION_TAIL; 11607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection; 11617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mOffset = mOrientationHelper.getDecoratedEnd(child); 11627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // calculate how much we can scroll without adding new children (independent of layout) 11637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas scrollingOffset = mOrientationHelper.getDecoratedEnd(child) 11647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas - mOrientationHelper.getEndAfterPadding(); 11657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 11667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 11677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final View child = getChildClosestToStart(); 11687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mExtra += mOrientationHelper.getStartAfterPadding(); 11697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL 11707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas : LayoutState.ITEM_DIRECTION_HEAD; 11717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection; 11727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mOffset = mOrientationHelper.getDecoratedStart(child); 11737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas scrollingOffset = -mOrientationHelper.getDecoratedStart(child) 11747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas + mOrientationHelper.getStartAfterPadding(); 11757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 11767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mAvailable = requiredSpace; 11777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (canUseExistingSpace) { 11787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mAvailable -= scrollingOffset; 11797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 11807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mScrollingOffset = scrollingOffset; 11817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 11827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 11837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas boolean resolveIsInfinite() { 11847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return mOrientationHelper.getMode() == View.MeasureSpec.UNSPECIFIED 11857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas && mOrientationHelper.getEnd() == 0; 11867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 11877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 11887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas void collectPrefetchPositionsForLayoutState(RecyclerView.State state, LayoutState layoutState, 11897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas LayoutPrefetchRegistry layoutPrefetchRegistry) { 11907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int pos = layoutState.mCurrentPosition; 11917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (pos >= 0 && pos < state.getItemCount()) { 11927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas layoutPrefetchRegistry.addPosition(pos, layoutState.mScrollingOffset); 11937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 11947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 11957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 11967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 11977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void collectInitialPrefetchPositions(int adapterItemCount, 11987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas LayoutPrefetchRegistry layoutPrefetchRegistry) { 11997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final boolean fromEnd; 12007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int anchorPos; 12017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) { 12027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // use restored state, since it hasn't been resolved yet 12037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas fromEnd = mPendingSavedState.mAnchorLayoutFromEnd; 12047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas anchorPos = mPendingSavedState.mAnchorPosition; 12057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 12067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas resolveShouldLayoutReverse(); 12077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas fromEnd = mShouldReverseLayout; 12087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mPendingScrollPosition == NO_POSITION) { 12097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas anchorPos = fromEnd ? adapterItemCount - 1 : 0; 12107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 12117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas anchorPos = mPendingScrollPosition; 12127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 12137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 12147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 12157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int direction = fromEnd 12167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas ? LayoutState.ITEM_DIRECTION_HEAD 12177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas : LayoutState.ITEM_DIRECTION_TAIL; 12187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int targetPos = anchorPos; 12197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas for (int i = 0; i < mInitialItemPrefetchCount; i++) { 12207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (targetPos >= 0 && targetPos < adapterItemCount) { 12217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas layoutPrefetchRegistry.addPosition(targetPos, 0); 12227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 12237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas break; // no more to prefetch 12247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 12257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas targetPos += direction; 12267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 12277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 12287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 12297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 12307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Sets the number of items to prefetch in 12317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)}, which defines 12327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * how many inner items should be prefetched when this LayoutManager's RecyclerView 12337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * is nested inside another RecyclerView. 12347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 12357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p>Set this value to the number of items this inner LayoutManager will display when it is 12367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * first scrolled into the viewport. RecyclerView will attempt to prefetch that number of items 12377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * so they are ready, avoiding jank as the inner RecyclerView is scrolled into the viewport.</p> 12387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 12397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p>For example, take a vertically scrolling RecyclerView with horizontally scrolling inner 12407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * RecyclerViews. The rows always have 4 items visible in them (or 5 if not aligned). Passing 12417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <code>4</code> to this method for each inner RecyclerView's LinearLayoutManager will enable 12427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * RecyclerView's prefetching feature to do create/bind work for 4 views within a row early, 12437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * before it is scrolled on screen, instead of just the default 2.</p> 12447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 12457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p>Calling this method does nothing unless the LayoutManager is in a RecyclerView 12467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * nested in another RecyclerView.</p> 12477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 12487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p class="note"><strong>Note:</strong> Setting this value to be larger than the number of 12497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * views that will be visible in this view can incur unnecessary bind work, and an increase to 12507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * the number of Views created and in active use.</p> 12517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 12527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param itemCount Number of items to prefetch 12537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 12547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @see #isItemPrefetchEnabled() 12557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @see #getInitialItemPrefetchCount() 12567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @see #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry) 12577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 12587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void setInitialPrefetchItemCount(int itemCount) { 12597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mInitialItemPrefetchCount = itemCount; 12607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 12617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 12627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 12637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Gets the number of items to prefetch in 12647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)}, which defines 12657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * how many inner items should be prefetched when this LayoutManager's RecyclerView 12667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * is nested inside another RecyclerView. 12677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 12687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @see #isItemPrefetchEnabled() 12697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @see #setInitialPrefetchItemCount(int) 12707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @see #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry) 12717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 12727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return number of items to prefetch. 12737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 12747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public int getInitialItemPrefetchCount() { 12757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return mInitialItemPrefetchCount; 12767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 12777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 12787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 12797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state, 12807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas LayoutPrefetchRegistry layoutPrefetchRegistry) { 12817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int delta = (mOrientation == HORIZONTAL) ? dx : dy; 12827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (getChildCount() == 0 || delta == 0) { 12837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // can't support this scroll, so don't bother prefetching 12847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return; 12857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 12867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 12877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int layoutDirection = delta > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START; 12887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int absDy = Math.abs(delta); 12897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas updateLayoutState(layoutDirection, absDy, true, state); 12907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas collectPrefetchPositionsForLayoutState(state, mLayoutState, layoutPrefetchRegistry); 12917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 12927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 12937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { 12947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (getChildCount() == 0 || dy == 0) { 12957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return 0; 12967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 12977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mRecycle = true; 12987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas ensureLayoutState(); 12997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int layoutDirection = dy > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START; 13007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int absDy = Math.abs(dy); 13017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas updateLayoutState(layoutDirection, absDy, true, state); 13027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int consumed = mLayoutState.mScrollingOffset 13037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas + fill(recycler, mLayoutState, state, false); 13047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (consumed < 0) { 13057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (DEBUG) { 13067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Log.d(TAG, "Don't have any more elements to scroll"); 13077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 13087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return 0; 13097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 13107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int scrolled = absDy > consumed ? layoutDirection * consumed : dy; 13117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mOrientationHelper.offsetChildren(-scrolled); 13127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (DEBUG) { 13137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Log.d(TAG, "scroll req: " + dy + " scrolled: " + scrolled); 13147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 13157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mLastScrollDelta = scrolled; 13167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return scrolled; 13177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 13187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 13197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 13207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void assertNotInLayoutOrScroll(String message) { 13217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mPendingSavedState == null) { 13227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas super.assertNotInLayoutOrScroll(message); 13237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 13247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 13257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 13267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 13277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Recycles children between given indices. 13287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 13297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param startIndex inclusive 13307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param endIndex exclusive 13317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 13327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private void recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex) { 13337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (startIndex == endIndex) { 13347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return; 13357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 13367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (DEBUG) { 13377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Log.d(TAG, "Recycling " + Math.abs(startIndex - endIndex) + " items"); 13387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 13397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (endIndex > startIndex) { 13407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas for (int i = endIndex - 1; i >= startIndex; i--) { 13417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas removeAndRecycleViewAt(i, recycler); 13427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 13437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 13447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas for (int i = startIndex; i > endIndex; i--) { 13457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas removeAndRecycleViewAt(i, recycler); 13467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 13477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 13487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 13497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 13507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 13517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Recycles views that went out of bounds after scrolling towards the end of the layout. 13527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p> 13537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Checks both layout position and visible position to guarantee that the view is not visible. 13547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 13557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param recycler Recycler instance of {@link com.android.internal.widget.RecyclerView} 13567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param dt This can be used to add additional padding to the visible area. This is used 13577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * to detect children that will go out of bounds after scrolling, without 13587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * actually moving them. 13597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 13607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private void recycleViewsFromStart(RecyclerView.Recycler recycler, int dt) { 13617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (dt < 0) { 13627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (DEBUG) { 13637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Log.d(TAG, "Called recycle from start with a negative value. This might happen" 13647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas + " during layout changes but may be sign of a bug"); 13657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 13667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return; 13677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 13687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // ignore padding, ViewGroup may not clip children. 13697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int limit = dt; 13707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int childCount = getChildCount(); 13717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mShouldReverseLayout) { 13727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas for (int i = childCount - 1; i >= 0; i--) { 13737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas View child = getChildAt(i); 13747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mOrientationHelper.getDecoratedEnd(child) > limit 13757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas || mOrientationHelper.getTransformedEndWithDecoration(child) > limit) { 13767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // stop here 13777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas recycleChildren(recycler, childCount - 1, i); 13787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return; 13797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 13807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 13817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 13827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas for (int i = 0; i < childCount; i++) { 13837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas View child = getChildAt(i); 13847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mOrientationHelper.getDecoratedEnd(child) > limit 13857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas || mOrientationHelper.getTransformedEndWithDecoration(child) > limit) { 13867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // stop here 13877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas recycleChildren(recycler, 0, i); 13887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return; 13897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 13907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 13917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 13927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 13937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 13947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 13957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 13967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Recycles views that went out of bounds after scrolling towards the start of the layout. 13977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p> 13987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Checks both layout position and visible position to guarantee that the view is not visible. 13997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 14007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param recycler Recycler instance of {@link com.android.internal.widget.RecyclerView} 14017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param dt This can be used to add additional padding to the visible area. This is used 14027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * to detect children that will go out of bounds after scrolling, without 14037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * actually moving them. 14047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 14057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private void recycleViewsFromEnd(RecyclerView.Recycler recycler, int dt) { 14067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int childCount = getChildCount(); 14077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (dt < 0) { 14087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (DEBUG) { 14097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Log.d(TAG, "Called recycle from end with a negative value. This might happen" 14107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas + " during layout changes but may be sign of a bug"); 14117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 14127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return; 14137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 14147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int limit = mOrientationHelper.getEnd() - dt; 14157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mShouldReverseLayout) { 14167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas for (int i = 0; i < childCount; i++) { 14177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas View child = getChildAt(i); 14187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mOrientationHelper.getDecoratedStart(child) < limit 14197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas || mOrientationHelper.getTransformedStartWithDecoration(child) < limit) { 14207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // stop here 14217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas recycleChildren(recycler, 0, i); 14227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return; 14237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 14247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 14257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 14267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas for (int i = childCount - 1; i >= 0; i--) { 14277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas View child = getChildAt(i); 14287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mOrientationHelper.getDecoratedStart(child) < limit 14297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas || mOrientationHelper.getTransformedStartWithDecoration(child) < limit) { 14307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // stop here 14317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas recycleChildren(recycler, childCount - 1, i); 14327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return; 14337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 14347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 14357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 14367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 14377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 14387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 14397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Helper method to call appropriate recycle method depending on current layout direction 14407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 14417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param recycler Current recycler that is attached to RecyclerView 14427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param layoutState Current layout state. Right now, this object does not change but 14437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * we may consider moving it out of this view so passing around as a 14447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * parameter for now, rather than accessing {@link #mLayoutState} 14457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @see #recycleViewsFromStart(com.android.internal.widget.RecyclerView.Recycler, int) 14467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @see #recycleViewsFromEnd(com.android.internal.widget.RecyclerView.Recycler, int) 14477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @see com.android.internal.widget.LinearLayoutManager.LayoutState#mLayoutDirection 14487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 14497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) { 14507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (!layoutState.mRecycle || layoutState.mInfinite) { 14517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return; 14527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 14537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { 14547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas recycleViewsFromEnd(recycler, layoutState.mScrollingOffset); 14557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 14567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas recycleViewsFromStart(recycler, layoutState.mScrollingOffset); 14577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 14587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 14597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 14607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 14617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * The magic functions :). Fills the given layout, defined by the layoutState. This is fairly 14627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * independent from the rest of the {@link com.android.internal.widget.LinearLayoutManager} 14637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * and with little change, can be made publicly available as a helper class. 14647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 14657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param recycler Current recycler that is attached to RecyclerView 14667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param layoutState Configuration on how we should fill out the available space. 14677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param state Context passed by the RecyclerView to control scroll steps. 14687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param stopOnFocusable If true, filling stops in the first focusable new child 14697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return Number of pixels that it added. Useful for scroll functions. 14707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 14717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int fill(RecyclerView.Recycler recycler, LayoutState layoutState, 14727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas RecyclerView.State state, boolean stopOnFocusable) { 14737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // max offset we should set is mFastScroll + available 14747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int start = layoutState.mAvailable; 14757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) { 14767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // TODO ugly bug fix. should not happen 14777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (layoutState.mAvailable < 0) { 14787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas layoutState.mScrollingOffset += layoutState.mAvailable; 14797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 14807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas recycleByLayoutState(recycler, layoutState); 14817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 14827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int remainingSpace = layoutState.mAvailable + layoutState.mExtra; 14837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas LayoutChunkResult layoutChunkResult = mLayoutChunkResult; 14847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) { 14857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas layoutChunkResult.resetInternal(); 14867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas layoutChunk(recycler, state, layoutState, layoutChunkResult); 14877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (layoutChunkResult.mFinished) { 14887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas break; 14897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 14907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection; 14917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 14927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Consume the available space if: 14937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * * layoutChunk did not request to be ignored 14947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * * OR we are laying out scrap children 14957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * * OR we are not doing pre-layout 14967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 14977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (!layoutChunkResult.mIgnoreConsumed || mLayoutState.mScrapList != null 14987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas || !state.isPreLayout()) { 14997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas layoutState.mAvailable -= layoutChunkResult.mConsumed; 15007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // we keep a separate remaining space because mAvailable is important for recycling 15017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas remainingSpace -= layoutChunkResult.mConsumed; 15027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 15037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 15047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) { 15057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas layoutState.mScrollingOffset += layoutChunkResult.mConsumed; 15067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (layoutState.mAvailable < 0) { 15077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas layoutState.mScrollingOffset += layoutState.mAvailable; 15087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 15097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas recycleByLayoutState(recycler, layoutState); 15107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 15117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (stopOnFocusable && layoutChunkResult.mFocusable) { 15127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas break; 15137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 15147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 15157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (DEBUG) { 15167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas validateChildOrder(); 15177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 15187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return start - layoutState.mAvailable; 15197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 15207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 15217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, 15227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas LayoutState layoutState, LayoutChunkResult result) { 15237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas View view = layoutState.next(recycler); 15247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (view == null) { 15257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (DEBUG && layoutState.mScrapList == null) { 15267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas throw new RuntimeException("received null view when unexpected"); 15277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 15287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // if we are laying out views in scrap, this may return null which means there is 15297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // no more items to layout. 15307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas result.mFinished = true; 15317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return; 15327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 15337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas LayoutParams params = (LayoutParams) view.getLayoutParams(); 15347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (layoutState.mScrapList == null) { 15357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mShouldReverseLayout == (layoutState.mLayoutDirection 15367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas == LayoutState.LAYOUT_START)) { 15377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas addView(view); 15387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 15397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas addView(view, 0); 15407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 15417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 15427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mShouldReverseLayout == (layoutState.mLayoutDirection 15437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas == LayoutState.LAYOUT_START)) { 15447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas addDisappearingView(view); 15457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 15467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas addDisappearingView(view, 0); 15477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 15487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 15497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas measureChildWithMargins(view, 0, 0); 15507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view); 15517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int left, top, right, bottom; 15527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mOrientation == VERTICAL) { 15537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (isLayoutRTL()) { 15547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas right = getWidth() - getPaddingRight(); 15557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas left = right - mOrientationHelper.getDecoratedMeasurementInOther(view); 15567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 15577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas left = getPaddingLeft(); 15587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas right = left + mOrientationHelper.getDecoratedMeasurementInOther(view); 15597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 15607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { 15617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas bottom = layoutState.mOffset; 15627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas top = layoutState.mOffset - result.mConsumed; 15637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 15647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas top = layoutState.mOffset; 15657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas bottom = layoutState.mOffset + result.mConsumed; 15667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 15677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 15687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas top = getPaddingTop(); 15697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view); 15707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 15717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { 15727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas right = layoutState.mOffset; 15737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas left = layoutState.mOffset - result.mConsumed; 15747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 15757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas left = layoutState.mOffset; 15767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas right = layoutState.mOffset + result.mConsumed; 15777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 15787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 15797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // We calculate everything with View's bounding box (which includes decor and margins) 15807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // To calculate correct layout position, we subtract margins. 15817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas layoutDecoratedWithMargins(view, left, top, right, bottom); 15827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (DEBUG) { 15837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:" 15847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas + (left + params.leftMargin) + ", t:" + (top + params.topMargin) + ", r:" 15857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas + (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin)); 15867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 15877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // Consume the available space if the view is not removed OR changed 15887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (params.isItemRemoved() || params.isItemChanged()) { 15897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas result.mIgnoreConsumed = true; 15907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 15917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas result.mFocusable = view.isFocusable(); 15927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 15937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 15947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 15957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas boolean shouldMeasureTwice() { 15967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return getHeightMode() != View.MeasureSpec.EXACTLY 15977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas && getWidthMode() != View.MeasureSpec.EXACTLY 15987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas && hasFlexibleChildInBothOrientations(); 15997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 16007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 16017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 16027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Converts a focusDirection to orientation. 16037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 16047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param focusDirection One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN}, 16057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, 16067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD} 16077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * or 0 for not applicable 16087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return {@link LayoutState#LAYOUT_START} or {@link LayoutState#LAYOUT_END} if focus direction 16097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * is applicable to current state, {@link LayoutState#INVALID_LAYOUT} otherwise. 16107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 16117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int convertFocusDirectionToLayoutDirection(int focusDirection) { 16127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas switch (focusDirection) { 16137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas case View.FOCUS_BACKWARD: 16147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mOrientation == VERTICAL) { 16157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return LayoutState.LAYOUT_START; 16167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else if (isLayoutRTL()) { 16177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return LayoutState.LAYOUT_END; 16187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 16197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return LayoutState.LAYOUT_START; 16207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 16217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas case View.FOCUS_FORWARD: 16227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mOrientation == VERTICAL) { 16237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return LayoutState.LAYOUT_END; 16247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else if (isLayoutRTL()) { 16257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return LayoutState.LAYOUT_START; 16267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 16277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return LayoutState.LAYOUT_END; 16287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 16297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas case View.FOCUS_UP: 16307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return mOrientation == VERTICAL ? LayoutState.LAYOUT_START 16317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas : LayoutState.INVALID_LAYOUT; 16327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas case View.FOCUS_DOWN: 16337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return mOrientation == VERTICAL ? LayoutState.LAYOUT_END 16347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas : LayoutState.INVALID_LAYOUT; 16357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas case View.FOCUS_LEFT: 16367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return mOrientation == HORIZONTAL ? LayoutState.LAYOUT_START 16377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas : LayoutState.INVALID_LAYOUT; 16387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas case View.FOCUS_RIGHT: 16397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return mOrientation == HORIZONTAL ? LayoutState.LAYOUT_END 16407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas : LayoutState.INVALID_LAYOUT; 16417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas default: 16427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (DEBUG) { 16437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Log.d(TAG, "Unknown focus request:" + focusDirection); 16447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 16457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return LayoutState.INVALID_LAYOUT; 16467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 16477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 16487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 16497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 16507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 16517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Convenience method to find the child closes to start. Caller should check it has enough 16527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * children. 16537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 16547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return The child closes to start of the layout from user's perspective. 16557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 16567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private View getChildClosestToStart() { 16577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return getChildAt(mShouldReverseLayout ? getChildCount() - 1 : 0); 16587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 16597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 16607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 16617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Convenience method to find the child closes to end. Caller should check it has enough 16627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * children. 16637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 16647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return The child closes to end of the layout from user's perspective. 16657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 16667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private View getChildClosestToEnd() { 16677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return getChildAt(mShouldReverseLayout ? 0 : getChildCount() - 1); 16687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 16697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 16707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 16717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Convenience method to find the visible child closes to start. Caller should check if it has 16727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * enough children. 16737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 16747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param completelyVisible Whether child should be completely visible or not 16757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return The first visible child closest to start of the layout from user's perspective. 16767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 16777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private View findFirstVisibleChildClosestToStart(boolean completelyVisible, 16787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas boolean acceptPartiallyVisible) { 16797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mShouldReverseLayout) { 16807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return findOneVisibleChild(getChildCount() - 1, -1, completelyVisible, 16817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas acceptPartiallyVisible); 16827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 16837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return findOneVisibleChild(0, getChildCount(), completelyVisible, 16847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas acceptPartiallyVisible); 16857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 16867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 16877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 16887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 16897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Convenience method to find the visible child closes to end. Caller should check if it has 16907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * enough children. 16917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 16927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @param completelyVisible Whether child should be completely visible or not 16937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return The first visible child closest to end of the layout from user's perspective. 16947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 16957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private View findFirstVisibleChildClosestToEnd(boolean completelyVisible, 16967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas boolean acceptPartiallyVisible) { 16977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mShouldReverseLayout) { 16987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return findOneVisibleChild(0, getChildCount(), completelyVisible, 16997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas acceptPartiallyVisible); 17007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 17017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return findOneVisibleChild(getChildCount() - 1, -1, completelyVisible, 17027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas acceptPartiallyVisible); 17037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 17047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 17057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 17067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 17077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 17087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Among the children that are suitable to be considered as an anchor child, returns the one 17097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * closest to the end of the layout. 17107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p> 17117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Due to ambiguous adapter updates or children being removed, some children's positions may be 17127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * invalid. This method is a best effort to find a position within adapter bounds if possible. 17137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p> 17147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * It also prioritizes children that are within the visible bounds. 17157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return A View that can be used an an anchor View. 17167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 17177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private View findReferenceChildClosestToEnd(RecyclerView.Recycler recycler, 17187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas RecyclerView.State state) { 17197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return mShouldReverseLayout ? findFirstReferenceChild(recycler, state) : 17207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas findLastReferenceChild(recycler, state); 17217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 17227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 17237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 17247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Among the children that are suitable to be considered as an anchor child, returns the one 17257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * closest to the start of the layout. 17267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p> 17277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Due to ambiguous adapter updates or children being removed, some children's positions may be 17287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * invalid. This method is a best effort to find a position within adapter bounds if possible. 17297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p> 17307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * It also prioritizes children that are within the visible bounds. 17317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 17327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return A View that can be used an an anchor View. 17337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 17347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private View findReferenceChildClosestToStart(RecyclerView.Recycler recycler, 17357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas RecyclerView.State state) { 17367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return mShouldReverseLayout ? findLastReferenceChild(recycler, state) : 17377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas findFirstReferenceChild(recycler, state); 17387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 17397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 17407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private View findFirstReferenceChild(RecyclerView.Recycler recycler, RecyclerView.State state) { 17417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return findReferenceChild(recycler, state, 0, getChildCount(), state.getItemCount()); 17427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 17437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 17447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private View findLastReferenceChild(RecyclerView.Recycler recycler, RecyclerView.State state) { 17457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return findReferenceChild(recycler, state, getChildCount() - 1, -1, state.getItemCount()); 17467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 17477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 17487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // overridden by GridLayoutManager 17497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas View findReferenceChild(RecyclerView.Recycler recycler, RecyclerView.State state, 17507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int start, int end, int itemCount) { 17517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas ensureLayoutState(); 17527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas View invalidMatch = null; 17537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas View outOfBoundsMatch = null; 17547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int boundsStart = mOrientationHelper.getStartAfterPadding(); 17557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int boundsEnd = mOrientationHelper.getEndAfterPadding(); 17567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int diff = end > start ? 1 : -1; 17577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas for (int i = start; i != end; i += diff) { 17587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final View view = getChildAt(i); 17597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int position = getPosition(view); 17607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (position >= 0 && position < itemCount) { 17617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (((LayoutParams) view.getLayoutParams()).isItemRemoved()) { 17627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (invalidMatch == null) { 17637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas invalidMatch = view; // removed item, least preferred 17647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 17657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else if (mOrientationHelper.getDecoratedStart(view) >= boundsEnd 17667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas || mOrientationHelper.getDecoratedEnd(view) < boundsStart) { 17677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (outOfBoundsMatch == null) { 17687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas outOfBoundsMatch = view; // item is not visible, less preferred 17697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 17707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 17717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return view; 17727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 17737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 17747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 17757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return outOfBoundsMatch != null ? outOfBoundsMatch : invalidMatch; 17767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 17777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 17787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 17797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Returns the adapter position of the first visible view. This position does not include 17807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * adapter changes that were dispatched after the last layout pass. 17817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p> 17827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Note that, this value is not affected by layout orientation or item order traversal. 17837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter, 17847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * not in the layout. 17857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p> 17867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * If RecyclerView has item decorators, they will be considered in calculations as well. 17877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p> 17887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * LayoutManager may pre-cache some views that are not necessarily visible. Those views 17897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * are ignored in this method. 17907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 17917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return The adapter position of the first visible item or {@link RecyclerView#NO_POSITION} if 17927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * there aren't any visible items. 17937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @see #findFirstCompletelyVisibleItemPosition() 17947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @see #findLastVisibleItemPosition() 17957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 17967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public int findFirstVisibleItemPosition() { 17977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final View child = findOneVisibleChild(0, getChildCount(), false, true); 17987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return child == null ? NO_POSITION : getPosition(child); 17997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 18007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 18017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 18027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Returns the adapter position of the first fully visible view. This position does not include 18037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * adapter changes that were dispatched after the last layout pass. 18047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p> 18057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Note that bounds check is only performed in the current orientation. That means, if 18067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * LayoutManager is horizontal, it will only check the view's left and right edges. 18077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 18087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return The adapter position of the first fully visible item or 18097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * {@link RecyclerView#NO_POSITION} if there aren't any visible items. 18107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @see #findFirstVisibleItemPosition() 18117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @see #findLastCompletelyVisibleItemPosition() 18127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 18137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public int findFirstCompletelyVisibleItemPosition() { 18147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final View child = findOneVisibleChild(0, getChildCount(), true, false); 18157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return child == null ? NO_POSITION : getPosition(child); 18167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 18177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 18187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 18197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Returns the adapter position of the last visible view. This position does not include 18207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * adapter changes that were dispatched after the last layout pass. 18217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p> 18227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Note that, this value is not affected by layout orientation or item order traversal. 18237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter, 18247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * not in the layout. 18257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p> 18267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * If RecyclerView has item decorators, they will be considered in calculations as well. 18277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p> 18287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * LayoutManager may pre-cache some views that are not necessarily visible. Those views 18297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * are ignored in this method. 18307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 18317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return The adapter position of the last visible view or {@link RecyclerView#NO_POSITION} if 18327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * there aren't any visible items. 18337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @see #findLastCompletelyVisibleItemPosition() 18347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @see #findFirstVisibleItemPosition() 18357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 18367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public int findLastVisibleItemPosition() { 18377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final View child = findOneVisibleChild(getChildCount() - 1, -1, false, true); 18387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return child == null ? NO_POSITION : getPosition(child); 18397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 18407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 18417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 18427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Returns the adapter position of the last fully visible view. This position does not include 18437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * adapter changes that were dispatched after the last layout pass. 18447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p> 18457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Note that bounds check is only performed in the current orientation. That means, if 18467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * LayoutManager is horizontal, it will only check the view's left and right edges. 18477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 18487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return The adapter position of the last fully visible view or 18497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * {@link RecyclerView#NO_POSITION} if there aren't any visible items. 18507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @see #findLastVisibleItemPosition() 18517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @see #findFirstCompletelyVisibleItemPosition() 18527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 18537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public int findLastCompletelyVisibleItemPosition() { 18547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final View child = findOneVisibleChild(getChildCount() - 1, -1, true, false); 18557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return child == null ? NO_POSITION : getPosition(child); 18567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 18577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 18587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas View findOneVisibleChild(int fromIndex, int toIndex, boolean completelyVisible, 18597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas boolean acceptPartiallyVisible) { 18607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas ensureLayoutState(); 18617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int start = mOrientationHelper.getStartAfterPadding(); 18627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int end = mOrientationHelper.getEndAfterPadding(); 18637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int next = toIndex > fromIndex ? 1 : -1; 18647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas View partiallyVisible = null; 18657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas for (int i = fromIndex; i != toIndex; i += next) { 18667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final View child = getChildAt(i); 18677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int childStart = mOrientationHelper.getDecoratedStart(child); 18687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int childEnd = mOrientationHelper.getDecoratedEnd(child); 18697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (childStart < end && childEnd > start) { 18707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (completelyVisible) { 18717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (childStart >= start && childEnd <= end) { 18727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return child; 18737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else if (acceptPartiallyVisible && partiallyVisible == null) { 18747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas partiallyVisible = child; 18757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 18767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 18777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return child; 18787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 18797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 18807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 18817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return partiallyVisible; 18827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 18837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 18847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 18857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public View onFocusSearchFailed(View focused, int focusDirection, 18867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas RecyclerView.Recycler recycler, RecyclerView.State state) { 18877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas resolveShouldLayoutReverse(); 18887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (getChildCount() == 0) { 18897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return null; 18907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 18917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 18927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int layoutDir = convertFocusDirectionToLayoutDirection(focusDirection); 18937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (layoutDir == LayoutState.INVALID_LAYOUT) { 18947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return null; 18957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 18967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas ensureLayoutState(); 18977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final View referenceChild; 18987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (layoutDir == LayoutState.LAYOUT_START) { 18997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas referenceChild = findReferenceChildClosestToStart(recycler, state); 19007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 19017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas referenceChild = findReferenceChildClosestToEnd(recycler, state); 19027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 19037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (referenceChild == null) { 19047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (DEBUG) { 19057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Log.d(TAG, 19067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas "Cannot find a child with a valid position to be used for focus search."); 19077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 19087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return null; 19097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 19107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas ensureLayoutState(); 19117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int maxScroll = (int) (MAX_SCROLL_FACTOR * mOrientationHelper.getTotalSpace()); 19127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas updateLayoutState(layoutDir, maxScroll, false, state); 19137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mScrollingOffset = LayoutState.SCROLLING_OFFSET_NaN; 19147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutState.mRecycle = false; 19157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas fill(recycler, mLayoutState, state, true); 19167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final View nextFocus; 19177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (layoutDir == LayoutState.LAYOUT_START) { 19187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas nextFocus = getChildClosestToStart(); 19197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 19207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas nextFocus = getChildClosestToEnd(); 19217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 19227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (nextFocus == referenceChild || !nextFocus.isFocusable()) { 19237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return null; 19247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 19257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return nextFocus; 19267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 19277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 19287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 19297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Used for debugging. 19307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Logs the internal representation of children to default logger. 19317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 19327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private void logChildren() { 19337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Log.d(TAG, "internal representation of views on the screen"); 19347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas for (int i = 0; i < getChildCount(); i++) { 19357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas View child = getChildAt(i); 19367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Log.d(TAG, "item " + getPosition(child) + ", coord:" 19377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas + mOrientationHelper.getDecoratedStart(child)); 19387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 19397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Log.d(TAG, "=============="); 19407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 19417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 19427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 19437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Used for debugging. 19447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Validates that child views are laid out in correct order. This is important because rest of 19457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * the algorithm relies on this constraint. 19467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 19477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * In default layout, child 0 should be closest to screen position 0 and last child should be 19487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * closest to position WIDTH or HEIGHT. 19497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * In reverse layout, last child should be closes to screen position 0 and first child should 19507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * be closest to position WIDTH or HEIGHT 19517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 19527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas void validateChildOrder() { 19537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Log.d(TAG, "validating child count " + getChildCount()); 19547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (getChildCount() < 1) { 19557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return; 19567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 19577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int lastPos = getPosition(getChildAt(0)); 19587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int lastScreenLoc = mOrientationHelper.getDecoratedStart(getChildAt(0)); 19597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mShouldReverseLayout) { 19607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas for (int i = 1; i < getChildCount(); i++) { 19617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas View child = getChildAt(i); 19627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int pos = getPosition(child); 19637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int screenLoc = mOrientationHelper.getDecoratedStart(child); 19647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (pos < lastPos) { 19657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas logChildren(); 19667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas throw new RuntimeException("detected invalid position. loc invalid? " 19677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas + (screenLoc < lastScreenLoc)); 19687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 19697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (screenLoc > lastScreenLoc) { 19707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas logChildren(); 19717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas throw new RuntimeException("detected invalid location"); 19727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 19737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 19747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 19757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas for (int i = 1; i < getChildCount(); i++) { 19767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas View child = getChildAt(i); 19777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int pos = getPosition(child); 19787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int screenLoc = mOrientationHelper.getDecoratedStart(child); 19797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (pos < lastPos) { 19807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas logChildren(); 19817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas throw new RuntimeException("detected invalid position. loc invalid? " 19827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas + (screenLoc < lastScreenLoc)); 19837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 19847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (screenLoc < lastScreenLoc) { 19857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas logChildren(); 19867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas throw new RuntimeException("detected invalid location"); 19877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 19887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 19897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 19907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 19917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 19927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 19937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public boolean supportsPredictiveItemAnimations() { 19947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return mPendingSavedState == null && mLastStackFromEnd == mStackFromEnd; 19957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 19967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 19977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 19987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @hide This method should be called by ItemTouchHelper only. 19997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 20007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 20017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void prepareForDrop(View view, View target, int x, int y) { 20027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas assertNotInLayoutOrScroll("Cannot drop a view during a scroll or layout calculation"); 20037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas ensureLayoutState(); 20047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas resolveShouldLayoutReverse(); 20057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int myPos = getPosition(view); 20067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int targetPos = getPosition(target); 20077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int dropDirection = myPos < targetPos ? LayoutState.ITEM_DIRECTION_TAIL 20087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas : LayoutState.ITEM_DIRECTION_HEAD; 20097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mShouldReverseLayout) { 20107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (dropDirection == LayoutState.ITEM_DIRECTION_TAIL) { 20117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas scrollToPositionWithOffset(targetPos, 20127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mOrientationHelper.getEndAfterPadding() 20137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas - (mOrientationHelper.getDecoratedStart(target) 20147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas + mOrientationHelper.getDecoratedMeasurement(view))); 20157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 20167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas scrollToPositionWithOffset(targetPos, 20177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mOrientationHelper.getEndAfterPadding() 20187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas - mOrientationHelper.getDecoratedEnd(target)); 20197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 20207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 20217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (dropDirection == LayoutState.ITEM_DIRECTION_HEAD) { 20227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas scrollToPositionWithOffset(targetPos, mOrientationHelper.getDecoratedStart(target)); 20237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 20247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas scrollToPositionWithOffset(targetPos, 20257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mOrientationHelper.getDecoratedEnd(target) 20267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas - mOrientationHelper.getDecoratedMeasurement(view)); 20277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 20287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 20297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 20307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 20317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 20327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Helper class that keeps temporary state while {LayoutManager} is filling out the empty 20337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * space. 20347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 20357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas static class LayoutState { 20367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 20377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas static final String TAG = "LLM#LayoutState"; 20387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 20397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas static final int LAYOUT_START = -1; 20407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 20417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas static final int LAYOUT_END = 1; 20427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 20437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas static final int INVALID_LAYOUT = Integer.MIN_VALUE; 20447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 20457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas static final int ITEM_DIRECTION_HEAD = -1; 20467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 20477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas static final int ITEM_DIRECTION_TAIL = 1; 20487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 20497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas static final int SCROLLING_OFFSET_NaN = Integer.MIN_VALUE; 20507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 20517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 20527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * We may not want to recycle children in some cases (e.g. layout) 20537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 20547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas boolean mRecycle = true; 20557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 20567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 20577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Pixel offset where layout should start 20587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 20597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int mOffset; 20607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 20617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 20627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Number of pixels that we should fill, in the layout direction. 20637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 20647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int mAvailable; 20657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 20667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 20677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Current position on the adapter to get the next item. 20687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 20697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int mCurrentPosition; 20707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 20717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 20727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Defines the direction in which the data adapter is traversed. 20737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Should be {@link #ITEM_DIRECTION_HEAD} or {@link #ITEM_DIRECTION_TAIL} 20747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 20757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int mItemDirection; 20767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 20777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 20787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Defines the direction in which the layout is filled. 20797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Should be {@link #LAYOUT_START} or {@link #LAYOUT_END} 20807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 20817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int mLayoutDirection; 20827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 20837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 20847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Used when LayoutState is constructed in a scrolling state. 20857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * It should be set the amount of scrolling we can make without creating a new view. 20867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Settings this is required for efficient view recycling. 20877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 20887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int mScrollingOffset; 20897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 20907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 20917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Used if you want to pre-layout items that are not yet visible. 20927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * The difference with {@link #mAvailable} is that, when recycling, distance laid out for 20937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * {@link #mExtra} is not considered to avoid recycling visible children. 20947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 20957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int mExtra = 0; 20967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 20977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 20987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Equal to {@link RecyclerView.State#isPreLayout()}. When consuming scrap, if this value 20997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * is set to true, we skip removed views since they should not be laid out in post layout 21007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * step. 21017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 21027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas boolean mIsPreLayout = false; 21037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 21047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 21057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * The most recent {@link #scrollBy(int, RecyclerView.Recycler, RecyclerView.State)} 21067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * amount. 21077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 21087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int mLastScrollDelta; 21097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 21107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 21117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * When LLM needs to layout particular views, it sets this list in which case, LayoutState 21127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * will only return views from this list and return null if it cannot find an item. 21137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 21147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas List<RecyclerView.ViewHolder> mScrapList = null; 21157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 21167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 21177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Used when there is no limit in how many views can be laid out. 21187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 21197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas boolean mInfinite; 21207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 21217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 21227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return true if there are more items in the data adapter 21237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 21247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas boolean hasMore(RecyclerView.State state) { 21257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return mCurrentPosition >= 0 && mCurrentPosition < state.getItemCount(); 21267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 21277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 21287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 21297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Gets the view for the next element that we should layout. 21307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Also updates current item index to the next item, based on {@link #mItemDirection} 21317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 21327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return The next element that we should layout. 21337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 21347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas View next(RecyclerView.Recycler recycler) { 21357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mScrapList != null) { 21367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return nextViewFromScrapList(); 21377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 21387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final View view = recycler.getViewForPosition(mCurrentPosition); 21397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mCurrentPosition += mItemDirection; 21407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return view; 21417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 21427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 21437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 21447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Returns the next item from the scrap list. 21457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p> 21467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Upon finding a valid VH, sets current item position to VH.itemPosition + mItemDirection 21477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 21487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @return View if an item in the current position or direction exists if not null. 21497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 21507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private View nextViewFromScrapList() { 21517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int size = mScrapList.size(); 21527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas for (int i = 0; i < size; i++) { 21537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final View view = mScrapList.get(i).itemView; 21547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final LayoutParams lp = (LayoutParams) view.getLayoutParams(); 21557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (lp.isItemRemoved()) { 21567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas continue; 21577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 21587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mCurrentPosition == lp.getViewLayoutPosition()) { 21597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas assignPositionFromScrapList(view); 21607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return view; 21617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 21627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 21637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return null; 21647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 21657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 21667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void assignPositionFromScrapList() { 21677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas assignPositionFromScrapList(null); 21687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 21697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 21707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void assignPositionFromScrapList(View ignore) { 21717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final View closest = nextViewInLimitedList(ignore); 21727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (closest == null) { 21737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mCurrentPosition = NO_POSITION; 21747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 21757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mCurrentPosition = ((LayoutParams) closest.getLayoutParams()) 21767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas .getViewLayoutPosition(); 21777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 21787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 21797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 21807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public View nextViewInLimitedList(View ignore) { 21817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int size = mScrapList.size(); 21827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas View closest = null; 21837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int closestDistance = Integer.MAX_VALUE; 21847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (DEBUG && mIsPreLayout) { 21857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas throw new IllegalStateException("Scrap list cannot be used in pre layout"); 21867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 21877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas for (int i = 0; i < size; i++) { 21887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas View view = mScrapList.get(i).itemView; 21897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final LayoutParams lp = (LayoutParams) view.getLayoutParams(); 21907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (view == ignore || lp.isItemRemoved()) { 21917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas continue; 21927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 21937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int distance = (lp.getViewLayoutPosition() - mCurrentPosition) 21947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * mItemDirection; 21957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (distance < 0) { 21967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas continue; // item is not in current direction 21977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 21987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (distance < closestDistance) { 21997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas closest = view; 22007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas closestDistance = distance; 22017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (distance == 0) { 22027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas break; 22037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 22047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 22057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 22067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return closest; 22077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 22087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 22097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas void log() { 22107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Log.d(TAG, "avail:" + mAvailable + ", ind:" + mCurrentPosition + ", dir:" 22117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas + mItemDirection + ", offset:" + mOffset + ", layoutDir:" + mLayoutDirection); 22127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 22137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 22147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 22157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 22167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * @hide 22177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 22187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public static class SavedState implements Parcelable { 22197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 22207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int mAnchorPosition; 22217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 22227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int mAnchorOffset; 22237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 22247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas boolean mAnchorLayoutFromEnd; 22257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 22267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public SavedState() { 22277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 22287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 22297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 22307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas SavedState(Parcel in) { 22317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mAnchorPosition = in.readInt(); 22327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mAnchorOffset = in.readInt(); 22337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mAnchorLayoutFromEnd = in.readInt() == 1; 22347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 22357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 22367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public SavedState(SavedState other) { 22377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mAnchorPosition = other.mAnchorPosition; 22387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mAnchorOffset = other.mAnchorOffset; 22397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mAnchorLayoutFromEnd = other.mAnchorLayoutFromEnd; 22407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 22417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 22427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas boolean hasValidAnchor() { 22437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return mAnchorPosition >= 0; 22447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 22457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 22467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas void invalidateAnchor() { 22477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mAnchorPosition = NO_POSITION; 22487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 22497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 22507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 22517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public int describeContents() { 22527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return 0; 22537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 22547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 22557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 22567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void writeToParcel(Parcel dest, int flags) { 22577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas dest.writeInt(mAnchorPosition); 22587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas dest.writeInt(mAnchorOffset); 22597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas dest.writeInt(mAnchorLayoutFromEnd ? 1 : 0); 22607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 22617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 22627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public static final Parcelable.Creator<SavedState> CREATOR = 22637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas new Parcelable.Creator<SavedState>() { 22647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 22657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public SavedState createFromParcel(Parcel in) { 22667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return new SavedState(in); 22677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 22687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 22697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 22707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public SavedState[] newArray(int size) { 22717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return new SavedState[size]; 22727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 22737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas }; 22747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 22757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 22767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 22777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Simple data class to keep Anchor information 22787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 22797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas class AnchorInfo { 22807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int mPosition; 22817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int mCoordinate; 22827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas boolean mLayoutFromEnd; 22837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas boolean mValid; 22847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 22857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas AnchorInfo() { 22867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas reset(); 22877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 22887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 22897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas void reset() { 22907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mPosition = NO_POSITION; 22917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mCoordinate = INVALID_OFFSET; 22927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mLayoutFromEnd = false; 22937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mValid = false; 22947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 22957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 22967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 22977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * assigns anchor coordinate from the RecyclerView's padding depending on current 22987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * layoutFromEnd value 22997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 23007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas void assignCoordinateFromPadding() { 23017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mCoordinate = mLayoutFromEnd 23027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas ? mOrientationHelper.getEndAfterPadding() 23037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas : mOrientationHelper.getStartAfterPadding(); 23047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 23057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 23067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 23077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public String toString() { 23087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return "AnchorInfo{" 23097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas + "mPosition=" + mPosition 23107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas + ", mCoordinate=" + mCoordinate 23117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas + ", mLayoutFromEnd=" + mLayoutFromEnd 23127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas + ", mValid=" + mValid 23137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas + '}'; 23147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 23157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 23167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas boolean isViewValidAsAnchor(View child, RecyclerView.State state) { 23177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas LayoutParams lp = (LayoutParams) child.getLayoutParams(); 23187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return !lp.isItemRemoved() && lp.getViewLayoutPosition() >= 0 23197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas && lp.getViewLayoutPosition() < state.getItemCount(); 23207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 23217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 23227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void assignFromViewAndKeepVisibleRect(View child) { 23237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int spaceChange = mOrientationHelper.getTotalSpaceChange(); 23247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (spaceChange >= 0) { 23257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas assignFromView(child); 23267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return; 23277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 23287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mPosition = getPosition(child); 23297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mLayoutFromEnd) { 23307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int prevLayoutEnd = mOrientationHelper.getEndAfterPadding() - spaceChange; 23317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int childEnd = mOrientationHelper.getDecoratedEnd(child); 23327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int previousEndMargin = prevLayoutEnd - childEnd; 23337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mCoordinate = mOrientationHelper.getEndAfterPadding() - previousEndMargin; 23347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // ensure we did not push child's top out of bounds because of this 23357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (previousEndMargin > 0) { // we have room to shift bottom if necessary 23367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int childSize = mOrientationHelper.getDecoratedMeasurement(child); 23377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int estimatedChildStart = mCoordinate - childSize; 23387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int layoutStart = mOrientationHelper.getStartAfterPadding(); 23397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int previousStartMargin = mOrientationHelper.getDecoratedStart(child) 23407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas - layoutStart; 23417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int startReference = layoutStart + Math.min(previousStartMargin, 0); 23427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int startMargin = estimatedChildStart - startReference; 23437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (startMargin < 0) { 23447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // offset to make top visible but not too much 23457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mCoordinate += Math.min(previousEndMargin, -startMargin); 23467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 23477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 23487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 23497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int childStart = mOrientationHelper.getDecoratedStart(child); 23507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int startMargin = childStart - mOrientationHelper.getStartAfterPadding(); 23517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mCoordinate = childStart; 23527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (startMargin > 0) { // we have room to fix end as well 23537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int estimatedEnd = childStart 23547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas + mOrientationHelper.getDecoratedMeasurement(child); 23557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int previousLayoutEnd = mOrientationHelper.getEndAfterPadding() 23567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas - spaceChange; 23577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int previousEndMargin = previousLayoutEnd 23587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas - mOrientationHelper.getDecoratedEnd(child); 23597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int endReference = mOrientationHelper.getEndAfterPadding() 23607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas - Math.min(0, previousEndMargin); 23617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int endMargin = endReference - estimatedEnd; 23627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (endMargin < 0) { 23637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mCoordinate -= Math.min(startMargin, -endMargin); 23647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 23657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 23667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 23677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 23687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 23697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void assignFromView(View child) { 23707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mLayoutFromEnd) { 23717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mCoordinate = mOrientationHelper.getDecoratedEnd(child) 23727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas + mOrientationHelper.getTotalSpaceChange(); 23737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 23747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mCoordinate = mOrientationHelper.getDecoratedStart(child); 23757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 23767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 23777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mPosition = getPosition(child); 23787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 23797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 23807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 23817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas protected static class LayoutChunkResult { 23827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public int mConsumed; 23837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public boolean mFinished; 23847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public boolean mIgnoreConsumed; 23857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public boolean mFocusable; 23867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 23877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas void resetInternal() { 23887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mConsumed = 0; 23897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mFinished = false; 23907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mIgnoreConsumed = false; 23917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mFocusable = false; 23927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 23937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 23947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas} 2395