17dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar/* 2ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * Copyright 2018 The Android Open Source Project 37dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * 47dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License"); 57dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * you may not use this file except in compliance with the License. 67dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * You may obtain a copy of the License at 77dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * 87dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * http://www.apache.org/licenses/LICENSE-2.0 97dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * 107dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Unless required by applicable law or agreed to in writing, software 117dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS, 127dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas * See the License for the specific language governing permissions and 147dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * limitations under the License. 157dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 167dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.recyclerview.widget; 187dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 19ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP; 206b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyar 217dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyarimport android.content.Context; 22baa826219e2ae7c054011cabf992cd7a37fe2a8fGabriel Pealimport android.graphics.PointF; 2308cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyarimport android.os.Parcel; 2408cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyarimport android.os.Parcelable; 254143554adb9b31b700b6876a251a64419e6111e2Yigit Boyarimport android.util.AttributeSet; 267dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyarimport android.util.Log; 277dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyarimport android.view.View; 287dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyarimport android.view.ViewGroup; 29a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.view.accessibility.AccessibilityEvent; 30a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar 31c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikasimport androidx.annotation.NonNull; 32c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikasimport androidx.annotation.RestrictTo; 33c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikasimport androidx.core.os.TraceCompat; 34c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikasimport androidx.core.view.ViewCompat; 35c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikas 36b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyarimport java.util.List; 37b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 387dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar/** 39ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * A {@link RecyclerView.LayoutManager} implementation which provides 407dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * similar functionality to {@link android.widget.ListView}. 417dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 42e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarpublic class LinearLayoutManager extends RecyclerView.LayoutManager implements 43c587f7dba5a337169e854e235da59f595255d6ccAga Madurska ItemTouchHelper.ViewDropHandler, RecyclerView.SmoothScroller.ScrollVectorProvider { 447dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 457dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar private static final String TAG = "LinearLayoutManager"; 467dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 473a500f61a8bdf48904f380f2d4925fe420d18ce7Aurimas Liutikas static final boolean DEBUG = false; 487dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 49a0e32c54b808887323ba05392fe4e340c9ad6c0bshepshapard public static final int HORIZONTAL = RecyclerView.HORIZONTAL; 507dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 51a0e32c54b808887323ba05392fe4e340c9ad6c0bshepshapard public static final int VERTICAL = RecyclerView.VERTICAL; 527dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 53d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar public static final int INVALID_OFFSET = Integer.MIN_VALUE; 54d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar 550447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar 567dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 575f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * While trying to find next view to focus, LayoutManager will not try to scroll more 585f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * than this factor times the total space of the list. If layout is vertical, total space is the 592aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar * height minus padding, if layout is horizontal, total space is the width minus padding. 602aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar */ 61f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar private static final float MAX_SCROLL_FACTOR = 1 / 3f; 622aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar 632aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar /** 647dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL} 657dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 66a8e9d2739e676eb27f2570b3db278e183ec327f2Aurimas Liutikas @RecyclerView.Orientation 67a0e32c54b808887323ba05392fe4e340c9ad6c0bshepshapard int mOrientation = RecyclerView.DEFAULT_ORIENTATION; 687dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 697dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 7094c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar * Helper class that keeps temporary layout state. 7194c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar * It does not keep state after layout is complete but we still keep a reference to re-use 727dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * the same object. 737dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 7494c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar private LayoutState mLayoutState; 757dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 767dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 777dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Many calculations are made depending on orientation. To keep it clean, this interface 787dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * helps {@link LinearLayoutManager} make those decisions. 797dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 80d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar OrientationHelper mOrientationHelper; 817dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 827dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 837dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * We need to track this so that we can ignore current position when it changes. 847dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 8508cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar private boolean mLastStackFromEnd; 867dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 877dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 887dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 897dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Defines if layout should be calculated from end to start. 907dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * 917dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * @see #mShouldReverseLayout 927dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 937dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar private boolean mReverseLayout = false; 947dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 957dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 968c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar * This keeps the final value for how LayoutManager should start laying out views. 977dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * It is calculated by checking {@link #getReverseLayout()} and View's layout direction. 98d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * {@link #onLayoutChildren(RecyclerView.Recycler, RecyclerView.State)} is run. 997dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 100b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar boolean mShouldReverseLayout = false; 1017dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 1027dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 1037dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Works the same way as {@link android.widget.AbsListView#setStackFromBottom(boolean)} and 1047dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * it supports both orientations. 1057dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * see {@link android.widget.AbsListView#setStackFromBottom(boolean)} 1067dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 1077dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar private boolean mStackFromEnd = false; 1087dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 1097dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 1108c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar * Works the same way as {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)}. 1118c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar * see {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)} 1128c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar */ 1138c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar private boolean mSmoothScrollbarEnabled = true; 1148c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar 1158c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar /** 116d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar * When LayoutManager needs to scroll to a position, it sets this variable and requests a 11725a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar * layout which will check this variable and re-layout accordingly. 11825a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar */ 119ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas int mPendingScrollPosition = RecyclerView.NO_POSITION; 12025a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar 12125a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar /** 122d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar * Used to keep the offset value when {@link #scrollToPositionWithOffset(int, int)} is 123d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar * called. 124d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar */ 1256e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar int mPendingScrollPositionOffset = INVALID_OFFSET; 126d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar 12749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar private boolean mRecycleChildrenOnDetach; 12849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 1296e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar SavedState mPendingSavedState = null; 13008cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar 131d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar /** 1321e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas * Re-used variable to keep anchor information on re-layout. 1331e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas * Anchor position and coordinate defines the reference point for LLM while doing a layout. 1341e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas * */ 1350194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta final AnchorInfo mAnchorInfo = new AnchorInfo(); 136310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 137310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar /** 138e835a0f12bb6c6a7aa323cb9f58c29e2a9f232adChris Craik * Stashed to avoid allocation, currently only used in #fill() 139e835a0f12bb6c6a7aa323cb9f58c29e2a9f232adChris Craik */ 140e835a0f12bb6c6a7aa323cb9f58c29e2a9f232adChris Craik private final LayoutChunkResult mLayoutChunkResult = new LayoutChunkResult(); 141e835a0f12bb6c6a7aa323cb9f58c29e2a9f232adChris Craik 142e835a0f12bb6c6a7aa323cb9f58c29e2a9f232adChris Craik /** 1431e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik * Number of items to prefetch when first coming on screen with new data. 1441e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik */ 145d6696c2abea2771acd000c2269cf9113acc6c0a9Chris Craik private int mInitialPrefetchItemCount = 2; 1461e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 1471e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik /** 1487dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Creates a vertical LinearLayoutManager 1497dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * 1507dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * @param context Current context, will be used to access resources. 1517dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 1527dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar public LinearLayoutManager(Context context) { 153a0e32c54b808887323ba05392fe4e340c9ad6c0bshepshapard this(context, RecyclerView.DEFAULT_ORIENTATION, false); 1547dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 1557dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 1567dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 1577dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * @param context Current context, will be used to access resources. 1587dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * @param orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link 1597dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * #VERTICAL}. 16094c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar * @param reverseLayout When set to true, layouts from end to start. 1617dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 162a8e9d2739e676eb27f2570b3db278e183ec327f2Aurimas Liutikas public LinearLayoutManager(Context context, @RecyclerView.Orientation int orientation, 163a8e9d2739e676eb27f2570b3db278e183ec327f2Aurimas Liutikas boolean reverseLayout) { 1647dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar setOrientation(orientation); 1657dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar setReverseLayout(reverseLayout); 1667dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 1677dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 1687dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 1690194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta * Constructor used when layout manager is set in XML by RecyclerView attribute 1700194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta * "layoutManager". Defaults to vertical orientation. 1710194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta * 172ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * @attr ref androidx.recyclerview.R.styleable#RecyclerView_android_orientation 173ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * @attr ref androidx.recyclerview.R.styleable#RecyclerView_reverseLayout 174ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * @attr ref androidx.recyclerview.R.styleable#RecyclerView_stackFromEnd 1750194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta */ 1760194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta public LinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, 1771e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas int defStyleRes) { 1780194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta Properties properties = getProperties(context, attrs, defStyleAttr, defStyleRes); 1790194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta setOrientation(properties.orientation); 1800194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta setReverseLayout(properties.reverseLayout); 1810194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta setStackFromEnd(properties.stackFromEnd); 18231df934a5cd4bcaac517f313a9fc1a2639beaf9fshepshapard } 18331df934a5cd4bcaac517f313a9fc1a2639beaf9fshepshapard 18431df934a5cd4bcaac517f313a9fc1a2639beaf9fshepshapard @Override 18531df934a5cd4bcaac517f313a9fc1a2639beaf9fshepshapard public boolean isAutoMeasureEnabled() { 18631df934a5cd4bcaac517f313a9fc1a2639beaf9fshepshapard return true; 1870194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta } 1880194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta 1890194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta /** 1907dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * {@inheritDoc} 1917dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 1927dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar @Override 193ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas public RecyclerView.LayoutParams generateDefaultLayoutParams() { 194ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 1957dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar ViewGroup.LayoutParams.WRAP_CONTENT); 1967dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 1977dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 19849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar /** 1995f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * Returns whether LayoutManager will recycle its children when it is detached from 20049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar * RecyclerView. 20149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar * 2025f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * @return true if LayoutManager will recycle its children when it is detached from 20349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar * RecyclerView. 20449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar */ 20549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public boolean getRecycleChildrenOnDetach() { 20649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar return mRecycleChildrenOnDetach; 20749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 20849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 20949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar /** 2105f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * Set whether LayoutManager will recycle its children when it is detached from 21149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar * RecyclerView. 21249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar * <p> 21349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar * If you are using a {@link RecyclerView.RecycledViewPool}, it might be a good idea to set 214c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas * this flag to <code>true</code> so that views will be available to other RecyclerViews 21549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar * immediately. 21649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar * <p> 21749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar * Note that, setting this flag will result in a performance drop if RecyclerView 21849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar * is restored. 21949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar * 22049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar * @param recycleChildrenOnDetach Whether children should be recycled in detach or not. 22149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar */ 22249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void setRecycleChildrenOnDetach(boolean recycleChildrenOnDetach) { 22349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecycleChildrenOnDetach = recycleChildrenOnDetach; 22449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 22549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 22649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 22749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) { 22849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar super.onDetachedFromWindow(view, recycler); 22949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar if (mRecycleChildrenOnDetach) { 23049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar removeAndRecycleAllViews(recycler); 23149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar recycler.clear(); 23249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 23349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 23449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 23508cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar @Override 236a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 237a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar super.onInitializeAccessibilityEvent(event); 238a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (getChildCount() > 0) { 23914d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas event.setFromIndex(findFirstVisibleItemPosition()); 24014d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas event.setToIndex(findLastVisibleItemPosition()); 241a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 242a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 243a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar 244a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar @Override 24508cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar public Parcelable onSaveInstanceState() { 24608cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar if (mPendingSavedState != null) { 24708cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar return new SavedState(mPendingSavedState); 24808cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar } 24908cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar SavedState state = new SavedState(); 25008cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar if (getChildCount() > 0) { 251ffff7c903fa7dbf176fd251f77a959e6b9531456Yigit Boyar ensureLayoutState(); 25208cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar boolean didLayoutFromEnd = mLastStackFromEnd ^ mShouldReverseLayout; 25308cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar state.mAnchorLayoutFromEnd = didLayoutFromEnd; 25408cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar if (didLayoutFromEnd) { 255668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar final View refChild = getChildClosestToEnd(); 2561e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas state.mAnchorOffset = mOrientationHelper.getEndAfterPadding() 2571e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas - mOrientationHelper.getDecoratedEnd(refChild); 25808cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar state.mAnchorPosition = getPosition(refChild); 25908cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar } else { 260668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar final View refChild = getChildClosestToStart(); 26108cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar state.mAnchorPosition = getPosition(refChild); 2621e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas state.mAnchorOffset = mOrientationHelper.getDecoratedStart(refChild) 2631e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas - mOrientationHelper.getStartAfterPadding(); 26408cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar } 26508cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar } else { 26675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar state.invalidateAnchor(); 26708cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar } 26808cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar return state; 26908cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar } 27008cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar 27108cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar @Override 27208cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar public void onRestoreInstanceState(Parcelable state) { 27308cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar if (state instanceof SavedState) { 27408cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar mPendingSavedState = (SavedState) state; 27508cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar requestLayout(); 27608cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar if (DEBUG) { 27708cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar Log.d(TAG, "loaded saved state"); 27808cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar } 27908cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar } else if (DEBUG) { 28008cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar Log.d(TAG, "invalid saved state class"); 28108cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar } 28208cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar } 28308cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar 2847dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 2857dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * @return true if {@link #getOrientation()} is {@link #HORIZONTAL} 2867dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 2877dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar @Override 2887dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar public boolean canScrollHorizontally() { 2897dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar return mOrientation == HORIZONTAL; 2907dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 2917dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 2927dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 2937dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * @return true if {@link #getOrientation()} is {@link #VERTICAL} 2947dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 2957dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar @Override 2967dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar public boolean canScrollVertically() { 2977dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar return mOrientation == VERTICAL; 2987dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 2997dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 3007dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 3017dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Compatibility support for {@link android.widget.AbsListView#setStackFromBottom(boolean)} 3027dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 3037dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar public void setStackFromEnd(boolean stackFromEnd) { 3040bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar assertNotInLayoutOrScroll(null); 3058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (mStackFromEnd == stackFromEnd) { 3068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return; 3078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3087dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar mStackFromEnd = stackFromEnd; 3097dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar requestLayout(); 3107dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 3117dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 3127dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar public boolean getStackFromEnd() { 3137dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar return mStackFromEnd; 3147dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 3157dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 3167dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 317c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas * Returns the current orientation of the layout. 3187dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * 319c47c04733b86048a31a2419af32a5f8df01d4602Andrew Solovay * @return Current orientation, either {@link #HORIZONTAL} or {@link #VERTICAL} 3207dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * @see #setOrientation(int) 3217dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 322a8e9d2739e676eb27f2570b3db278e183ec327f2Aurimas Liutikas @RecyclerView.Orientation 3237dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar public int getOrientation() { 3247dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar return mOrientation; 3257dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 3267dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 3277dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 328ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * Sets the orientation of the layout. {@link LinearLayoutManager} 3297dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * will do its best to keep scroll position. 3307dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * 3317dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * @param orientation {@link #HORIZONTAL} or {@link #VERTICAL} 3327dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 333a8e9d2739e676eb27f2570b3db278e183ec327f2Aurimas Liutikas public void setOrientation(@RecyclerView.Orientation int orientation) { 3347dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar if (orientation != HORIZONTAL && orientation != VERTICAL) { 33594c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar throw new IllegalArgumentException("invalid orientation:" + orientation); 3367dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 337a0e32c54b808887323ba05392fe4e340c9ad6c0bshepshapard 3380bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar assertNotInLayoutOrScroll(null); 339a0e32c54b808887323ba05392fe4e340c9ad6c0bshepshapard 340a0e32c54b808887323ba05392fe4e340c9ad6c0bshepshapard if (orientation != mOrientation || mOrientationHelper == null) { 341a0e32c54b808887323ba05392fe4e340c9ad6c0bshepshapard mOrientationHelper = 342a0e32c54b808887323ba05392fe4e340c9ad6c0bshepshapard OrientationHelper.createOrientationHelper(this, orientation); 343a0e32c54b808887323ba05392fe4e340c9ad6c0bshepshapard mAnchorInfo.mOrientationHelper = mOrientationHelper; 344a0e32c54b808887323ba05392fe4e340c9ad6c0bshepshapard mOrientation = orientation; 345a0e32c54b808887323ba05392fe4e340c9ad6c0bshepshapard requestLayout(); 3467dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 3477dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 3487dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 3497dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 3507dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Calculates the view layout order. (e.g. from end to start or start to end) 3517dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * RTL layout support is applied automatically. So if layout is RTL and 3527dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * {@link #getReverseLayout()} is {@code true}, elements will be laid out starting from left. 3537dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 3547dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar private void resolveShouldLayoutReverse() { 3557dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar // A == B is the same result, but we rather keep it readable 356d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar if (mOrientation == VERTICAL || !isLayoutRTL()) { 3577dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar mShouldReverseLayout = mReverseLayout; 3587dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } else { 3597dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar mShouldReverseLayout = !mReverseLayout; 3607dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 3617dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 3627dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 3637dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 3647dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Returns if views are laid out from the opposite direction of the layout. 3657dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * 3667dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * @return If layout is reversed or not. 367c47c04733b86048a31a2419af32a5f8df01d4602Andrew Solovay * @see #setReverseLayout(boolean) 3687dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 3697dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar public boolean getReverseLayout() { 3707dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar return mReverseLayout; 3717dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 3727dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 3737dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 3747dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Used to reverse item traversal and layout order. 3757dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * This behaves similar to the layout change for RTL views. When set to true, first item is 37694c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar * laid out at the end of the UI, second item is laid out before it etc. 3777dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * 3787dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * For horizontal layouts, it depends on the layout direction. 379ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * When set to true, If {@link RecyclerView} is LTR, than it will 380ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * layout from RTL, if {@link RecyclerView}} is RTL, it will layout 3817dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * from LTR. 3827dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * 3837dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * If you are looking for the exact same behavior of 3847dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * {@link android.widget.AbsListView#setStackFromBottom(boolean)}, use 3857dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * {@link #setStackFromEnd(boolean)} 3867dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 3877dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar public void setReverseLayout(boolean reverseLayout) { 3880bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar assertNotInLayoutOrScroll(null); 3897dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar if (reverseLayout == mReverseLayout) { 3907dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar return; 3917dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 3927dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar mReverseLayout = reverseLayout; 3937dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar requestLayout(); 3947dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 3957dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 3960447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar /** 3970447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar * {@inheritDoc} 3980447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar */ 3990447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar @Override 4000447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar public View findViewByPosition(int position) { 401d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar final int childCount = getChildCount(); 402d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar if (childCount == 0) { 403d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar return null; 404d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar } 405d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar final int firstChild = getPosition(getChildAt(0)); 406d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar final int viewPosition = position - firstChild; 407d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar if (viewPosition >= 0 && viewPosition < childCount) { 408e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final View child = getChildAt(viewPosition); 409e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (getPosition(child) == position) { 410e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return child; // in pre-layout, this may not match 411e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 412d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar } 413e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // fallback to traversal. This might be necessary in pre-layout. 414e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return super.findViewByPosition(position); 415d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar } 4167dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 4177dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 418565b7cf4798c21d54a3a5af12933400c72fb3885Chris Craik * <p>Returns the amount of extra space that should be laid out by LayoutManager.</p> 419565b7cf4798c21d54a3a5af12933400c72fb3885Chris Craik * 420ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * <p>By default, {@link LinearLayoutManager} lays out 1 extra page 421565b7cf4798c21d54a3a5af12933400c72fb3885Chris Craik * of items while smooth scrolling and 0 otherwise. You can override this method to implement 422565b7cf4798c21d54a3a5af12933400c72fb3885Chris Craik * your custom layout pre-cache logic.</p> 423565b7cf4798c21d54a3a5af12933400c72fb3885Chris Craik * 424565b7cf4798c21d54a3a5af12933400c72fb3885Chris Craik * <p><strong>Note:</strong>Laying out invisible elements generally comes with significant 425565b7cf4798c21d54a3a5af12933400c72fb3885Chris Craik * performance cost. It's typically only desirable in places like smooth scrolling to an unknown 426565b7cf4798c21d54a3a5af12933400c72fb3885Chris Craik * location, where 1) the extra content helps LinearLayoutManager know in advance when its 427565b7cf4798c21d54a3a5af12933400c72fb3885Chris Craik * target is approaching, so it can decelerate early and smoothly and 2) while motion is 428565b7cf4798c21d54a3a5af12933400c72fb3885Chris Craik * continuous.</p> 429565b7cf4798c21d54a3a5af12933400c72fb3885Chris Craik * 430565b7cf4798c21d54a3a5af12933400c72fb3885Chris Craik * <p>Extending the extra layout space is especially expensive if done while the user may change 431565b7cf4798c21d54a3a5af12933400c72fb3885Chris Craik * scrolling direction. Changing direction will cause the extra layout space to swap to the 432565b7cf4798c21d54a3a5af12933400c72fb3885Chris Craik * opposite side of the viewport, incurring many rebinds/recycles, unless the cache is large 433565b7cf4798c21d54a3a5af12933400c72fb3885Chris Craik * enough to handle it.</p> 4340447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar * 4350447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar * @return The extra space that should be laid out (in pixels). 4360447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar */ 4370447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar protected int getExtraLayoutSpace(RecyclerView.State state) { 4380447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar if (state.hasTargetScrollPosition()) { 4390447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar return mOrientationHelper.getTotalSpace(); 4400447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar } else { 4410447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar return 0; 4420447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar } 4430447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar } 4440447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar 4450447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar @Override 446d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, 4470447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar int position) { 4480447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar LinearSmoothScroller linearSmoothScroller = 449c587f7dba5a337169e854e235da59f595255d6ccAga Madurska new LinearSmoothScroller(recyclerView.getContext()); 4500447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar linearSmoothScroller.setTargetPosition(position); 4510447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar startSmoothScroll(linearSmoothScroller); 4520447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar } 4530447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar 454c587f7dba5a337169e854e235da59f595255d6ccAga Madurska @Override 4550447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar public PointF computeScrollVectorForPosition(int targetPosition) { 4560447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar if (getChildCount() == 0) { 4570447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar return null; 4580447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar } 4590447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar final int firstChildPos = getPosition(getChildAt(0)); 4600447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar final int direction = targetPosition < firstChildPos != mShouldReverseLayout ? -1 : 1; 4610447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar if (mOrientation == HORIZONTAL) { 4620447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar return new PointF(direction, 0); 4630447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar } else { 4640447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar return new PointF(0, direction); 4650447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar } 4660447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar } 4670447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar 4680447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar /** 4697dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * {@inheritDoc} 4707dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 4717dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar @Override 472d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 473d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar // layout algorithm: 474d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar // 1) by checking children and other variables, find an anchor coordinate and an anchor 475d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar // item position. 476d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar // 2) fill towards start, stacking from bottom 477d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar // 3) fill towards end, stacking from top 478d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar // 4) scroll to fulfill requirements like stack from bottom. 47994c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar // create layout state 480b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (DEBUG) { 481b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar Log.d(TAG, "is pre layout:" + state.isPreLayout()); 482b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 483ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas if (mPendingSavedState != null || mPendingScrollPosition != RecyclerView.NO_POSITION) { 4843d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev if (state.getItemCount() == 0) { 4853d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev removeAndRecycleAllViews(recycler); 4863d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev return; 4873d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev } 4883d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev } 489b8403301bbec29129730f6cce3fe2fa3ee8e1e0bYigit Boyar if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) { 490b8403301bbec29129730f6cce3fe2fa3ee8e1e0bYigit Boyar mPendingScrollPosition = mPendingSavedState.mAnchorPosition; 49108cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar } 492b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 49394c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar ensureLayoutState(); 494c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mLayoutState.mRecycle = false; 495d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar // resolve layout direction 4967dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar resolveShouldLayoutReverse(); 4977dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 498151e43e7057619d714bc0f91ac7e2139ee334058Keyvan Amiri final View focused = getFocusedChild(); 499ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas if (!mAnchorInfo.mValid || mPendingScrollPosition != RecyclerView.NO_POSITION 5001e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas || mPendingSavedState != null) { 5019aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar mAnchorInfo.reset(); 5029aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar mAnchorInfo.mLayoutFromEnd = mShouldReverseLayout ^ mStackFromEnd; 5039aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar // calculate anchor position and coordinate 5049aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar updateAnchorInfoForLayout(recycler, state, mAnchorInfo); 5059aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar mAnchorInfo.mValid = true; 506151e43e7057619d714bc0f91ac7e2139ee334058Keyvan Amiri } else if (focused != null && (mOrientationHelper.getDecoratedStart(focused) 507151e43e7057619d714bc0f91ac7e2139ee334058Keyvan Amiri >= mOrientationHelper.getEndAfterPadding() 508151e43e7057619d714bc0f91ac7e2139ee334058Keyvan Amiri || mOrientationHelper.getDecoratedEnd(focused) 509151e43e7057619d714bc0f91ac7e2139ee334058Keyvan Amiri <= mOrientationHelper.getStartAfterPadding())) { 510151e43e7057619d714bc0f91ac7e2139ee334058Keyvan Amiri // This case relates to when the anchor child is the focused view and due to layout 511151e43e7057619d714bc0f91ac7e2139ee334058Keyvan Amiri // shrinking the focused view fell outside the viewport, e.g. when soft keyboard shows 512151e43e7057619d714bc0f91ac7e2139ee334058Keyvan Amiri // up after tapping an EditText which shrinks RV causing the focused view (The tapped 513151e43e7057619d714bc0f91ac7e2139ee334058Keyvan Amiri // EditText which is the anchor child) to get kicked out of the screen. Will update the 514151e43e7057619d714bc0f91ac7e2139ee334058Keyvan Amiri // anchor coordinate in order to make sure that the focused view is laid out. Otherwise, 515151e43e7057619d714bc0f91ac7e2139ee334058Keyvan Amiri // the available space in layoutState will be calculated as negative preventing the 516151e43e7057619d714bc0f91ac7e2139ee334058Keyvan Amiri // focused view from being laid out in fill. 517151e43e7057619d714bc0f91ac7e2139ee334058Keyvan Amiri // Note that we won't update the anchor position between layout passes (refer to 518151e43e7057619d714bc0f91ac7e2139ee334058Keyvan Amiri // TestResizingRelayoutWithAutoMeasure), which happens if we were to call 519151e43e7057619d714bc0f91ac7e2139ee334058Keyvan Amiri // updateAnchorInfoForLayout for an anchor that's not the focused view (e.g. a reference 520151e43e7057619d714bc0f91ac7e2139ee334058Keyvan Amiri // child which can change between layout passes). 521a0e32c54b808887323ba05392fe4e340c9ad6c0bshepshapard mAnchorInfo.assignFromViewAndKeepVisibleRect(focused, getPosition(focused)); 5229aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar } 523310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (DEBUG) { 524310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar Log.d(TAG, "Anchor info:" + mAnchorInfo); 5257dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 526d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar 527310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar // LLM may decide to layout items for "extra" pixels to account for scrolling target, 528310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar // caching or predictive animations. 529e679158cd5f7469d49918c80e6cfaf4ba35d6ea6Yigit Boyar int extraForStart; 530e679158cd5f7469d49918c80e6cfaf4ba35d6ea6Yigit Boyar int extraForEnd; 5310447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar final int extra = getExtraLayoutSpace(state); 532baa826219e2ae7c054011cabf992cd7a37fe2a8fGabriel Peal // If the previous scroll delta was less than zero, the extra space should be laid out 533baa826219e2ae7c054011cabf992cd7a37fe2a8fGabriel Peal // at the start. Otherwise, it should be at the end. 534baa826219e2ae7c054011cabf992cd7a37fe2a8fGabriel Peal if (mLayoutState.mLastScrollDelta >= 0) { 5350447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar extraForEnd = extra; 5360447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar extraForStart = 0; 5370447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar } else { 5380447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar extraForStart = extra; 5390447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar extraForEnd = 0; 5400447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar } 541c032ec5462f6c7c07031310090e23af65841deeeYigit Boyar extraForStart += mOrientationHelper.getStartAfterPadding(); 542c032ec5462f6c7c07031310090e23af65841deeeYigit Boyar extraForEnd += mOrientationHelper.getEndPadding(); 543ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas if (state.isPreLayout() && mPendingScrollPosition != RecyclerView.NO_POSITION 5441e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas && mPendingScrollPositionOffset != INVALID_OFFSET) { 545c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar // if the child is visible and we are going to move it around, we should layout 546c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar // extra items in the opposite direction to make sure new items animate nicely 547c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar // instead of just fading in 548c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final View existing = findViewByPosition(mPendingScrollPosition); 549c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar if (existing != null) { 550c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int current; 551c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int upcomingOffset; 552c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar if (mShouldReverseLayout) { 5531e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas current = mOrientationHelper.getEndAfterPadding() 5541e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas - mOrientationHelper.getDecoratedEnd(existing); 555c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar upcomingOffset = current - mPendingScrollPositionOffset; 556c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } else { 557c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar current = mOrientationHelper.getDecoratedStart(existing) 558c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar - mOrientationHelper.getStartAfterPadding(); 559c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar upcomingOffset = mPendingScrollPositionOffset - current; 560c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 561c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar if (upcomingOffset > 0) { 562c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar extraForStart += upcomingOffset; 563c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } else { 564c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar extraForEnd -= upcomingOffset; 565c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 566c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 567c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 568e679158cd5f7469d49918c80e6cfaf4ba35d6ea6Yigit Boyar int startOffset; 569e679158cd5f7469d49918c80e6cfaf4ba35d6ea6Yigit Boyar int endOffset; 570f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar final int firstLayoutDirection; 571f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar if (mAnchorInfo.mLayoutFromEnd) { 5721e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL 5731e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas : LayoutState.ITEM_DIRECTION_HEAD; 574f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar } else { 5751e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD 5761e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas : LayoutState.ITEM_DIRECTION_TAIL; 577f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar } 578f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar 579f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar onAnchorReady(recycler, state, mAnchorInfo, firstLayoutDirection); 580c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar detachAndScrapAttachedViews(recycler); 581f3844451301cb14ca885e125cb27f108a834c386Yigit Boyar mLayoutState.mInfinite = resolveIsInfinite(); 58218ae336dbaae3db982f38cde08936c9f0243757bYigit Boyar mLayoutState.mIsPreLayout = state.isPreLayout(); 583310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (mAnchorInfo.mLayoutFromEnd) { 584e679158cd5f7469d49918c80e6cfaf4ba35d6ea6Yigit Boyar // fill towards start 585310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar updateLayoutStateToFillStart(mAnchorInfo); 58694c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mLayoutState.mExtra = extraForStart; 58794c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar fill(recycler, mLayoutState, state, false); 58894c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar startOffset = mLayoutState.mOffset; 5891f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar final int firstElement = mLayoutState.mCurrentPosition; 590c032ec5462f6c7c07031310090e23af65841deeeYigit Boyar if (mLayoutState.mAvailable > 0) { 591c032ec5462f6c7c07031310090e23af65841deeeYigit Boyar extraForEnd += mLayoutState.mAvailable; 592c032ec5462f6c7c07031310090e23af65841deeeYigit Boyar } 593e679158cd5f7469d49918c80e6cfaf4ba35d6ea6Yigit Boyar // fill towards end 594310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar updateLayoutStateToFillEnd(mAnchorInfo); 59594c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mLayoutState.mExtra = extraForEnd; 59694c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mLayoutState.mCurrentPosition += mLayoutState.mItemDirection; 59794c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar fill(recycler, mLayoutState, state, false); 59894c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar endOffset = mLayoutState.mOffset; 5991f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 6001f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (mLayoutState.mAvailable > 0) { 6011f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar // end could not consume all. add more items towards start 6021f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar extraForStart = mLayoutState.mAvailable; 6031f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar updateLayoutStateToFillStart(firstElement, startOffset); 6041f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mLayoutState.mExtra = extraForStart; 6051f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar fill(recycler, mLayoutState, state, false); 6061f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar startOffset = mLayoutState.mOffset; 6071f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 608e679158cd5f7469d49918c80e6cfaf4ba35d6ea6Yigit Boyar } else { 609e679158cd5f7469d49918c80e6cfaf4ba35d6ea6Yigit Boyar // fill towards end 610310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar updateLayoutStateToFillEnd(mAnchorInfo); 61194c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mLayoutState.mExtra = extraForEnd; 61294c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar fill(recycler, mLayoutState, state, false); 61394c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar endOffset = mLayoutState.mOffset; 6141f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar final int lastElement = mLayoutState.mCurrentPosition; 615c032ec5462f6c7c07031310090e23af65841deeeYigit Boyar if (mLayoutState.mAvailable > 0) { 616c032ec5462f6c7c07031310090e23af65841deeeYigit Boyar extraForStart += mLayoutState.mAvailable; 617c032ec5462f6c7c07031310090e23af65841deeeYigit Boyar } 618e679158cd5f7469d49918c80e6cfaf4ba35d6ea6Yigit Boyar // fill towards start 619310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar updateLayoutStateToFillStart(mAnchorInfo); 62094c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mLayoutState.mExtra = extraForStart; 62194c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mLayoutState.mCurrentPosition += mLayoutState.mItemDirection; 62294c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar fill(recycler, mLayoutState, state, false); 62394c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar startOffset = mLayoutState.mOffset; 6241f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 6251f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (mLayoutState.mAvailable > 0) { 6261f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar extraForEnd = mLayoutState.mAvailable; 6271f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar // start could not consume all it should. add more items towards end 6281f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar updateLayoutStateToFillEnd(lastElement, endOffset); 6291f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mLayoutState.mExtra = extraForEnd; 6301f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar fill(recycler, mLayoutState, state, false); 6311f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar endOffset = mLayoutState.mOffset; 6321f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 633d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar } 634e679158cd5f7469d49918c80e6cfaf4ba35d6ea6Yigit Boyar 635d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar // changes may cause gaps on the UI, try to fix them. 636310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar // TODO we can probably avoid this if neither stackFromEnd/reverseLayout/RTL values have 637310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar // changed 63825a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar if (getChildCount() > 0) { 639d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar // because layout from end may be changed by scroll to position 640d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar // we re-calculate it. 641d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar // find which side we should check for gaps. 642d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar if (mShouldReverseLayout ^ mStackFromEnd) { 643b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar int fixOffset = fixLayoutEndGap(endOffset, recycler, state, true); 644b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar startOffset += fixOffset; 645b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar endOffset += fixOffset; 646b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar fixOffset = fixLayoutStartGap(startOffset, recycler, state, false); 647b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar startOffset += fixOffset; 648b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar endOffset += fixOffset; 64925a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar } else { 650b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar int fixOffset = fixLayoutStartGap(startOffset, recycler, state, true); 651b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar startOffset += fixOffset; 652b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar endOffset += fixOffset; 653b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar fixOffset = fixLayoutEndGap(endOffset, recycler, state, false); 654b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar startOffset += fixOffset; 655b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar endOffset += fixOffset; 65625a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar } 65725a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar } 658310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar layoutForPredictiveAnimations(recycler, state, startOffset, endOffset); 659310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (!state.isPreLayout()) { 660310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mOrientationHelper.onLayoutComplete(); 6619aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar } else { 6629aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar mAnchorInfo.reset(); 663310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 664310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLastStackFromEnd = mStackFromEnd; 665310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (DEBUG) { 666310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar validateChildOrder(); 667310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 668310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 66925a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar 6708cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar @Override 6718cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar public void onLayoutCompleted(RecyclerView.State state) { 6728cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar super.onLayoutCompleted(state); 6738cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar mPendingSavedState = null; // we don't need this anymore 674ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas mPendingScrollPosition = RecyclerView.NO_POSITION; 6758cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar mPendingScrollPositionOffset = INVALID_OFFSET; 6769aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar mAnchorInfo.reset(); 6778cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar } 6788cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar 679310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar /** 680b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Method called when Anchor position is decided. Extending class can setup accordingly or 681b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * even update anchor info if necessary. 682f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar * @param recycler The recycler for the layout 683f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar * @param state The layout state 684f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar * @param anchorInfo The mutable POJO that keeps the position and offset. 685f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar * @param firstLayoutItemDirection The direction of the first layout filling in terms of adapter 686f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar * indices. 687b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 688cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar void onAnchorReady(RecyclerView.Recycler recycler, RecyclerView.State state, 6891e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas AnchorInfo anchorInfo, int firstLayoutItemDirection) { 690b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 691b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 692b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 693310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar * If necessary, layouts new items for predictive animations 694310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar */ 695310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar private void layoutForPredictiveAnimations(RecyclerView.Recycler recycler, 6969c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri RecyclerView.State state, int startOffset, 6979c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri int endOffset) { 698b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar // If there are scrap children that we did not layout, we need to find where they did go 699b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar // and layout them accordingly so that animations can work as expected. 700b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar // This case may happen if new views are added or an existing view expands and pushes 701b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar // another view out of bounds. 702b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (!state.willRunPredictiveAnimations() || getChildCount() == 0 || state.isPreLayout() 703b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar || !supportsPredictiveItemAnimations()) { 704310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar return; 705310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 706310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar // to make the logic simpler, we calculate the size of children and call fill. 707310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar int scrapExtraStart = 0, scrapExtraEnd = 0; 708310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar final List<RecyclerView.ViewHolder> scrapList = recycler.getScrapList(); 709310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar final int scrapSize = scrapList.size(); 710310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar final int firstChildPos = getPosition(getChildAt(0)); 711310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar for (int i = 0; i < scrapSize; i++) { 712310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar RecyclerView.ViewHolder scrap = scrapList.get(i); 713888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar if (scrap.isRemoved()) { 714888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar continue; 715888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar } 716115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar final int position = scrap.getLayoutPosition(); 717310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar final int direction = position < firstChildPos != mShouldReverseLayout 718310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar ? LayoutState.LAYOUT_START : LayoutState.LAYOUT_END; 719310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (direction == LayoutState.LAYOUT_START) { 720310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar scrapExtraStart += mOrientationHelper.getDecoratedMeasurement(scrap.itemView); 721310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } else { 722310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar scrapExtraEnd += mOrientationHelper.getDecoratedMeasurement(scrap.itemView); 723b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 724310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 725b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 726310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (DEBUG) { 727310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar Log.d(TAG, "for unused scrap, decided to add " + scrapExtraStart 728310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar + " towards start and " + scrapExtraEnd + " towards end"); 729310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 730310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutState.mScrapList = scrapList; 731310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (scrapExtraStart > 0) { 732310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar View anchor = getChildClosestToStart(); 733310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar updateLayoutStateToFillStart(getPosition(anchor), startOffset); 734310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutState.mExtra = scrapExtraStart; 735310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutState.mAvailable = 0; 736888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar mLayoutState.assignPositionFromScrapList(); 737310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar fill(recycler, mLayoutState, state, false); 738310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 739310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 740310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (scrapExtraEnd > 0) { 741310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar View anchor = getChildClosestToEnd(); 742310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar updateLayoutStateToFillEnd(getPosition(anchor), endOffset); 743310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutState.mExtra = scrapExtraEnd; 744310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutState.mAvailable = 0; 745888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar mLayoutState.assignPositionFromScrapList(); 746310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar fill(recycler, mLayoutState, state, false); 747310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 748310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutState.mScrapList = null; 749310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 750310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 751cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar private void updateAnchorInfoForLayout(RecyclerView.Recycler recycler, RecyclerView.State state, 7521e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas AnchorInfo anchorInfo) { 753310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (updateAnchorFromPendingData(state, anchorInfo)) { 754b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (DEBUG) { 755310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar Log.d(TAG, "updated anchor info from pending information"); 756b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 757310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar return; 758310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 759310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 760cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar if (updateAnchorFromChildren(recycler, state, anchorInfo)) { 761310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (DEBUG) { 762310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar Log.d(TAG, "updated anchor info from existing children"); 763b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 764310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar return; 765310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 766310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (DEBUG) { 767310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar Log.d(TAG, "deciding anchor info for fresh state"); 768310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 769310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar anchorInfo.assignCoordinateFromPadding(); 770310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar anchorInfo.mPosition = mStackFromEnd ? state.getItemCount() - 1 : 0; 771310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 772b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 773310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar /** 774310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar * Finds an anchor child from existing Views. Most of the time, this is the view closest to 775310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar * start or end that has a valid position (e.g. not removed). 776310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar * <p> 777310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar * If a child has focus, it is given priority. 778310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar */ 779cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar private boolean updateAnchorFromChildren(RecyclerView.Recycler recycler, 7801e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas RecyclerView.State state, AnchorInfo anchorInfo) { 781310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (getChildCount() == 0) { 782310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar return false; 783310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 784542f1260934df280985294eaef1ec8469863281fYigit Boyar final View focused = getFocusedChild(); 785542f1260934df280985294eaef1ec8469863281fYigit Boyar if (focused != null && anchorInfo.isViewValidAsAnchor(focused, state)) { 786a0e32c54b808887323ba05392fe4e340c9ad6c0bshepshapard anchorInfo.assignFromViewAndKeepVisibleRect(focused, getPosition(focused)); 787310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar return true; 788b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 789310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (mLastStackFromEnd != mStackFromEnd) { 790310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar return false; 791310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 792cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar View referenceChild = anchorInfo.mLayoutFromEnd 793cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar ? findReferenceChildClosestToEnd(recycler, state) 794cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar : findReferenceChildClosestToStart(recycler, state); 795310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (referenceChild != null) { 796a0e32c54b808887323ba05392fe4e340c9ad6c0bshepshapard anchorInfo.assignFromView(referenceChild, getPosition(referenceChild)); 797ff6a18ccc12673e67ae2b143de1bb27048824365Yigit Boyar // If all visible views are removed in 1 pass, reference child might be out of bounds. 798ff6a18ccc12673e67ae2b143de1bb27048824365Yigit Boyar // If that is the case, offset it back to 0 so that we use these pre-layout children. 799ff6a18ccc12673e67ae2b143de1bb27048824365Yigit Boyar if (!state.isPreLayout() && supportsPredictiveItemAnimations()) { 800ff6a18ccc12673e67ae2b143de1bb27048824365Yigit Boyar // validate this child is at least partially visible. if not, offset it to start 801ff6a18ccc12673e67ae2b143de1bb27048824365Yigit Boyar final boolean notVisible = 802ff6a18ccc12673e67ae2b143de1bb27048824365Yigit Boyar mOrientationHelper.getDecoratedStart(referenceChild) >= mOrientationHelper 803ff6a18ccc12673e67ae2b143de1bb27048824365Yigit Boyar .getEndAfterPadding() 804be9090704b5671c49f1e89cb7003f4744cd69918Yigit Boyar || mOrientationHelper.getDecoratedEnd(referenceChild) 805ff6a18ccc12673e67ae2b143de1bb27048824365Yigit Boyar < mOrientationHelper.getStartAfterPadding(); 806ff6a18ccc12673e67ae2b143de1bb27048824365Yigit Boyar if (notVisible) { 807245b9720dad47a694d16a1d0f48ad462bc27989fYigit Boyar anchorInfo.mCoordinate = anchorInfo.mLayoutFromEnd 808ff6a18ccc12673e67ae2b143de1bb27048824365Yigit Boyar ? mOrientationHelper.getEndAfterPadding() 809ff6a18ccc12673e67ae2b143de1bb27048824365Yigit Boyar : mOrientationHelper.getStartAfterPadding(); 810ff6a18ccc12673e67ae2b143de1bb27048824365Yigit Boyar } 811ff6a18ccc12673e67ae2b143de1bb27048824365Yigit Boyar } 812310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar return true; 813310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 814310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar return false; 815310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 816310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 817310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar /** 818310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar * If there is a pending scroll position or saved states, updates the anchor info from that 819310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar * data and returns true 820310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar */ 821310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar private boolean updateAnchorFromPendingData(RecyclerView.State state, AnchorInfo anchorInfo) { 822ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas if (state.isPreLayout() || mPendingScrollPosition == RecyclerView.NO_POSITION) { 823310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar return false; 824310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 825310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar // validate scroll position 826310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (mPendingScrollPosition < 0 || mPendingScrollPosition >= state.getItemCount()) { 827ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas mPendingScrollPosition = RecyclerView.NO_POSITION; 8286e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mPendingScrollPositionOffset = INVALID_OFFSET; 829310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (DEBUG) { 830310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar Log.e(TAG, "ignoring invalid scroll position " + mPendingScrollPosition); 831310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 832310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar return false; 833310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 834310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 835310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar // if child is visible, try to make it a reference child and ensure it is fully visible. 836310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar // if child is not visible, align it depending on its virtual position. 837310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar anchorInfo.mPosition = mPendingScrollPosition; 838310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) { 839310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar // Anchor offset depends on how that child was laid out. Here, we update it 840310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar // according to our current view bounds 841310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar anchorInfo.mLayoutFromEnd = mPendingSavedState.mAnchorLayoutFromEnd; 842310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (anchorInfo.mLayoutFromEnd) { 8431e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding() 8441e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas - mPendingSavedState.mAnchorOffset; 845310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } else { 8461e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding() 8471e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas + mPendingSavedState.mAnchorOffset; 848310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 849310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar return true; 8506e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 851310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 852310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (mPendingScrollPositionOffset == INVALID_OFFSET) { 853310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar View child = findViewByPosition(mPendingScrollPosition); 854310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (child != null) { 855310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar final int childSize = mOrientationHelper.getDecoratedMeasurement(child); 856310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (childSize > mOrientationHelper.getTotalSpace()) { 857310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar // item does not fit. fix depending on layout direction 858310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar anchorInfo.assignCoordinateFromPadding(); 859310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar return true; 860310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 861310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar final int startGap = mOrientationHelper.getDecoratedStart(child) 862310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar - mOrientationHelper.getStartAfterPadding(); 863310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (startGap < 0) { 864310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding(); 865310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar anchorInfo.mLayoutFromEnd = false; 866310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar return true; 867310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 8681e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas final int endGap = mOrientationHelper.getEndAfterPadding() 8691e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas - mOrientationHelper.getDecoratedEnd(child); 870310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (endGap < 0) { 871310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding(); 872310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar anchorInfo.mLayoutFromEnd = true; 873310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar return true; 874310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 875310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar anchorInfo.mCoordinate = anchorInfo.mLayoutFromEnd 876310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar ? (mOrientationHelper.getDecoratedEnd(child) + mOrientationHelper 8779c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri .getTotalSpaceChange()) 878310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar : mOrientationHelper.getDecoratedStart(child); 879310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } else { // item is not visible. 880310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (getChildCount() > 0) { 881310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar // get position of any child, does not matter 882310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar int pos = getPosition(getChildAt(0)); 883310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar anchorInfo.mLayoutFromEnd = mPendingScrollPosition < pos 884310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar == mShouldReverseLayout; 885310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 886310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar anchorInfo.assignCoordinateFromPadding(); 887310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 888310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar return true; 889d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar } 890310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar // override layout from end values for consistency 891310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar anchorInfo.mLayoutFromEnd = mShouldReverseLayout; 892e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // if this changes, we should update prepareForDrop as well 893c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar if (mShouldReverseLayout) { 8941e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding() 8951e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas - mPendingScrollPositionOffset; 896c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } else { 8971e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding() 8981e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas + mPendingScrollPositionOffset; 899c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 900310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar return true; 901d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar } 902d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar 903b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar /** 904b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * @return The final offset amount for children 905b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar */ 906b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar private int fixLayoutEndGap(int endOffset, RecyclerView.Recycler recycler, 907b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView.State state, boolean canOffsetChildren) { 908b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar int gap = mOrientationHelper.getEndAfterPadding() - endOffset; 909b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar int fixOffset = 0; 910d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar if (gap > 0) { 911b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar fixOffset = -scrollBy(-gap, recycler, state); 912d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar } else { 913b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar return 0; // nothing to fix 914d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar } 915b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar // move offset according to scroll amount 916b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar endOffset += fixOffset; 917d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar if (canOffsetChildren) { 918d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar // re-calculate gap, see if we could fix it 919b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar gap = mOrientationHelper.getEndAfterPadding() - endOffset; 920d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar if (gap > 0) { 921d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar mOrientationHelper.offsetChildren(gap); 922b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar return gap + fixOffset; 923d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar } 924d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar } 925b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar return fixOffset; 9267dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 9277dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 928b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar /** 929b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * @return The final offset amount for children 930b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar */ 931b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar private int fixLayoutStartGap(int startOffset, RecyclerView.Recycler recycler, 932b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView.State state, boolean canOffsetChildren) { 933b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar int gap = startOffset - mOrientationHelper.getStartAfterPadding(); 934b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar int fixOffset = 0; 935d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar if (gap > 0) { 936d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar // check if we should fix this gap. 937b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar fixOffset = -scrollBy(gap, recycler, state); 938d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar } else { 939b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar return 0; // nothing to fix 940d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar } 941b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar startOffset += fixOffset; 942d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar if (canOffsetChildren) { 943d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar // re-calculate gap, see if we could fix it 944b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar gap = startOffset - mOrientationHelper.getStartAfterPadding(); 945d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar if (gap > 0) { 946d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar mOrientationHelper.offsetChildren(-gap); 947b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar return fixOffset - gap; 948d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar } 949d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar } 950b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar return fixOffset; 951d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar } 952d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar 953310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar private void updateLayoutStateToFillEnd(AnchorInfo anchorInfo) { 954310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar updateLayoutStateToFillEnd(anchorInfo.mPosition, anchorInfo.mCoordinate); 955310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 956310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 95794c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar private void updateLayoutStateToFillEnd(int itemPosition, int offset) { 95894c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mLayoutState.mAvailable = mOrientationHelper.getEndAfterPadding() - offset; 95994c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD : 96094c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar LayoutState.ITEM_DIRECTION_TAIL; 96194c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mLayoutState.mCurrentPosition = itemPosition; 96294c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mLayoutState.mLayoutDirection = LayoutState.LAYOUT_END; 96394c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mLayoutState.mOffset = offset; 964c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas mLayoutState.mScrollingOffset = LayoutState.SCROLLING_OFFSET_NaN; 965d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar } 966d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar 967310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar private void updateLayoutStateToFillStart(AnchorInfo anchorInfo) { 968310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar updateLayoutStateToFillStart(anchorInfo.mPosition, anchorInfo.mCoordinate); 969310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 970310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 97194c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar private void updateLayoutStateToFillStart(int itemPosition, int offset) { 97294c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mLayoutState.mAvailable = offset - mOrientationHelper.getStartAfterPadding(); 97394c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mLayoutState.mCurrentPosition = itemPosition; 97494c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL : 97594c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar LayoutState.ITEM_DIRECTION_HEAD; 97694c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mLayoutState.mLayoutDirection = LayoutState.LAYOUT_START; 97794c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mLayoutState.mOffset = offset; 978c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas mLayoutState.mScrollingOffset = LayoutState.SCROLLING_OFFSET_NaN; 979d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar 980d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar } 981d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar 982b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar protected boolean isLayoutRTL() { 9837dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar return getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL; 9847dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 9857dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 98694c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar void ensureLayoutState() { 98794c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar if (mLayoutState == null) { 988888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar mLayoutState = createLayoutState(); 9897dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 9907dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 9917dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 9927dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 993888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar * Test overrides this to plug some tracking and verification. 994888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar * 995888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar * @return A new LayoutState 996888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar */ 997888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar LayoutState createLayoutState() { 998888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar return new LayoutState(); 999888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar } 1000888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar 1001888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar /** 1002d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar * <p>Scroll the RecyclerView to make the position visible.</p> 1003d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar * 1004d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar * <p>RecyclerView will scroll the minimum amount that is necessary to make the 1005d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar * target position visible. If you are looking for a similar behavior to 1006d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar * {@link android.widget.ListView#setSelection(int)} or 1007d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar * {@link android.widget.ListView#setSelectionFromTop(int, int)}, use 1008d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar * {@link #scrollToPositionWithOffset(int, int)}.</p> 100925a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar * 101025a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar * <p>Note that scroll position change will not be reflected until the next layout call.</p> 101125a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar * 101225a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar * @param position Scroll to this adapter position 1013d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar * @see #scrollToPositionWithOffset(int, int) 101425a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar */ 101525a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar @Override 101625a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar public void scrollToPosition(int position) { 101725a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar mPendingScrollPosition = position; 1018d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar mPendingScrollPositionOffset = INVALID_OFFSET; 101975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar if (mPendingSavedState != null) { 102075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar mPendingSavedState.invalidateAnchor(); 102175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 102225a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar requestLayout(); 102325a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar } 102425a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar 102525a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar /** 1026c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar * Scroll to the specified adapter position with the given offset from resolved layout 1027c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar * start. Resolved layout start depends on {@link #getReverseLayout()}, 1028c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar * {@link ViewCompat#getLayoutDirection(android.view.View)} and {@link #getStackFromEnd()}. 1029c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar * <p> 1030c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar * For example, if layout is {@link #VERTICAL} and {@link #getStackFromEnd()} is true, calling 1031c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar * <code>scrollToPositionWithOffset(10, 20)</code> will layout such that 1032c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar * <code>item[10]</code>'s bottom is 20 pixels above the RecyclerView's bottom. 1033c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar * <p> 1034c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar * Note that scroll position change will not be reflected until the next layout call. 1035c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar * <p> 1036c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar * If you are just trying to make a position visible, use {@link #scrollToPosition(int)}. 1037d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar * 1038d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar * @param position Index (starting at 0) of the reference item. 1039d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar * @param offset The distance (in pixels) between the start edge of the item view and 1040d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar * start edge of the RecyclerView. 1041d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar * @see #setReverseLayout(boolean) 1042d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar * @see #scrollToPosition(int) 1043d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar */ 1044d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar public void scrollToPositionWithOffset(int position, int offset) { 1045d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar mPendingScrollPosition = position; 1046d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar mPendingScrollPositionOffset = offset; 104775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar if (mPendingSavedState != null) { 104875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar mPendingSavedState.invalidateAnchor(); 104975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 1050d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar requestLayout(); 1051d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar } 1052d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar 1053d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar 1054d9746b39ccf730ebb2780fa0d6a5d4970c06076dYigit Boyar /** 10557dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * {@inheritDoc} 10567dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 10577dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar @Override 1058d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, 1059d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase RecyclerView.State state) { 10602aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar if (mOrientation == VERTICAL) { 10612aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar return 0; 10622aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar } 1063d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return scrollBy(dx, recycler, state); 10647dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 10657dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 10667dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 10677dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * {@inheritDoc} 10687dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 10697dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar @Override 1070d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, 1071d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase RecyclerView.State state) { 10722aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar if (mOrientation == HORIZONTAL) { 10732aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar return 0; 10742aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar } 1075d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return scrollBy(dy, recycler, state); 10767dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 10777dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 107825a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar @Override 1079d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public int computeHorizontalScrollOffset(RecyclerView.State state) { 10808c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar return computeScrollOffset(state); 108125a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar } 108225a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar 108325a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar @Override 1084d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public int computeVerticalScrollOffset(RecyclerView.State state) { 10858c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar return computeScrollOffset(state); 108625a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar } 108725a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar 108825a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar @Override 1089d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public int computeHorizontalScrollExtent(RecyclerView.State state) { 10908c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar return computeScrollExtent(state); 109125a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar } 109225a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar 109325a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar @Override 1094d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public int computeVerticalScrollExtent(RecyclerView.State state) { 10958c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar return computeScrollExtent(state); 109625a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar } 109725a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar 109825a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar @Override 1099d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public int computeHorizontalScrollRange(RecyclerView.State state) { 11008c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar return computeScrollRange(state); 110125a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar } 110225a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar 110325a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar @Override 1104d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public int computeVerticalScrollRange(RecyclerView.State state) { 11058c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar return computeScrollRange(state); 11068c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar } 11078c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar 11088c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar private int computeScrollOffset(RecyclerView.State state) { 1109d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (getChildCount() == 0) { 11108c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar return 0; 11118c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar } 1112ffff7c903fa7dbf176fd251f77a959e6b9531456Yigit Boyar ensureLayoutState(); 1113d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return ScrollbarHelper.computeScrollOffset(state, mOrientationHelper, 11146490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true), 11156490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true), 11166490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar this, mSmoothScrollbarEnabled, mShouldReverseLayout); 11178c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar } 11188c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar 11198c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar private int computeScrollExtent(RecyclerView.State state) { 1120d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (getChildCount() == 0) { 1121d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return 0; 11228c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar } 1123ffff7c903fa7dbf176fd251f77a959e6b9531456Yigit Boyar ensureLayoutState(); 1124d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return ScrollbarHelper.computeScrollExtent(state, mOrientationHelper, 11256490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true), 11266490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true), 11276490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar this, mSmoothScrollbarEnabled); 11288c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar } 11298c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar 11308c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar private int computeScrollRange(RecyclerView.State state) { 1131d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (getChildCount() == 0) { 11328c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar return 0; 11338c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar } 1134ffff7c903fa7dbf176fd251f77a959e6b9531456Yigit Boyar ensureLayoutState(); 1135d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return ScrollbarHelper.computeScrollRange(state, mOrientationHelper, 11366490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true), 11376490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true), 11386490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar this, mSmoothScrollbarEnabled); 11398c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar } 11408c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar 11418c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar /** 11428c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar * When smooth scrollbar is enabled, the position and size of the scrollbar thumb is computed 11438c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar * based on the number of visible pixels in the visible items. This however assumes that all 11448c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar * list items have similar or equal widths or heights (depending on list orientation). 11458c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar * If you use a list in which items have different dimensions, the scrollbar will change 11468c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar * appearance as the user scrolls through the list. To avoid this issue, you need to disable 11478c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar * this property. 11488c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar * 11498c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar * When smooth scrollbar is disabled, the position and size of the scrollbar thumb is based 11508c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar * solely on the number of items in the adapter and the position of the visible items inside 11518c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar * the adapter. This provides a stable scrollbar as the user navigates through a list of items 11528c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar * with varying widths / heights. 11538c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar * 11548c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar * @param enabled Whether or not to enable smooth scrollbar. 11558c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar * 11568c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar * @see #setSmoothScrollbarEnabled(boolean) 11578c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar */ 11588c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar public void setSmoothScrollbarEnabled(boolean enabled) { 11598c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar mSmoothScrollbarEnabled = enabled; 11608c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar } 11618c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar 11628c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar /** 11638c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar * Returns the current state of the smooth scrollbar feature. It is enabled by default. 11648c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar * 11658c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar * @return True if smooth scrollbar is enabled, false otherwise. 11668c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar * 11678c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar * @see #setSmoothScrollbarEnabled(boolean) 11688c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar */ 11698c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar public boolean isSmoothScrollbarEnabled() { 11708c23e5d30a95b7807109db376d43a07a52e00802Yigit Boyar return mSmoothScrollbarEnabled; 117125a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar } 117225a1df3b5e7fcd875440ccf6b877cb280d63f418Yigit Boyar 117394c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar private void updateLayoutState(int layoutDirection, int requiredSpace, 11740447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar boolean canUseExistingSpace, RecyclerView.State state) { 1175f3844451301cb14ca885e125cb27f108a834c386Yigit Boyar // If parent provides a hint, don't measure unlimited. 1176f3844451301cb14ca885e125cb27f108a834c386Yigit Boyar mLayoutState.mInfinite = resolveIsInfinite(); 117794c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mLayoutState.mExtra = getExtraLayoutSpace(state); 117894c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mLayoutState.mLayoutDirection = layoutDirection; 11793ebdce1f656e8f92d28c81a6951052e38d6abe07Aurimas Liutikas int scrollingOffset; 118094c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar if (layoutDirection == LayoutState.LAYOUT_END) { 1181c032ec5462f6c7c07031310090e23af65841deeeYigit Boyar mLayoutState.mExtra += mOrientationHelper.getEndPadding(); 11827dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar // get the first child in the direction we are going 1183668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar final View child = getChildClosestToEnd(); 11847dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar // the direction in which we are traversing children 118594c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD 118694c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar : LayoutState.ITEM_DIRECTION_TAIL; 118794c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection; 118894c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mLayoutState.mOffset = mOrientationHelper.getDecoratedEnd(child); 11892aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar // calculate how much we can scroll without adding new children (independent of layout) 11903ebdce1f656e8f92d28c81a6951052e38d6abe07Aurimas Liutikas scrollingOffset = mOrientationHelper.getDecoratedEnd(child) 11912aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar - mOrientationHelper.getEndAfterPadding(); 11922aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar 11937dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } else { 1194668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar final View child = getChildClosestToStart(); 1195c032ec5462f6c7c07031310090e23af65841deeeYigit Boyar mLayoutState.mExtra += mOrientationHelper.getStartAfterPadding(); 119694c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL 119794c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar : LayoutState.ITEM_DIRECTION_HEAD; 119894c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection; 119994c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mLayoutState.mOffset = mOrientationHelper.getDecoratedStart(child); 12003ebdce1f656e8f92d28c81a6951052e38d6abe07Aurimas Liutikas scrollingOffset = -mOrientationHelper.getDecoratedStart(child) 12012aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar + mOrientationHelper.getStartAfterPadding(); 12022aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar } 120394c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mLayoutState.mAvailable = requiredSpace; 12042aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar if (canUseExistingSpace) { 12053ebdce1f656e8f92d28c81a6951052e38d6abe07Aurimas Liutikas mLayoutState.mAvailable -= scrollingOffset; 12062aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar } 12073ebdce1f656e8f92d28c81a6951052e38d6abe07Aurimas Liutikas mLayoutState.mScrollingOffset = scrollingOffset; 12082aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar } 12092aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar 1210f3844451301cb14ca885e125cb27f108a834c386Yigit Boyar boolean resolveIsInfinite() { 1211f3844451301cb14ca885e125cb27f108a834c386Yigit Boyar return mOrientationHelper.getMode() == View.MeasureSpec.UNSPECIFIED 1212f3844451301cb14ca885e125cb27f108a834c386Yigit Boyar && mOrientationHelper.getEnd() == 0; 1213f3844451301cb14ca885e125cb27f108a834c386Yigit Boyar } 1214f3844451301cb14ca885e125cb27f108a834c386Yigit Boyar 1215945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik void collectPrefetchPositionsForLayoutState(RecyclerView.State state, LayoutState layoutState, 12163104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik LayoutPrefetchRegistry layoutPrefetchRegistry) { 1217ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik final int pos = layoutState.mCurrentPosition; 1218ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik if (pos >= 0 && pos < state.getItemCount()) { 12196425bbc0816bc5c2dbd14010d8dee4245d14fd1dChris Craik layoutPrefetchRegistry.addPosition(pos, Math.max(0, layoutState.mScrollingOffset)); 1220ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik } 1221ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik } 1222ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik 12231e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik @Override 12241e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik public void collectInitialPrefetchPositions(int adapterItemCount, 12253104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik LayoutPrefetchRegistry layoutPrefetchRegistry) { 12261e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik final boolean fromEnd; 12271e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik final int anchorPos; 12281e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) { 12291e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // use restored state, since it hasn't been resolved yet 12301e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik fromEnd = mPendingSavedState.mAnchorLayoutFromEnd; 12311e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik anchorPos = mPendingSavedState.mAnchorPosition; 12321e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } else { 12331e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik resolveShouldLayoutReverse(); 12341e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik fromEnd = mShouldReverseLayout; 1235ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas if (mPendingScrollPosition == RecyclerView.NO_POSITION) { 12361e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik anchorPos = fromEnd ? adapterItemCount - 1 : 0; 12371e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } else { 12381e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik anchorPos = mPendingScrollPosition; 12391e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 12401e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 12411e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 12421e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik final int direction = fromEnd 12431e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik ? LayoutState.ITEM_DIRECTION_HEAD 12441e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik : LayoutState.ITEM_DIRECTION_TAIL; 12451e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik int targetPos = anchorPos; 1246d6696c2abea2771acd000c2269cf9113acc6c0a9Chris Craik for (int i = 0; i < mInitialPrefetchItemCount; i++) { 12471e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik if (targetPos >= 0 && targetPos < adapterItemCount) { 12483104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik layoutPrefetchRegistry.addPosition(targetPos, 0); 12491e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } else { 12501e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik break; // no more to prefetch 12511e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 12521e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik targetPos += direction; 12531e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 12541e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 12551e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 12563104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik /** 12573104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * Sets the number of items to prefetch in 12583104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)}, which defines 12593104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * how many inner items should be prefetched when this LayoutManager's RecyclerView 12603104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * is nested inside another RecyclerView. 12613104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * 12623104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * <p>Set this value to the number of items this inner LayoutManager will display when it is 12633104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * first scrolled into the viewport. RecyclerView will attempt to prefetch that number of items 12643104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * so they are ready, avoiding jank as the inner RecyclerView is scrolled into the viewport.</p> 12653104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * 12663104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * <p>For example, take a vertically scrolling RecyclerView with horizontally scrolling inner 12673104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * RecyclerViews. The rows always have 4 items visible in them (or 5 if not aligned). Passing 12683104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * <code>4</code> to this method for each inner RecyclerView's LinearLayoutManager will enable 12693104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * RecyclerView's prefetching feature to do create/bind work for 4 views within a row early, 12703104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * before it is scrolled on screen, instead of just the default 2.</p> 12713104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * 12723104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * <p>Calling this method does nothing unless the LayoutManager is in a RecyclerView 12733104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * nested in another RecyclerView.</p> 12743104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * 12753104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * <p class="note"><strong>Note:</strong> Setting this value to be larger than the number of 12763104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * views that will be visible in this view can incur unnecessary bind work, and an increase to 12773104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * the number of Views created and in active use.</p> 12783104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * 12793104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * @param itemCount Number of items to prefetch 12803104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * 12813104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * @see #isItemPrefetchEnabled() 1282d6696c2abea2771acd000c2269cf9113acc6c0a9Chris Craik * @see #getInitialPrefetchItemCount() 12833104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * @see #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry) 12843104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik */ 12851e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik public void setInitialPrefetchItemCount(int itemCount) { 1286d6696c2abea2771acd000c2269cf9113acc6c0a9Chris Craik mInitialPrefetchItemCount = itemCount; 12871e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 12881e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 12893104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik /** 12903104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * Gets the number of items to prefetch in 12913104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)}, which defines 12923104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * how many inner items should be prefetched when this LayoutManager's RecyclerView 12933104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * is nested inside another RecyclerView. 12943104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * 12953104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * @see #isItemPrefetchEnabled() 12963104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * @see #setInitialPrefetchItemCount(int) 12973104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * @see #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry) 12983104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * 12993104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik * @return number of items to prefetch. 13003104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik */ 1301d6696c2abea2771acd000c2269cf9113acc6c0a9Chris Craik public int getInitialPrefetchItemCount() { 1302d6696c2abea2771acd000c2269cf9113acc6c0a9Chris Craik return mInitialPrefetchItemCount; 1303d6696c2abea2771acd000c2269cf9113acc6c0a9Chris Craik } 1304d6696c2abea2771acd000c2269cf9113acc6c0a9Chris Craik 1305ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik @Override 13061e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state, 13073104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik LayoutPrefetchRegistry layoutPrefetchRegistry) { 1308ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik int delta = (mOrientation == HORIZONTAL) ? dx : dy; 1309466d1f5850356125fd12c4f6f0032eb2d72f4ff1Chris Craik if (getChildCount() == 0 || delta == 0) { 1310466d1f5850356125fd12c4f6f0032eb2d72f4ff1Chris Craik // can't support this scroll, so don't bother prefetching 1311945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik return; 1312ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik } 1313ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik 1314213c0aa34bc54fb1b540a040609f097a4e4f65faChris Craik ensureLayoutState(); 1315ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik final int layoutDirection = delta > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START; 1316ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik final int absDy = Math.abs(delta); 1317ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik updateLayoutState(layoutDirection, absDy, true, state); 13183104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik collectPrefetchPositionsForLayoutState(state, mLayoutState, layoutPrefetchRegistry); 1319ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik } 1320ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik 1321061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { 13222aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar if (getChildCount() == 0 || dy == 0) { 13232aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar return 0; 13242aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar } 1325c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mLayoutState.mRecycle = true; 132694c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar ensureLayoutState(); 132794c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar final int layoutDirection = dy > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START; 13282aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar final int absDy = Math.abs(dy); 132994c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar updateLayoutState(layoutDirection, absDy, true, state); 13303ebdce1f656e8f92d28c81a6951052e38d6abe07Aurimas Liutikas final int consumed = mLayoutState.mScrollingOffset 13313ebdce1f656e8f92d28c81a6951052e38d6abe07Aurimas Liutikas + fill(recycler, mLayoutState, state, false); 13322aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar if (consumed < 0) { 13337dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar if (DEBUG) { 13342aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar Log.d(TAG, "Don't have any more elements to scroll"); 13357dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 13362aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar return 0; 13372aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar } 13382aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar final int scrolled = absDy > consumed ? layoutDirection * consumed : dy; 13392aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar mOrientationHelper.offsetChildren(-scrolled); 13402aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar if (DEBUG) { 13412aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar Log.d(TAG, "scroll req: " + dy + " scrolled: " + scrolled); 13427dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 1343baa826219e2ae7c054011cabf992cd7a37fe2a8fGabriel Peal mLayoutState.mLastScrollDelta = scrolled; 13442aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar return scrolled; 13457dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 13467dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 13470bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar @Override 13480bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar public void assertNotInLayoutOrScroll(String message) { 13490bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar if (mPendingSavedState == null) { 13500bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar super.assertNotInLayoutOrScroll(message); 13510bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar } 13520bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar } 13530bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar 13547dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 13557dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Recycles children between given indices. 13567dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * 13577dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * @param startIndex inclusive 13587dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * @param endIndex exclusive 13597dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 13607dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar private void recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex) { 13617dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar if (startIndex == endIndex) { 13627dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar return; 13637dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 13647dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar if (DEBUG) { 13657dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar Log.d(TAG, "Recycling " + Math.abs(startIndex - endIndex) + " items"); 13667dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 13677dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar if (endIndex > startIndex) { 13687dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar for (int i = endIndex - 1; i >= startIndex; i--) { 13697dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar removeAndRecycleViewAt(i, recycler); 13707dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 13717dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } else { 13727dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar for (int i = startIndex; i > endIndex; i--) { 13737dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar removeAndRecycleViewAt(i, recycler); 13747dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 13757dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 13767dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 13777dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 13787dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 13797dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Recycles views that went out of bounds after scrolling towards the end of the layout. 13804510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar * <p> 13814510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar * Checks both layout position and visible position to guarantee that the view is not visible. 13827dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * 1383ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * @param recycler Recycler instance of {@link RecyclerView} 13847dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * @param dt This can be used to add additional padding to the visible area. This is used 1385e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar * to detect children that will go out of bounds after scrolling, without 1386e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar * actually moving them. 13877dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 13887dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar private void recycleViewsFromStart(RecyclerView.Recycler recycler, int dt) { 13897dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar if (dt < 0) { 13907dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar if (DEBUG) { 13917dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar Log.d(TAG, "Called recycle from start with a negative value. This might happen" 13927dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar + " during layout changes but may be sign of a bug"); 13937dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 13947dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar return; 13957dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 13963ed05355fded55e438477b23a1864c3b6d129342Yigit Boyar // ignore padding, ViewGroup may not clip children. 13973ed05355fded55e438477b23a1864c3b6d129342Yigit Boyar final int limit = dt; 13987dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar final int childCount = getChildCount(); 13997dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar if (mShouldReverseLayout) { 14007dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar for (int i = childCount - 1; i >= 0; i--) { 14017dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar View child = getChildAt(i); 14024510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar if (mOrientationHelper.getDecoratedEnd(child) > limit 14034510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar || mOrientationHelper.getTransformedEndWithDecoration(child) > limit) { 14044510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar // stop here 14057dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar recycleChildren(recycler, childCount - 1, i); 14067dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar return; 14077dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 14087dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 14097dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } else { 14107dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar for (int i = 0; i < childCount; i++) { 14117dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar View child = getChildAt(i); 14124510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar if (mOrientationHelper.getDecoratedEnd(child) > limit 14134510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar || mOrientationHelper.getTransformedEndWithDecoration(child) > limit) { 14144510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar // stop here 14157dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar recycleChildren(recycler, 0, i); 14167dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar return; 14177dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 14187dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 14197dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 14207dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 14217dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 14227dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 14237dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 14247dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Recycles views that went out of bounds after scrolling towards the start of the layout. 14254510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar * <p> 14264510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar * Checks both layout position and visible position to guarantee that the view is not visible. 14277dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * 1428ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * @param recycler Recycler instance of {@link RecyclerView} 14297dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * @param dt This can be used to add additional padding to the visible area. This is used 14307dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * to detect children that will go out of bounds after scrolling, without 14317dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * actually moving them. 14327dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 14337dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar private void recycleViewsFromEnd(RecyclerView.Recycler recycler, int dt) { 14347dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar final int childCount = getChildCount(); 14357dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar if (dt < 0) { 14367dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar if (DEBUG) { 14377dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar Log.d(TAG, "Called recycle from end with a negative value. This might happen" 14387dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar + " during layout changes but may be sign of a bug"); 14397dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 14407dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar return; 14417dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 14423ed05355fded55e438477b23a1864c3b6d129342Yigit Boyar final int limit = mOrientationHelper.getEnd() - dt; 14437dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar if (mShouldReverseLayout) { 14447dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar for (int i = 0; i < childCount; i++) { 14457dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar View child = getChildAt(i); 14464510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar if (mOrientationHelper.getDecoratedStart(child) < limit 14474510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar || mOrientationHelper.getTransformedStartWithDecoration(child) < limit) { 14484510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar // stop here 14497dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar recycleChildren(recycler, 0, i); 14507dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar return; 14517dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 14527dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 14537dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } else { 14547dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar for (int i = childCount - 1; i >= 0; i--) { 14557dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar View child = getChildAt(i); 14564510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar if (mOrientationHelper.getDecoratedStart(child) < limit 14574510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar || mOrientationHelper.getTransformedStartWithDecoration(child) < limit) { 14584510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar // stop here 14597dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar recycleChildren(recycler, childCount - 1, i); 14607dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar return; 14617dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 14627dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 14637dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 14647dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 14657dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 14667dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 146794c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar * Helper method to call appropriate recycle method depending on current layout direction 14687dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * 14697dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * @param recycler Current recycler that is attached to RecyclerView 147094c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar * @param layoutState Current layout state. Right now, this object does not change but 14717dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * we may consider moving it out of this view so passing around as a 147294c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar * parameter for now, rather than accessing {@link #mLayoutState} 1473ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * @see #recycleViewsFromStart(RecyclerView.Recycler, int) 1474ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * @see #recycleViewsFromEnd(RecyclerView.Recycler, int) 1475ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * @see LinearLayoutManager.LayoutState#mLayoutDirection 14767dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 147794c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) { 14784143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (!layoutState.mRecycle || layoutState.mInfinite) { 1479c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar return; 1480c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 148194c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { 148294c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar recycleViewsFromEnd(recycler, layoutState.mScrollingOffset); 14837dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } else { 148494c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar recycleViewsFromStart(recycler, layoutState.mScrollingOffset); 14857dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 14867dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 14877dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 14887dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 148994c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar * The magic functions :). Fills the given layout, defined by the layoutState. This is fairly 1490ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * independent from the rest of the {@link LinearLayoutManager} 14917dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * and with little change, can be made publicly available as a helper class. 14927dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * 14930447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar * @param recycler Current recycler that is attached to RecyclerView 149494c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar * @param layoutState Configuration on how we should fill out the available space. 14950447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar * @param state Context passed by the RecyclerView to control scroll steps. 14960447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar * @param stopOnFocusable If true, filling stops in the first focusable new child 1497c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas * @return Number of pixels that it added. Useful for scroll functions. 14987dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 1499b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar int fill(RecyclerView.Recycler recycler, LayoutState layoutState, 15006d35693091a469a1048e418171176a6792f5d015Yigit Boyar RecyclerView.State state, boolean stopOnFocusable) { 15017dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar // max offset we should set is mFastScroll + available 150294c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar final int start = layoutState.mAvailable; 1503c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) { 15047dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar // TODO ugly bug fix. should not happen 150594c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar if (layoutState.mAvailable < 0) { 150694c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar layoutState.mScrollingOffset += layoutState.mAvailable; 15077dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 150894c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar recycleByLayoutState(recycler, layoutState); 15097dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 151094c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar int remainingSpace = layoutState.mAvailable + layoutState.mExtra; 1511e835a0f12bb6c6a7aa323cb9f58c29e2a9f232adChris Craik LayoutChunkResult layoutChunkResult = mLayoutChunkResult; 15124143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) { 1513b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar layoutChunkResult.resetInternal(); 1514ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas if (RecyclerView.VERBOSE_TRACING) { 15157c29d5250936de17de62ef295794ff98aa5d0dc4Chris Craik TraceCompat.beginSection("LLM LayoutChunk"); 15167c29d5250936de17de62ef295794ff98aa5d0dc4Chris Craik } 1517b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar layoutChunk(recycler, state, layoutState, layoutChunkResult); 1518ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas if (RecyclerView.VERBOSE_TRACING) { 15197c29d5250936de17de62ef295794ff98aa5d0dc4Chris Craik TraceCompat.endSection(); 15207c29d5250936de17de62ef295794ff98aa5d0dc4Chris Craik } 1521b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (layoutChunkResult.mFinished) { 1522b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar break; 1523b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1524b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection; 15256d35693091a469a1048e418171176a6792f5d015Yigit Boyar /** 15266d35693091a469a1048e418171176a6792f5d015Yigit Boyar * Consume the available space if: 1527b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * * layoutChunk did not request to be ignored 15286d35693091a469a1048e418171176a6792f5d015Yigit Boyar * * OR we are laying out scrap children 15296d35693091a469a1048e418171176a6792f5d015Yigit Boyar * * OR we are not doing pre-layout 15306d35693091a469a1048e418171176a6792f5d015Yigit Boyar */ 1531b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (!layoutChunkResult.mIgnoreConsumed || mLayoutState.mScrapList != null 1532b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar || !state.isPreLayout()) { 1533b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar layoutState.mAvailable -= layoutChunkResult.mConsumed; 1534b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar // we keep a separate remaining space because mAvailable is important for recycling 1535b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar remainingSpace -= layoutChunkResult.mConsumed; 1536b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 15377dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 1538c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) { 1539b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar layoutState.mScrollingOffset += layoutChunkResult.mConsumed; 154094c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar if (layoutState.mAvailable < 0) { 154194c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar layoutState.mScrollingOffset += layoutState.mAvailable; 15427dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 154394c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar recycleByLayoutState(recycler, layoutState); 15447dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 1545b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (stopOnFocusable && layoutChunkResult.mFocusable) { 15462aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar break; 15472aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar } 15487dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 15497dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar if (DEBUG) { 15507dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar validateChildOrder(); 15517dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 155294c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar return start - layoutState.mAvailable; 15537dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 15547dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 1555b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, 1556b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar LayoutState layoutState, LayoutChunkResult result) { 1557b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar View view = layoutState.next(recycler); 1558b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (view == null) { 1559b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (DEBUG && layoutState.mScrapList == null) { 1560b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar throw new RuntimeException("received null view when unexpected"); 1561b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1562b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar // if we are laying out views in scrap, this may return null which means there is 1563b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar // no more items to layout. 1564b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar result.mFinished = true; 1565b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return; 1566b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1567ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams(); 1568b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (layoutState.mScrapList == null) { 1569b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (mShouldReverseLayout == (layoutState.mLayoutDirection 1570b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar == LayoutState.LAYOUT_START)) { 1571b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar addView(view); 1572b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 1573b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar addView(view, 0); 1574b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1575b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 1576b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (mShouldReverseLayout == (layoutState.mLayoutDirection 1577b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar == LayoutState.LAYOUT_START)) { 1578b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar addDisappearingView(view); 1579b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 1580b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar addDisappearingView(view, 0); 1581b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1582b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1583b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar measureChildWithMargins(view, 0, 0); 1584b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view); 1585b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar int left, top, right, bottom; 1586b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (mOrientation == VERTICAL) { 1587b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (isLayoutRTL()) { 1588b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar right = getWidth() - getPaddingRight(); 1589b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar left = right - mOrientationHelper.getDecoratedMeasurementInOther(view); 1590b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 1591b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar left = getPaddingLeft(); 1592b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar right = left + mOrientationHelper.getDecoratedMeasurementInOther(view); 1593b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1594b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { 1595b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar bottom = layoutState.mOffset; 1596b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar top = layoutState.mOffset - result.mConsumed; 1597b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 1598b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar top = layoutState.mOffset; 1599b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar bottom = layoutState.mOffset + result.mConsumed; 1600b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1601b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 1602b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar top = getPaddingTop(); 1603b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view); 1604b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1605b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { 1606b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar right = layoutState.mOffset; 1607b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar left = layoutState.mOffset - result.mConsumed; 1608b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 1609b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar left = layoutState.mOffset; 1610b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar right = layoutState.mOffset + result.mConsumed; 1611b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1612b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1613b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar // We calculate everything with View's bounding box (which includes decor and margins) 1614b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar // To calculate correct layout position, we subtract margins. 16155f538711872b050b93f49a5dcaff1753e0299449Dake Gu layoutDecoratedWithMargins(view, left, top, right, bottom); 1616b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (DEBUG) { 1617b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:" 1618b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar + (left + params.leftMargin) + ", t:" + (top + params.topMargin) + ", r:" 1619b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar + (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin)); 1620b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1621b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar // Consume the available space if the view is not removed OR changed 1622b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (params.isItemRemoved() || params.isItemChanged()) { 1623b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar result.mIgnoreConsumed = true; 1624b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1625b174744db6a70f384d44caeb43e10854652e81b9Keyvan Amiri result.mFocusable = view.hasFocusable(); 1626b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1627b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 16284143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar @Override 16294143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar boolean shouldMeasureTwice() { 16304143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar return getHeightMode() != View.MeasureSpec.EXACTLY 16314143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar && getWidthMode() != View.MeasureSpec.EXACTLY 16324143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar && hasFlexibleChildInBothOrientations(); 16334143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 16344143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 16352aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar /** 16362aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar * Converts a focusDirection to orientation. 16372aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar * 16382aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar * @param focusDirection One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN}, 16392aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, 16402aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar * {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD} 16412aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar * or 0 for not applicable 164294c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar * @return {@link LayoutState#LAYOUT_START} or {@link LayoutState#LAYOUT_END} if focus direction 164394c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar * is applicable to current state, {@link LayoutState#INVALID_LAYOUT} otherwise. 16442aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar */ 1645f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar int convertFocusDirectionToLayoutDirection(int focusDirection) { 16462aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar switch (focusDirection) { 16472aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar case View.FOCUS_BACKWARD: 1648d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar if (mOrientation == VERTICAL) { 1649d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar return LayoutState.LAYOUT_START; 1650d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar } else if (isLayoutRTL()) { 1651d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar return LayoutState.LAYOUT_END; 1652d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar } else { 1653d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar return LayoutState.LAYOUT_START; 1654d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar } 16552aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar case View.FOCUS_FORWARD: 1656d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar if (mOrientation == VERTICAL) { 1657d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar return LayoutState.LAYOUT_END; 1658d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar } else if (isLayoutRTL()) { 1659d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar return LayoutState.LAYOUT_START; 1660d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar } else { 1661d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar return LayoutState.LAYOUT_END; 1662d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar } 16632aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar case View.FOCUS_UP: 166494c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar return mOrientation == VERTICAL ? LayoutState.LAYOUT_START 166594c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar : LayoutState.INVALID_LAYOUT; 16662aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar case View.FOCUS_DOWN: 166794c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar return mOrientation == VERTICAL ? LayoutState.LAYOUT_END 166894c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar : LayoutState.INVALID_LAYOUT; 16692aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar case View.FOCUS_LEFT: 167094c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar return mOrientation == HORIZONTAL ? LayoutState.LAYOUT_START 167194c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar : LayoutState.INVALID_LAYOUT; 16722aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar case View.FOCUS_RIGHT: 167394c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar return mOrientation == HORIZONTAL ? LayoutState.LAYOUT_END 167494c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar : LayoutState.INVALID_LAYOUT; 16752aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar default: 16762aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar if (DEBUG) { 16772aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar Log.d(TAG, "Unknown focus request:" + focusDirection); 16782aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar } 167994c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar return LayoutState.INVALID_LAYOUT; 16802aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar } 16812aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar 16822aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar } 16832aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar 16842aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar /** 16852aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar * Convenience method to find the child closes to start. Caller should check it has enough 16862aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar * children. 1687719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar * 16882aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar * @return The child closes to start of the layout from user's perspective. 16892aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar */ 1690668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar private View getChildClosestToStart() { 1691668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar return getChildAt(mShouldReverseLayout ? getChildCount() - 1 : 0); 16922aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar } 16932aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar 16942aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar /** 16952aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar * Convenience method to find the child closes to end. Caller should check it has enough 16962aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar * children. 1697719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar * 16982aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar * @return The child closes to end of the layout from user's perspective. 16992aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar */ 1700668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar private View getChildClosestToEnd() { 1701668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar return getChildAt(mShouldReverseLayout ? 0 : getChildCount() - 1); 17022aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar } 17032aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar 17046490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar /** 17056490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar * Convenience method to find the visible child closes to start. Caller should check if it has 17066490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar * enough children. 17076490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar * 17086490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar * @param completelyVisible Whether child should be completely visible or not 17096490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar * @return The first visible child closest to start of the layout from user's perspective. 17106490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar */ 17116490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar private View findFirstVisibleChildClosestToStart(boolean completelyVisible, 17126490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar boolean acceptPartiallyVisible) { 17136490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar if (mShouldReverseLayout) { 17146490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar return findOneVisibleChild(getChildCount() - 1, -1, completelyVisible, 17156490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar acceptPartiallyVisible); 17166490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar } else { 17176490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar return findOneVisibleChild(0, getChildCount(), completelyVisible, 17186490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar acceptPartiallyVisible); 17196490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar } 17206490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar } 17216490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar 17226490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar /** 17236490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar * Convenience method to find the visible child closes to end. Caller should check if it has 17246490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar * enough children. 17256490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar * 17266490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar * @param completelyVisible Whether child should be completely visible or not 17276490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar * @return The first visible child closest to end of the layout from user's perspective. 17286490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar */ 17296490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar private View findFirstVisibleChildClosestToEnd(boolean completelyVisible, 17306490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar boolean acceptPartiallyVisible) { 17316490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar if (mShouldReverseLayout) { 17326490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar return findOneVisibleChild(0, getChildCount(), completelyVisible, 17336490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar acceptPartiallyVisible); 17346490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar } else { 17356490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar return findOneVisibleChild(getChildCount() - 1, -1, completelyVisible, 17366490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar acceptPartiallyVisible); 17376490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar } 17386490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar } 17396490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar 1740719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar 1741719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar /** 1742719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar * Among the children that are suitable to be considered as an anchor child, returns the one 1743719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar * closest to the end of the layout. 1744719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar * <p> 1745719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar * Due to ambiguous adapter updates or children being removed, some children's positions may be 1746719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar * invalid. This method is a best effort to find a position within adapter bounds if possible. 17478a1f650a5d51c95b0d4e34c11b4c6d453ce6696cYigit Boyar * <p> 17488a1f650a5d51c95b0d4e34c11b4c6d453ce6696cYigit Boyar * It also prioritizes children that are within the visible bounds. 1749719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar * @return A View that can be used an an anchor View. 1750719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar */ 1751cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar private View findReferenceChildClosestToEnd(RecyclerView.Recycler recycler, 17521e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas RecyclerView.State state) { 1753cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar return mShouldReverseLayout ? findFirstReferenceChild(recycler, state) : 1754cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar findLastReferenceChild(recycler, state); 1755719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar } 1756719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar 1757719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar /** 1758719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar * Among the children that are suitable to be considered as an anchor child, returns the one 1759719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar * closest to the start of the layout. 1760719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar * <p> 1761719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar * Due to ambiguous adapter updates or children being removed, some children's positions may be 1762719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar * invalid. This method is a best effort to find a position within adapter bounds if possible. 17638a1f650a5d51c95b0d4e34c11b4c6d453ce6696cYigit Boyar * <p> 17648a1f650a5d51c95b0d4e34c11b4c6d453ce6696cYigit Boyar * It also prioritizes children that are within the visible bounds. 1765719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar * 1766719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar * @return A View that can be used an an anchor View. 1767719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar */ 1768cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar private View findReferenceChildClosestToStart(RecyclerView.Recycler recycler, 17691e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas RecyclerView.State state) { 1770cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar return mShouldReverseLayout ? findLastReferenceChild(recycler, state) : 1771cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar findFirstReferenceChild(recycler, state); 1772719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar } 1773719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar 1774cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar private View findFirstReferenceChild(RecyclerView.Recycler recycler, RecyclerView.State state) { 1775cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar return findReferenceChild(recycler, state, 0, getChildCount(), state.getItemCount()); 1776719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar } 1777719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar 1778cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar private View findLastReferenceChild(RecyclerView.Recycler recycler, RecyclerView.State state) { 1779cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar return findReferenceChild(recycler, state, getChildCount() - 1, -1, state.getItemCount()); 17808a1f650a5d51c95b0d4e34c11b4c6d453ce6696cYigit Boyar } 17818a1f650a5d51c95b0d4e34c11b4c6d453ce6696cYigit Boyar 17821f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar // overridden by GridLayoutManager 1783cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar View findReferenceChild(RecyclerView.Recycler recycler, RecyclerView.State state, 17841e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas int start, int end, int itemCount) { 1785ffff7c903fa7dbf176fd251f77a959e6b9531456Yigit Boyar ensureLayoutState(); 17868a1f650a5d51c95b0d4e34c11b4c6d453ce6696cYigit Boyar View invalidMatch = null; 17878a1f650a5d51c95b0d4e34c11b4c6d453ce6696cYigit Boyar View outOfBoundsMatch = null; 17888a1f650a5d51c95b0d4e34c11b4c6d453ce6696cYigit Boyar final int boundsStart = mOrientationHelper.getStartAfterPadding(); 17898a1f650a5d51c95b0d4e34c11b4c6d453ce6696cYigit Boyar final int boundsEnd = mOrientationHelper.getEndAfterPadding(); 17908a1f650a5d51c95b0d4e34c11b4c6d453ce6696cYigit Boyar final int diff = end > start ? 1 : -1; 17918a1f650a5d51c95b0d4e34c11b4c6d453ce6696cYigit Boyar for (int i = start; i != end; i += diff) { 1792719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar final View view = getChildAt(i); 1793719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar final int position = getPosition(view); 1794719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar if (position >= 0 && position < itemCount) { 1795ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas if (((RecyclerView.LayoutParams) view.getLayoutParams()).isItemRemoved()) { 17968a1f650a5d51c95b0d4e34c11b4c6d453ce6696cYigit Boyar if (invalidMatch == null) { 17978a1f650a5d51c95b0d4e34c11b4c6d453ce6696cYigit Boyar invalidMatch = view; // removed item, least preferred 17988a1f650a5d51c95b0d4e34c11b4c6d453ce6696cYigit Boyar } 17991e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas } else if (mOrientationHelper.getDecoratedStart(view) >= boundsEnd 18001e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas || mOrientationHelper.getDecoratedEnd(view) < boundsStart) { 18018a1f650a5d51c95b0d4e34c11b4c6d453ce6696cYigit Boyar if (outOfBoundsMatch == null) { 18028a1f650a5d51c95b0d4e34c11b4c6d453ce6696cYigit Boyar outOfBoundsMatch = view; // item is not visible, less preferred 18038a1f650a5d51c95b0d4e34c11b4c6d453ce6696cYigit Boyar } 18048a1f650a5d51c95b0d4e34c11b4c6d453ce6696cYigit Boyar } else { 18058a1f650a5d51c95b0d4e34c11b4c6d453ce6696cYigit Boyar return view; 18068a1f650a5d51c95b0d4e34c11b4c6d453ce6696cYigit Boyar } 1807719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar } 1808719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar } 18098a1f650a5d51c95b0d4e34c11b4c6d453ce6696cYigit Boyar return outOfBoundsMatch != null ? outOfBoundsMatch : invalidMatch; 1810719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar } 1811719a0dfbcf2ca2b63cc3ce1e7ab18eadb3694f2cYigit Boyar 18129c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // returns the out-of-bound child view closest to RV's end bounds. An out-of-bound child is 18139c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // defined as a child that's either partially or fully invisible (outside RV's padding area). 18149c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri private View findPartiallyOrCompletelyInvisibleChildClosestToEnd(RecyclerView.Recycler recycler, 18159c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri RecyclerView.State state) { 18169c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return mShouldReverseLayout ? findFirstPartiallyOrCompletelyInvisibleChild(recycler, state) 18179c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri : findLastPartiallyOrCompletelyInvisibleChild(recycler, state); 18189c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 18199c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri 18209c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // returns the out-of-bound child view closest to RV's starting bounds. An out-of-bound child is 18219c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // defined as a child that's either partially or fully invisible (outside RV's padding area). 18229c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri private View findPartiallyOrCompletelyInvisibleChildClosestToStart( 18239c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri RecyclerView.Recycler recycler, RecyclerView.State state) { 18249c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return mShouldReverseLayout ? findLastPartiallyOrCompletelyInvisibleChild(recycler, state) : 18259c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri findFirstPartiallyOrCompletelyInvisibleChild(recycler, state); 18269c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 18279c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri 18289c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri private View findFirstPartiallyOrCompletelyInvisibleChild(RecyclerView.Recycler recycler, 18299c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri RecyclerView.State state) { 18309c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return findOnePartiallyOrCompletelyInvisibleChild(0, getChildCount()); 18319c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 18329c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri 18339c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri private View findLastPartiallyOrCompletelyInvisibleChild(RecyclerView.Recycler recycler, 18349c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri RecyclerView.State state) { 18359c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return findOnePartiallyOrCompletelyInvisibleChild(getChildCount() - 1, -1); 18369c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 18379c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri 1838d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar /** 1839115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar * Returns the adapter position of the first visible view. This position does not include 1840115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar * adapter changes that were dispatched after the last layout pass. 1841d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * <p> 1842d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * Note that, this value is not affected by layout orientation or item order traversal. 1843d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter, 1844d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * not in the layout. 1845d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * <p> 1846d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * If RecyclerView has item decorators, they will be considered in calculations as well. 1847d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * <p> 18485f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * LayoutManager may pre-cache some views that are not necessarily visible. Those views 1849d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * are ignored in this method. 1850d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * 1851d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * @return The adapter position of the first visible item or {@link RecyclerView#NO_POSITION} if 1852d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * there aren't any visible items. 1853d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * @see #findFirstCompletelyVisibleItemPosition() 1854d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * @see #findLastVisibleItemPosition() 1855d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar */ 1856d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public int findFirstVisibleItemPosition() { 18576490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar final View child = findOneVisibleChild(0, getChildCount(), false, true); 1858ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas return child == null ? RecyclerView.NO_POSITION : getPosition(child); 1859d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1860d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1861d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar /** 1862115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar * Returns the adapter position of the first fully visible view. This position does not include 1863115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar * adapter changes that were dispatched after the last layout pass. 1864d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * <p> 1865d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * Note that bounds check is only performed in the current orientation. That means, if 18665f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * LayoutManager is horizontal, it will only check the view's left and right edges. 1867d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * 1868d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * @return The adapter position of the first fully visible item or 1869d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * {@link RecyclerView#NO_POSITION} if there aren't any visible items. 1870d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * @see #findFirstVisibleItemPosition() 1871d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * @see #findLastCompletelyVisibleItemPosition() 1872d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar */ 1873d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public int findFirstCompletelyVisibleItemPosition() { 18746490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar final View child = findOneVisibleChild(0, getChildCount(), true, false); 1875ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas return child == null ? RecyclerView.NO_POSITION : getPosition(child); 1876d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1877d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1878d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar /** 1879115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar * Returns the adapter position of the last visible view. This position does not include 1880115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar * adapter changes that were dispatched after the last layout pass. 1881d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * <p> 1882d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * Note that, this value is not affected by layout orientation or item order traversal. 1883d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter, 1884d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * not in the layout. 1885d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * <p> 1886d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * If RecyclerView has item decorators, they will be considered in calculations as well. 1887d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * <p> 18885f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * LayoutManager may pre-cache some views that are not necessarily visible. Those views 1889d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * are ignored in this method. 1890d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * 1891d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * @return The adapter position of the last visible view or {@link RecyclerView#NO_POSITION} if 1892d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * there aren't any visible items. 1893d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * @see #findLastCompletelyVisibleItemPosition() 1894d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * @see #findFirstVisibleItemPosition() 1895d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar */ 1896d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public int findLastVisibleItemPosition() { 18976490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar final View child = findOneVisibleChild(getChildCount() - 1, -1, false, true); 1898ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas return child == null ? RecyclerView.NO_POSITION : getPosition(child); 1899d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1900d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1901d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar /** 1902115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar * Returns the adapter position of the last fully visible view. This position does not include 1903115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar * adapter changes that were dispatched after the last layout pass. 1904d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * <p> 1905d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * Note that bounds check is only performed in the current orientation. That means, if 19065f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * LayoutManager is horizontal, it will only check the view's left and right edges. 1907d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * 1908d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * @return The adapter position of the last fully visible view or 1909d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * {@link RecyclerView#NO_POSITION} if there aren't any visible items. 1910d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * @see #findLastVisibleItemPosition() 1911d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * @see #findFirstCompletelyVisibleItemPosition() 1912d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar */ 1913d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public int findLastCompletelyVisibleItemPosition() { 19146490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar final View child = findOneVisibleChild(getChildCount() - 1, -1, true, false); 1915ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas return child == null ? RecyclerView.NO_POSITION : getPosition(child); 1916d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1917d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 19189c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // Returns the first child that is visible in the provided index range, i.e. either partially or 19199c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // fully visible depending on the arguments provided. Completely invisible children are not 19209c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // acceptable by this method, but could be returned 19219c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // using #findOnePartiallyOrCompletelyInvisibleChild 19226490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar View findOneVisibleChild(int fromIndex, int toIndex, boolean completelyVisible, 19236490f4b25e7725acd06248f0247fb0c7634afcb1Yigit Boyar boolean acceptPartiallyVisible) { 1924ffff7c903fa7dbf176fd251f77a959e6b9531456Yigit Boyar ensureLayoutState(); 19259c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri @ViewBoundsCheck.ViewBounds int preferredBoundsFlag = 0; 19269c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri @ViewBoundsCheck.ViewBounds int acceptableBoundsFlag = 0; 19279c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri if (completelyVisible) { 19289c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri preferredBoundsFlag = (ViewBoundsCheck.FLAG_CVS_GT_PVS | ViewBoundsCheck.FLAG_CVS_EQ_PVS 19299c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri | ViewBoundsCheck.FLAG_CVE_LT_PVE | ViewBoundsCheck.FLAG_CVE_EQ_PVE); 19309c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } else { 19319c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri preferredBoundsFlag = (ViewBoundsCheck.FLAG_CVS_LT_PVE 19329c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri | ViewBoundsCheck.FLAG_CVE_GT_PVS); 19338f25dd858240e8b86dffc4c1af54307bb462052aKeyvan Amiri } 19349c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri if (acceptPartiallyVisible) { 19359c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri acceptableBoundsFlag = (ViewBoundsCheck.FLAG_CVS_LT_PVE 19369c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri | ViewBoundsCheck.FLAG_CVE_GT_PVS); 19379c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 19389c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return (mOrientation == HORIZONTAL) ? mHorizontalBoundCheck 19399c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri .findOneViewWithinBoundFlags(fromIndex, toIndex, preferredBoundsFlag, 19409c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri acceptableBoundsFlag) : mVerticalBoundCheck 19419c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri .findOneViewWithinBoundFlags(fromIndex, toIndex, preferredBoundsFlag, 19429c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri acceptableBoundsFlag); 19439c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 19449c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri 19459c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri View findOnePartiallyOrCompletelyInvisibleChild(int fromIndex, int toIndex) { 19469c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri ensureLayoutState(); 19479c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri final int next = toIndex > fromIndex ? 1 : (toIndex < fromIndex ? -1 : 0); 19489c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri if (next == 0) { 19499c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return getChildAt(fromIndex); 19509c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 19519c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri @ViewBoundsCheck.ViewBounds int preferredBoundsFlag = 0; 19529c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri @ViewBoundsCheck.ViewBounds int acceptableBoundsFlag = 0; 19539c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri if (mOrientationHelper.getDecoratedStart(getChildAt(fromIndex)) 19549c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri < mOrientationHelper.getStartAfterPadding()) { 19559c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri preferredBoundsFlag = (ViewBoundsCheck.FLAG_CVS_LT_PVS | ViewBoundsCheck.FLAG_CVE_LT_PVE 19569c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri | ViewBoundsCheck.FLAG_CVE_GT_PVS); 19579c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri acceptableBoundsFlag = (ViewBoundsCheck.FLAG_CVS_LT_PVS 19589c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri | ViewBoundsCheck.FLAG_CVE_LT_PVE); 19599c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } else { 19609c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri preferredBoundsFlag = (ViewBoundsCheck.FLAG_CVE_GT_PVE | ViewBoundsCheck.FLAG_CVS_GT_PVS 19619c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri | ViewBoundsCheck.FLAG_CVS_LT_PVE); 19629c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri acceptableBoundsFlag = (ViewBoundsCheck.FLAG_CVE_GT_PVE 19639c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri | ViewBoundsCheck.FLAG_CVS_GT_PVS); 19649c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 19659c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return (mOrientation == HORIZONTAL) ? mHorizontalBoundCheck 19669c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri .findOneViewWithinBoundFlags(fromIndex, toIndex, preferredBoundsFlag, 19679c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri acceptableBoundsFlag) : mVerticalBoundCheck 19689c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri .findOneViewWithinBoundFlags(fromIndex, toIndex, preferredBoundsFlag, 19699c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri acceptableBoundsFlag); 1970d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1971d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 19722aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar @Override 1973d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public View onFocusSearchFailed(View focused, int focusDirection, 19748c1ebc6e59cc5a28de408f4138d3fa8870e8faceYigit Boyar RecyclerView.Recycler recycler, RecyclerView.State state) { 19752aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar resolveShouldLayoutReverse(); 19762aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar if (getChildCount() == 0) { 19772aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar return null; 19782aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar } 19792aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar 19802aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar final int layoutDir = convertFocusDirectionToLayoutDirection(focusDirection); 198194c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar if (layoutDir == LayoutState.INVALID_LAYOUT) { 19822aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar return null; 19832aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar } 1984ffff7c903fa7dbf176fd251f77a959e6b9531456Yigit Boyar ensureLayoutState(); 198594c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar ensureLayoutState(); 1986c032ec5462f6c7c07031310090e23af65841deeeYigit Boyar final int maxScroll = (int) (MAX_SCROLL_FACTOR * mOrientationHelper.getTotalSpace()); 198794c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar updateLayoutState(layoutDir, maxScroll, false, state); 1988c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas mLayoutState.mScrollingOffset = LayoutState.SCROLLING_OFFSET_NaN; 1989c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mLayoutState.mRecycle = false; 199094c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar fill(recycler, mLayoutState, state, true); 19919c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri 19929c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // nextCandidate is the first child view in the layout direction that's partially 19939c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // within RV's bounds, i.e. part of it is visible or it's completely invisible but still 19949c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // touching RV's bounds. This will be the unfocusable candidate view to become visible onto 19959c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // the screen if no focusable views are found in the given layout direction. 19969c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri final View nextCandidate; 19979c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri if (layoutDir == LayoutState.LAYOUT_START) { 19989c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri nextCandidate = findPartiallyOrCompletelyInvisibleChildClosestToStart(recycler, state); 19999c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } else { 20009c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri nextCandidate = findPartiallyOrCompletelyInvisibleChildClosestToEnd(recycler, state); 20019c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 20029c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // nextFocus is meaningful only if it refers to a focusable child, in which case it 20039c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // indicates the next view to gain focus. 20042aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar final View nextFocus; 200594c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar if (layoutDir == LayoutState.LAYOUT_START) { 2006668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar nextFocus = getChildClosestToStart(); 20072aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar } else { 2008668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar nextFocus = getChildClosestToEnd(); 20092aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar } 2010b174744db6a70f384d44caeb43e10854652e81b9Keyvan Amiri if (nextFocus.hasFocusable()) { 20119c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri if (nextCandidate == null) { 20129c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return null; 20139c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 20149c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return nextFocus; 20152aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar } 20169c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return nextCandidate; 20172aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar } 20187dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 20197dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 20207dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Used for debugging. 20217dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Logs the internal representation of children to default logger. 20227dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 20237dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar private void logChildren() { 20247dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar Log.d(TAG, "internal representation of views on the screen"); 20257dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar for (int i = 0; i < getChildCount(); i++) { 20267dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar View child = getChildAt(i); 20277dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar Log.d(TAG, "item " + getPosition(child) + ", coord:" 20287dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar + mOrientationHelper.getDecoratedStart(child)); 20297dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 20307dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar Log.d(TAG, "=============="); 20317dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 20327dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 20337dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 20347dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Used for debugging. 20357dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Validates that child views are laid out in correct order. This is important because rest of 20367dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * the algorithm relies on this constraint. 20377dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * 20387dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * In default layout, child 0 should be closest to screen position 0 and last child should be 20397dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * closest to position WIDTH or HEIGHT. 20407dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * In reverse layout, last child should be closes to screen position 0 and first child should 20417dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * be closest to position WIDTH or HEIGHT 20427dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 2043b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar void validateChildOrder() { 20440447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar Log.d(TAG, "validating child count " + getChildCount()); 20457dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar if (getChildCount() < 1) { 20467dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar return; 20477dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 20487dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar int lastPos = getPosition(getChildAt(0)); 20497dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar int lastScreenLoc = mOrientationHelper.getDecoratedStart(getChildAt(0)); 20507dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar if (mShouldReverseLayout) { 20517dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar for (int i = 1; i < getChildCount(); i++) { 20527dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar View child = getChildAt(i); 20537dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar int pos = getPosition(child); 20547dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar int screenLoc = mOrientationHelper.getDecoratedStart(child); 20557dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar if (pos < lastPos) { 20567dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar logChildren(); 20571e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas throw new RuntimeException("detected invalid position. loc invalid? " 20581e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas + (screenLoc < lastScreenLoc)); 20597dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 20607dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar if (screenLoc > lastScreenLoc) { 20617dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar logChildren(); 20627dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar throw new RuntimeException("detected invalid location"); 20637dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 20647dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 20657dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } else { 20667dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar for (int i = 1; i < getChildCount(); i++) { 20677dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar View child = getChildAt(i); 20687dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar int pos = getPosition(child); 20697dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar int screenLoc = mOrientationHelper.getDecoratedStart(child); 20707dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar if (pos < lastPos) { 20717dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar logChildren(); 20721e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas throw new RuntimeException("detected invalid position. loc invalid? " 20731e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas + (screenLoc < lastScreenLoc)); 20747dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 20757dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar if (screenLoc < lastScreenLoc) { 20767dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar logChildren(); 20777dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar throw new RuntimeException("detected invalid location"); 20787dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 20797dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 20807dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 20817dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 20827dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 2083d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 2084c35968d173f900d8024bdf38174e2225c9a7f311Chet Haase public boolean supportsPredictiveItemAnimations() { 20856e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar return mPendingSavedState == null && mLastStackFromEnd == mStackFromEnd; 2086d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 2087d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 20887dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 2089e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @hide This method should be called by ItemTouchHelper only. 2090e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 20918e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas @RestrictTo(LIBRARY_GROUP) 2092e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 20938a11e6829c522aa1efcc903afa4c01d337082eabChris Craik public void prepareForDrop(@NonNull View view, @NonNull View target, int x, int y) { 2094e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar assertNotInLayoutOrScroll("Cannot drop a view during a scroll or layout calculation"); 2095e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar ensureLayoutState(); 2096e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar resolveShouldLayoutReverse(); 2097e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int myPos = getPosition(view); 2098e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int targetPos = getPosition(target); 20991e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas final int dropDirection = myPos < targetPos ? LayoutState.ITEM_DIRECTION_TAIL 21001e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas : LayoutState.ITEM_DIRECTION_HEAD; 2101e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mShouldReverseLayout) { 2102e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (dropDirection == LayoutState.ITEM_DIRECTION_TAIL) { 2103e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar scrollToPositionWithOffset(targetPos, 21049c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri mOrientationHelper.getEndAfterPadding() 21059c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri - (mOrientationHelper.getDecoratedStart(target) 21069c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri + mOrientationHelper.getDecoratedMeasurement(view))); 2107e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 2108e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar scrollToPositionWithOffset(targetPos, 21091e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas mOrientationHelper.getEndAfterPadding() 21101e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas - mOrientationHelper.getDecoratedEnd(target)); 2111e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2112e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 2113e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (dropDirection == LayoutState.ITEM_DIRECTION_HEAD) { 2114e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar scrollToPositionWithOffset(targetPos, mOrientationHelper.getDecoratedStart(target)); 2115e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 2116e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar scrollToPositionWithOffset(targetPos, 21171e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas mOrientationHelper.getDecoratedEnd(target) 21181e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas - mOrientationHelper.getDecoratedMeasurement(view)); 2119e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2120e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2121e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2122e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2123e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 21247dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Helper class that keeps temporary state while {LayoutManager} is filling out the empty 21257dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * space. 21267dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 2127b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar static class LayoutState { 21287dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 21291e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas static final String TAG = "LLM#LayoutState"; 21307dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 21311e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas static final int LAYOUT_START = -1; 21327dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 21331e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas static final int LAYOUT_END = 1; 21347dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 21351e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas static final int INVALID_LAYOUT = Integer.MIN_VALUE; 21362aad60a0bcc40c5985f8e5e7c6b5c139cde6ceeeYigit Boyar 21371e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas static final int ITEM_DIRECTION_HEAD = -1; 21387dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 21391e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas static final int ITEM_DIRECTION_TAIL = 1; 21407dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 21411e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas static final int SCROLLING_OFFSET_NaN = Integer.MIN_VALUE; 21427dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 21437dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 2144c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar * We may not want to recycle children in some cases (e.g. layout) 2145c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar */ 2146c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar boolean mRecycle = true; 2147c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 2148c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar /** 214994c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar * Pixel offset where layout should start 21507dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 21517dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar int mOffset; 21527dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 21537dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 21547dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Number of pixels that we should fill, in the layout direction. 21557dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 21567dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar int mAvailable; 21577dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 21587dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 21597dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Current position on the adapter to get the next item. 21607dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 21617dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar int mCurrentPosition; 21627dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 21637dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 21647dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Defines the direction in which the data adapter is traversed. 21657dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Should be {@link #ITEM_DIRECTION_HEAD} or {@link #ITEM_DIRECTION_TAIL} 21667dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 21677dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar int mItemDirection; 21687dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 21697dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 21707dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Defines the direction in which the layout is filled. 21717dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Should be {@link #LAYOUT_START} or {@link #LAYOUT_END} 21727dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 21737dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar int mLayoutDirection; 21747dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 21757dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 217694c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar * Used when LayoutState is constructed in a scrolling state. 21777dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * It should be set the amount of scrolling we can make without creating a new view. 21787dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Settings this is required for efficient view recycling. 21797dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 21807dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar int mScrollingOffset; 21817dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 21827dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 21830447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar * Used if you want to pre-layout items that are not yet visible. 218494c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar * The difference with {@link #mAvailable} is that, when recycling, distance laid out for 21850447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar * {@link #mExtra} is not considered to avoid recycling visible children. 21860447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar */ 21870447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar int mExtra = 0; 21880447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar 21890447ba889146f60d6965e6ea66fa4e2cac4d0891Yigit Boyar /** 219018ae336dbaae3db982f38cde08936c9f0243757bYigit Boyar * Equal to {@link RecyclerView.State#isPreLayout()}. When consuming scrap, if this value 219118ae336dbaae3db982f38cde08936c9f0243757bYigit Boyar * is set to true, we skip removed views since they should not be laid out in post layout 219218ae336dbaae3db982f38cde08936c9f0243757bYigit Boyar * step. 219318ae336dbaae3db982f38cde08936c9f0243757bYigit Boyar */ 219418ae336dbaae3db982f38cde08936c9f0243757bYigit Boyar boolean mIsPreLayout = false; 219518ae336dbaae3db982f38cde08936c9f0243757bYigit Boyar 219618ae336dbaae3db982f38cde08936c9f0243757bYigit Boyar /** 21974143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar * The most recent {@link #scrollBy(int, RecyclerView.Recycler, RecyclerView.State)} 21984143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar * amount. 2199baa826219e2ae7c054011cabf992cd7a37fe2a8fGabriel Peal */ 2200baa826219e2ae7c054011cabf992cd7a37fe2a8fGabriel Peal int mLastScrollDelta; 2201baa826219e2ae7c054011cabf992cd7a37fe2a8fGabriel Peal 2202baa826219e2ae7c054011cabf992cd7a37fe2a8fGabriel Peal /** 220394c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar * When LLM needs to layout particular views, it sets this list in which case, LayoutState 2204b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * will only return views from this list and return null if it cannot find an item. 2205b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar */ 2206b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar List<RecyclerView.ViewHolder> mScrapList = null; 2207b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 2208b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar /** 22094143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar * Used when there is no limit in how many views can be laid out. 22104143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar */ 22114143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar boolean mInfinite; 22124143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 22134143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar /** 22147dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * @return true if there are more items in the data adapter 22157dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 2216d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase boolean hasMore(RecyclerView.State state) { 2217d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return mCurrentPosition >= 0 && mCurrentPosition < state.getItemCount(); 22187dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 22197dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 22207dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar /** 222194c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar * Gets the view for the next element that we should layout. 22227dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * Also updates current item index to the next item, based on {@link #mItemDirection} 22237dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar * 222494c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar * @return The next element that we should layout. 22257dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar */ 2226d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase View next(RecyclerView.Recycler recycler) { 2227b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mScrapList != null) { 2228888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar return nextViewFromScrapList(); 2229b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 2230d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase final View view = recycler.getViewForPosition(mCurrentPosition); 22317dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar mCurrentPosition += mItemDirection; 22327dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar return view; 22337dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 22347dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar 2235b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar /** 2236888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar * Returns the next item from the scrap list. 2237b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <p> 2238b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * Upon finding a valid VH, sets current item position to VH.itemPosition + mItemDirection 2239b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * 2240b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * @return View if an item in the current position or direction exists if not null. 2241b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar */ 2242888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar private View nextViewFromScrapList() { 2243888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar final int size = mScrapList.size(); 2244888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar for (int i = 0; i < size; i++) { 2245364ca611da0789aefae36126a8e302abaefa0d48Yigit Boyar final View view = mScrapList.get(i).itemView; 2246ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas final RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams(); 2247364ca611da0789aefae36126a8e302abaefa0d48Yigit Boyar if (lp.isItemRemoved()) { 2248888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar continue; 2249888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar } 2250364ca611da0789aefae36126a8e302abaefa0d48Yigit Boyar if (mCurrentPosition == lp.getViewLayoutPosition()) { 2251364ca611da0789aefae36126a8e302abaefa0d48Yigit Boyar assignPositionFromScrapList(view); 2252364ca611da0789aefae36126a8e302abaefa0d48Yigit Boyar return view; 2253888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar } 2254888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar } 2255888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar return null; 2256888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar } 2257888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar 2258888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar public void assignPositionFromScrapList() { 2259888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar assignPositionFromScrapList(null); 2260888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar } 2261888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar 2262364ca611da0789aefae36126a8e302abaefa0d48Yigit Boyar public void assignPositionFromScrapList(View ignore) { 2263364ca611da0789aefae36126a8e302abaefa0d48Yigit Boyar final View closest = nextViewInLimitedList(ignore); 2264364ca611da0789aefae36126a8e302abaefa0d48Yigit Boyar if (closest == null) { 2265ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas mCurrentPosition = RecyclerView.NO_POSITION; 2266364ca611da0789aefae36126a8e302abaefa0d48Yigit Boyar } else { 2267ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas mCurrentPosition = ((RecyclerView.LayoutParams) closest.getLayoutParams()) 2268364ca611da0789aefae36126a8e302abaefa0d48Yigit Boyar .getViewLayoutPosition(); 2269364ca611da0789aefae36126a8e302abaefa0d48Yigit Boyar } 2270888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar } 2271888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar 2272364ca611da0789aefae36126a8e302abaefa0d48Yigit Boyar public View nextViewInLimitedList(View ignore) { 2273b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar int size = mScrapList.size(); 2274364ca611da0789aefae36126a8e302abaefa0d48Yigit Boyar View closest = null; 2275b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar int closestDistance = Integer.MAX_VALUE; 2276888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar if (DEBUG && mIsPreLayout) { 2277888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar throw new IllegalStateException("Scrap list cannot be used in pre layout"); 2278888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar } 2279b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar for (int i = 0; i < size; i++) { 2280364ca611da0789aefae36126a8e302abaefa0d48Yigit Boyar View view = mScrapList.get(i).itemView; 2281ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas final RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams(); 2282364ca611da0789aefae36126a8e302abaefa0d48Yigit Boyar if (view == ignore || lp.isItemRemoved()) { 228318ae336dbaae3db982f38cde08936c9f0243757bYigit Boyar continue; 228418ae336dbaae3db982f38cde08936c9f0243757bYigit Boyar } 22851e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas final int distance = (lp.getViewLayoutPosition() - mCurrentPosition) 22861e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas * mItemDirection; 2287b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (distance < 0) { 2288b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar continue; // item is not in current direction 2289b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 2290b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (distance < closestDistance) { 2291364ca611da0789aefae36126a8e302abaefa0d48Yigit Boyar closest = view; 2292b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar closestDistance = distance; 2293b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (distance == 0) { 2294b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar break; 2295b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 2296b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 2297b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 2298888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar return closest; 2299b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 2300b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 23017dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar void log() { 23021e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas Log.d(TAG, "avail:" + mAvailable + ", ind:" + mCurrentPosition + ", dir:" 23031e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas + mItemDirection + ", offset:" + mOffset + ", layoutDir:" + mLayoutDirection); 23047dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 23057dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar } 230608cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar 2307ceb1d0ab9e22f5a48b72e9850f713be60311c516Yigit Boyar /** 2308ceb1d0ab9e22f5a48b72e9850f713be60311c516Yigit Boyar * @hide 2309ceb1d0ab9e22f5a48b72e9850f713be60311c516Yigit Boyar */ 23108e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas @RestrictTo(LIBRARY_GROUP) 2311ceb1d0ab9e22f5a48b72e9850f713be60311c516Yigit Boyar public static class SavedState implements Parcelable { 231208cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar 231308cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar int mAnchorPosition; 231408cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar 231508cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar int mAnchorOffset; 231608cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar 231708cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar boolean mAnchorLayoutFromEnd; 231808cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar 231908cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar public SavedState() { 232008cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar 232108cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar } 232208cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar 232308cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar SavedState(Parcel in) { 232408cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar mAnchorPosition = in.readInt(); 232508cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar mAnchorOffset = in.readInt(); 232608cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar mAnchorLayoutFromEnd = in.readInt() == 1; 232708cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar } 232808cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar 232908cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar public SavedState(SavedState other) { 233008cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar mAnchorPosition = other.mAnchorPosition; 233108cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar mAnchorOffset = other.mAnchorOffset; 233208cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar mAnchorLayoutFromEnd = other.mAnchorLayoutFromEnd; 233308cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar } 233408cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar 233575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar boolean hasValidAnchor() { 233675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar return mAnchorPosition >= 0; 233775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 233875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 233975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar void invalidateAnchor() { 2340ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas mAnchorPosition = RecyclerView.NO_POSITION; 234175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 234275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 234308cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar @Override 234408cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar public int describeContents() { 234508cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar return 0; 234608cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar } 234708cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar 234808cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar @Override 234908cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar public void writeToParcel(Parcel dest, int flags) { 235008cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar dest.writeInt(mAnchorPosition); 235108cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar dest.writeInt(mAnchorOffset); 235208cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar dest.writeInt(mAnchorLayoutFromEnd ? 1 : 0); 235308cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar } 235408cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar 23551e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas public static final Parcelable.Creator<SavedState> CREATOR = 23561e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas new Parcelable.Creator<SavedState>() { 23571e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas @Override 23581e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas public SavedState createFromParcel(Parcel in) { 23591e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas return new SavedState(in); 23601e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas } 236108cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar 23621e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas @Override 23631e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas public SavedState[] newArray(int size) { 23641e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas return new SavedState[size]; 23651e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas } 23661e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas }; 236708cbcdeb4283048c097397d042d5ae0d2b8683d3Yigit Boyar } 2368310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 2369310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar /** 2370310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar * Simple data class to keep Anchor information 2371310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar */ 2372a0e32c54b808887323ba05392fe4e340c9ad6c0bshepshapard static class AnchorInfo { 2373a0e32c54b808887323ba05392fe4e340c9ad6c0bshepshapard OrientationHelper mOrientationHelper; 2374310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar int mPosition; 2375310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar int mCoordinate; 2376310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar boolean mLayoutFromEnd; 23779aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar boolean mValid; 23789aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar 23799aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar AnchorInfo() { 23809aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar reset(); 23819aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar } 23829aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar 2383310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar void reset() { 2384ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas mPosition = RecyclerView.NO_POSITION; 2385310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mCoordinate = INVALID_OFFSET; 2386310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutFromEnd = false; 23879aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar mValid = false; 2388310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 2389310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 2390310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar /** 2391310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar * assigns anchor coordinate from the RecyclerView's padding depending on current 2392310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar * layoutFromEnd value 2393310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar */ 2394310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar void assignCoordinateFromPadding() { 2395310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mCoordinate = mLayoutFromEnd 2396310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar ? mOrientationHelper.getEndAfterPadding() 2397310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar : mOrientationHelper.getStartAfterPadding(); 2398310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 2399310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 2400310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar @Override 2401310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar public String toString() { 24021e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas return "AnchorInfo{" 24031e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas + "mPosition=" + mPosition 24041e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas + ", mCoordinate=" + mCoordinate 24051e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas + ", mLayoutFromEnd=" + mLayoutFromEnd 24061e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas + ", mValid=" + mValid 24071e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas + '}'; 2408310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 2409310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 24103a500f61a8bdf48904f380f2d4925fe420d18ce7Aurimas Liutikas boolean isViewValidAsAnchor(View child, RecyclerView.State state) { 2411ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams(); 2412542f1260934df280985294eaef1ec8469863281fYigit Boyar return !lp.isItemRemoved() && lp.getViewLayoutPosition() >= 0 2413542f1260934df280985294eaef1ec8469863281fYigit Boyar && lp.getViewLayoutPosition() < state.getItemCount(); 2414542f1260934df280985294eaef1ec8469863281fYigit Boyar } 2415542f1260934df280985294eaef1ec8469863281fYigit Boyar 2416a0e32c54b808887323ba05392fe4e340c9ad6c0bshepshapard public void assignFromViewAndKeepVisibleRect(View child, int position) { 2417542f1260934df280985294eaef1ec8469863281fYigit Boyar final int spaceChange = mOrientationHelper.getTotalSpaceChange(); 2418542f1260934df280985294eaef1ec8469863281fYigit Boyar if (spaceChange >= 0) { 2419a0e32c54b808887323ba05392fe4e340c9ad6c0bshepshapard assignFromView(child, position); 2420542f1260934df280985294eaef1ec8469863281fYigit Boyar return; 2421542f1260934df280985294eaef1ec8469863281fYigit Boyar } 2422a0e32c54b808887323ba05392fe4e340c9ad6c0bshepshapard mPosition = position; 2423542f1260934df280985294eaef1ec8469863281fYigit Boyar if (mLayoutFromEnd) { 2424542f1260934df280985294eaef1ec8469863281fYigit Boyar final int prevLayoutEnd = mOrientationHelper.getEndAfterPadding() - spaceChange; 2425542f1260934df280985294eaef1ec8469863281fYigit Boyar final int childEnd = mOrientationHelper.getDecoratedEnd(child); 2426542f1260934df280985294eaef1ec8469863281fYigit Boyar final int previousEndMargin = prevLayoutEnd - childEnd; 2427542f1260934df280985294eaef1ec8469863281fYigit Boyar mCoordinate = mOrientationHelper.getEndAfterPadding() - previousEndMargin; 2428542f1260934df280985294eaef1ec8469863281fYigit Boyar // ensure we did not push child's top out of bounds because of this 24291e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas if (previousEndMargin > 0) { // we have room to shift bottom if necessary 2430542f1260934df280985294eaef1ec8469863281fYigit Boyar final int childSize = mOrientationHelper.getDecoratedMeasurement(child); 2431542f1260934df280985294eaef1ec8469863281fYigit Boyar final int estimatedChildStart = mCoordinate - childSize; 2432542f1260934df280985294eaef1ec8469863281fYigit Boyar final int layoutStart = mOrientationHelper.getStartAfterPadding(); 24331e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas final int previousStartMargin = mOrientationHelper.getDecoratedStart(child) 24341e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas - layoutStart; 2435542f1260934df280985294eaef1ec8469863281fYigit Boyar final int startReference = layoutStart + Math.min(previousStartMargin, 0); 2436542f1260934df280985294eaef1ec8469863281fYigit Boyar final int startMargin = estimatedChildStart - startReference; 2437542f1260934df280985294eaef1ec8469863281fYigit Boyar if (startMargin < 0) { 2438542f1260934df280985294eaef1ec8469863281fYigit Boyar // offset to make top visible but not too much 2439542f1260934df280985294eaef1ec8469863281fYigit Boyar mCoordinate += Math.min(previousEndMargin, -startMargin); 2440542f1260934df280985294eaef1ec8469863281fYigit Boyar } 2441542f1260934df280985294eaef1ec8469863281fYigit Boyar } 2442542f1260934df280985294eaef1ec8469863281fYigit Boyar } else { 2443542f1260934df280985294eaef1ec8469863281fYigit Boyar final int childStart = mOrientationHelper.getDecoratedStart(child); 2444542f1260934df280985294eaef1ec8469863281fYigit Boyar final int startMargin = childStart - mOrientationHelper.getStartAfterPadding(); 2445542f1260934df280985294eaef1ec8469863281fYigit Boyar mCoordinate = childStart; 2446542f1260934df280985294eaef1ec8469863281fYigit Boyar if (startMargin > 0) { // we have room to fix end as well 24471e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas final int estimatedEnd = childStart 24481e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas + mOrientationHelper.getDecoratedMeasurement(child); 24491e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas final int previousLayoutEnd = mOrientationHelper.getEndAfterPadding() 24501e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas - spaceChange; 24511e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas final int previousEndMargin = previousLayoutEnd 24521e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas - mOrientationHelper.getDecoratedEnd(child); 24531e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas final int endReference = mOrientationHelper.getEndAfterPadding() 24541e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas - Math.min(0, previousEndMargin); 2455542f1260934df280985294eaef1ec8469863281fYigit Boyar final int endMargin = endReference - estimatedEnd; 2456542f1260934df280985294eaef1ec8469863281fYigit Boyar if (endMargin < 0) { 2457542f1260934df280985294eaef1ec8469863281fYigit Boyar mCoordinate -= Math.min(startMargin, -endMargin); 2458542f1260934df280985294eaef1ec8469863281fYigit Boyar } 2459542f1260934df280985294eaef1ec8469863281fYigit Boyar } 2460542f1260934df280985294eaef1ec8469863281fYigit Boyar } 2461542f1260934df280985294eaef1ec8469863281fYigit Boyar } 2462542f1260934df280985294eaef1ec8469863281fYigit Boyar 2463a0e32c54b808887323ba05392fe4e340c9ad6c0bshepshapard public void assignFromView(View child, int position) { 2464310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (mLayoutFromEnd) { 24651e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas mCoordinate = mOrientationHelper.getDecoratedEnd(child) 24661e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas + mOrientationHelper.getTotalSpaceChange(); 2467310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } else { 2468310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mCoordinate = mOrientationHelper.getDecoratedStart(child); 2469310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 2470310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 2471a0e32c54b808887323ba05392fe4e340c9ad6c0bshepshapard mPosition = position; 2472310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 2473310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 2474b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 2475b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar protected static class LayoutChunkResult { 2476b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public int mConsumed; 2477b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public boolean mFinished; 2478b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public boolean mIgnoreConsumed; 2479b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public boolean mFocusable; 2480b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 2481b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar void resetInternal() { 2482b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar mConsumed = 0; 2483b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar mFinished = false; 2484b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar mIgnoreConsumed = false; 2485b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar mFocusable = false; 2486b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 2487b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 24887dad56243ebcde65d75d592dc802269a4d86c875Yigit Boyar} 2489