1c587f7dba5a337169e854e235da59f595255d6ccAga Madurska/* 2ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * Copyright 2018 The Android Open Source Project 3c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * 4c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * Licensed under the Apache License, Version 2.0 (the "License"); 5c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * you may not use this file except in compliance with the License. 6c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * You may obtain a copy of the License at 7c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * 8c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * http://www.apache.org/licenses/LICENSE-2.0 9c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * 10c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * Unless required by applicable law or agreed to in writing, software 11c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * distributed under the License is distributed on an "AS IS" BASIS, 12c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas * See the License for the specific language governing permissions and 14c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * limitations under the License. 15c587f7dba5a337169e854e235da59f595255d6ccAga Madurska */ 16c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.recyclerview.widget; 18c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 19c587f7dba5a337169e854e235da59f595255d6ccAga Madurskaimport android.graphics.PointF; 20c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikasimport android.view.View; 21c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikas 22ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.NonNull; 23ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.Nullable; 24c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 25c587f7dba5a337169e854e235da59f595255d6ccAga Madurska/** 26c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * Implementation of the {@link SnapHelper} supporting snapping in either vertical or horizontal 27c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * orientation. 28c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * <p> 29c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * The implementation will snap the center of the target child view to the center of 30c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * the attached {@link RecyclerView}. If you intend to change this behavior then override 31c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * {@link SnapHelper#calculateDistanceToFinalSnap}. 32c587f7dba5a337169e854e235da59f595255d6ccAga Madurska */ 33c587f7dba5a337169e854e235da59f595255d6ccAga Madurskapublic class LinearSnapHelper extends SnapHelper { 34c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 35c587f7dba5a337169e854e235da59f595255d6ccAga Madurska private static final float INVALID_DISTANCE = 1f; 36c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 37c587f7dba5a337169e854e235da59f595255d6ccAga Madurska // Orientation helpers are lazily created per LayoutManager. 38c587f7dba5a337169e854e235da59f595255d6ccAga Madurska @Nullable 39c587f7dba5a337169e854e235da59f595255d6ccAga Madurska private OrientationHelper mVerticalHelper; 40c587f7dba5a337169e854e235da59f595255d6ccAga Madurska @Nullable 41c587f7dba5a337169e854e235da59f595255d6ccAga Madurska private OrientationHelper mHorizontalHelper; 42c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 43c587f7dba5a337169e854e235da59f595255d6ccAga Madurska @Override 44c587f7dba5a337169e854e235da59f595255d6ccAga Madurska public int[] calculateDistanceToFinalSnap( 45c587f7dba5a337169e854e235da59f595255d6ccAga Madurska @NonNull RecyclerView.LayoutManager layoutManager, @NonNull View targetView) { 46c587f7dba5a337169e854e235da59f595255d6ccAga Madurska int[] out = new int[2]; 47c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (layoutManager.canScrollHorizontally()) { 48c587f7dba5a337169e854e235da59f595255d6ccAga Madurska out[0] = distanceToCenter(layoutManager, targetView, 49c587f7dba5a337169e854e235da59f595255d6ccAga Madurska getHorizontalHelper(layoutManager)); 50c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } else { 51c587f7dba5a337169e854e235da59f595255d6ccAga Madurska out[0] = 0; 52c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 53c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 54c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (layoutManager.canScrollVertically()) { 55c587f7dba5a337169e854e235da59f595255d6ccAga Madurska out[1] = distanceToCenter(layoutManager, targetView, 56c587f7dba5a337169e854e235da59f595255d6ccAga Madurska getVerticalHelper(layoutManager)); 57c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } else { 58c587f7dba5a337169e854e235da59f595255d6ccAga Madurska out[1] = 0; 59c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 60c587f7dba5a337169e854e235da59f595255d6ccAga Madurska return out; 61c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 62c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 63c587f7dba5a337169e854e235da59f595255d6ccAga Madurska @Override 64c587f7dba5a337169e854e235da59f595255d6ccAga Madurska public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX, 65c587f7dba5a337169e854e235da59f595255d6ccAga Madurska int velocityY) { 66c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (!(layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) { 67c587f7dba5a337169e854e235da59f595255d6ccAga Madurska return RecyclerView.NO_POSITION; 68c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 69c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 70c587f7dba5a337169e854e235da59f595255d6ccAga Madurska final int itemCount = layoutManager.getItemCount(); 71c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (itemCount == 0) { 72c587f7dba5a337169e854e235da59f595255d6ccAga Madurska return RecyclerView.NO_POSITION; 73c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 74c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 75c587f7dba5a337169e854e235da59f595255d6ccAga Madurska final View currentView = findSnapView(layoutManager); 76c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (currentView == null) { 77c587f7dba5a337169e854e235da59f595255d6ccAga Madurska return RecyclerView.NO_POSITION; 78c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 79c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 80c587f7dba5a337169e854e235da59f595255d6ccAga Madurska final int currentPosition = layoutManager.getPosition(currentView); 81c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (currentPosition == RecyclerView.NO_POSITION) { 82c587f7dba5a337169e854e235da59f595255d6ccAga Madurska return RecyclerView.NO_POSITION; 83c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 84c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 85c587f7dba5a337169e854e235da59f595255d6ccAga Madurska RecyclerView.SmoothScroller.ScrollVectorProvider vectorProvider = 86c587f7dba5a337169e854e235da59f595255d6ccAga Madurska (RecyclerView.SmoothScroller.ScrollVectorProvider) layoutManager; 87c587f7dba5a337169e854e235da59f595255d6ccAga Madurska // deltaJumps sign comes from the velocity which may not match the order of children in 88c587f7dba5a337169e854e235da59f595255d6ccAga Madurska // the LayoutManager. To overcome this, we ask for a vector from the LayoutManager to 89c587f7dba5a337169e854e235da59f595255d6ccAga Madurska // get the direction. 90c587f7dba5a337169e854e235da59f595255d6ccAga Madurska PointF vectorForEnd = vectorProvider.computeScrollVectorForPosition(itemCount - 1); 91c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (vectorForEnd == null) { 92c587f7dba5a337169e854e235da59f595255d6ccAga Madurska // cannot get a vector for the given position. 93c587f7dba5a337169e854e235da59f595255d6ccAga Madurska return RecyclerView.NO_POSITION; 94c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 95c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 96c587f7dba5a337169e854e235da59f595255d6ccAga Madurska int vDeltaJump, hDeltaJump; 97c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (layoutManager.canScrollHorizontally()) { 98c587f7dba5a337169e854e235da59f595255d6ccAga Madurska hDeltaJump = estimateNextPositionDiffForFling(layoutManager, 99c587f7dba5a337169e854e235da59f595255d6ccAga Madurska getHorizontalHelper(layoutManager), velocityX, 0); 100c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (vectorForEnd.x < 0) { 101c587f7dba5a337169e854e235da59f595255d6ccAga Madurska hDeltaJump = -hDeltaJump; 102c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 103c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } else { 104c587f7dba5a337169e854e235da59f595255d6ccAga Madurska hDeltaJump = 0; 105c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 106c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (layoutManager.canScrollVertically()) { 107c587f7dba5a337169e854e235da59f595255d6ccAga Madurska vDeltaJump = estimateNextPositionDiffForFling(layoutManager, 108c587f7dba5a337169e854e235da59f595255d6ccAga Madurska getVerticalHelper(layoutManager), 0, velocityY); 109c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (vectorForEnd.y < 0) { 110c587f7dba5a337169e854e235da59f595255d6ccAga Madurska vDeltaJump = -vDeltaJump; 111c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 112c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } else { 113c587f7dba5a337169e854e235da59f595255d6ccAga Madurska vDeltaJump = 0; 114c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 115c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 116c587f7dba5a337169e854e235da59f595255d6ccAga Madurska int deltaJump = layoutManager.canScrollVertically() ? vDeltaJump : hDeltaJump; 117c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (deltaJump == 0) { 118c587f7dba5a337169e854e235da59f595255d6ccAga Madurska return RecyclerView.NO_POSITION; 119c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 120c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 121c587f7dba5a337169e854e235da59f595255d6ccAga Madurska int targetPos = currentPosition + deltaJump; 122c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (targetPos < 0) { 123c587f7dba5a337169e854e235da59f595255d6ccAga Madurska targetPos = 0; 124c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 125c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (targetPos >= itemCount) { 126c587f7dba5a337169e854e235da59f595255d6ccAga Madurska targetPos = itemCount - 1; 127c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 128c587f7dba5a337169e854e235da59f595255d6ccAga Madurska return targetPos; 129c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 130c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 131c587f7dba5a337169e854e235da59f595255d6ccAga Madurska @Override 132c587f7dba5a337169e854e235da59f595255d6ccAga Madurska public View findSnapView(RecyclerView.LayoutManager layoutManager) { 133c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (layoutManager.canScrollVertically()) { 134c587f7dba5a337169e854e235da59f595255d6ccAga Madurska return findCenterView(layoutManager, getVerticalHelper(layoutManager)); 135c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } else if (layoutManager.canScrollHorizontally()) { 136c587f7dba5a337169e854e235da59f595255d6ccAga Madurska return findCenterView(layoutManager, getHorizontalHelper(layoutManager)); 137c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 138c587f7dba5a337169e854e235da59f595255d6ccAga Madurska return null; 139c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 140c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 141c587f7dba5a337169e854e235da59f595255d6ccAga Madurska private int distanceToCenter(@NonNull RecyclerView.LayoutManager layoutManager, 142c587f7dba5a337169e854e235da59f595255d6ccAga Madurska @NonNull View targetView, OrientationHelper helper) { 1431e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas final int childCenter = helper.getDecoratedStart(targetView) 1441e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas + (helper.getDecoratedMeasurement(targetView) / 2); 145c587f7dba5a337169e854e235da59f595255d6ccAga Madurska final int containerCenter; 146c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (layoutManager.getClipToPadding()) { 147c587f7dba5a337169e854e235da59f595255d6ccAga Madurska containerCenter = helper.getStartAfterPadding() + helper.getTotalSpace() / 2; 148c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } else { 149c587f7dba5a337169e854e235da59f595255d6ccAga Madurska containerCenter = helper.getEnd() / 2; 150c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 151c587f7dba5a337169e854e235da59f595255d6ccAga Madurska return childCenter - containerCenter; 152c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 153c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 154c587f7dba5a337169e854e235da59f595255d6ccAga Madurska /** 155c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * Estimates a position to which SnapHelper will try to scroll to in response to a fling. 156c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * 157c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * @param layoutManager The {@link RecyclerView.LayoutManager} associated with the attached 158c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * {@link RecyclerView}. 159c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * @param helper The {@link OrientationHelper} that is created from the LayoutManager. 160c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * @param velocityX The velocity on the x axis. 161c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * @param velocityY The velocity on the y axis. 162c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * 163c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * @return The diff between the target scroll position and the current position. 164c587f7dba5a337169e854e235da59f595255d6ccAga Madurska */ 165c587f7dba5a337169e854e235da59f595255d6ccAga Madurska private int estimateNextPositionDiffForFling(RecyclerView.LayoutManager layoutManager, 166c587f7dba5a337169e854e235da59f595255d6ccAga Madurska OrientationHelper helper, int velocityX, int velocityY) { 167c587f7dba5a337169e854e235da59f595255d6ccAga Madurska int[] distances = calculateScrollDistance(velocityX, velocityY); 168c587f7dba5a337169e854e235da59f595255d6ccAga Madurska float distancePerChild = computeDistancePerChild(layoutManager, helper); 169c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (distancePerChild <= 0) { 170c587f7dba5a337169e854e235da59f595255d6ccAga Madurska return 0; 171c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 172c587f7dba5a337169e854e235da59f595255d6ccAga Madurska int distance = 173c587f7dba5a337169e854e235da59f595255d6ccAga Madurska Math.abs(distances[0]) > Math.abs(distances[1]) ? distances[0] : distances[1]; 17467c3576b3a045b47febab2242890f1953dedfbbbGus Prevas return (int) Math.round(distance / distancePerChild); 175c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 176c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 177c587f7dba5a337169e854e235da59f595255d6ccAga Madurska /** 178c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * Return the child view that is currently closest to the center of this parent. 179c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * 180c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * @param layoutManager The {@link RecyclerView.LayoutManager} associated with the attached 181c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * {@link RecyclerView}. 182c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * @param helper The relevant {@link OrientationHelper} for the attached {@link RecyclerView}. 183c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * 184c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * @return the child view that is currently closest to the center of this parent. 185c587f7dba5a337169e854e235da59f595255d6ccAga Madurska */ 186c587f7dba5a337169e854e235da59f595255d6ccAga Madurska @Nullable 187c587f7dba5a337169e854e235da59f595255d6ccAga Madurska private View findCenterView(RecyclerView.LayoutManager layoutManager, 188c587f7dba5a337169e854e235da59f595255d6ccAga Madurska OrientationHelper helper) { 189c587f7dba5a337169e854e235da59f595255d6ccAga Madurska int childCount = layoutManager.getChildCount(); 190c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (childCount == 0) { 191c587f7dba5a337169e854e235da59f595255d6ccAga Madurska return null; 192c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 193c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 194c587f7dba5a337169e854e235da59f595255d6ccAga Madurska View closestChild = null; 195c587f7dba5a337169e854e235da59f595255d6ccAga Madurska final int center; 196c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (layoutManager.getClipToPadding()) { 197c587f7dba5a337169e854e235da59f595255d6ccAga Madurska center = helper.getStartAfterPadding() + helper.getTotalSpace() / 2; 198c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } else { 199c587f7dba5a337169e854e235da59f595255d6ccAga Madurska center = helper.getEnd() / 2; 200c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 201c587f7dba5a337169e854e235da59f595255d6ccAga Madurska int absClosest = Integer.MAX_VALUE; 202c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 203c587f7dba5a337169e854e235da59f595255d6ccAga Madurska for (int i = 0; i < childCount; i++) { 204c587f7dba5a337169e854e235da59f595255d6ccAga Madurska final View child = layoutManager.getChildAt(i); 2051e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas int childCenter = helper.getDecoratedStart(child) 2061e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas + (helper.getDecoratedMeasurement(child) / 2); 207c587f7dba5a337169e854e235da59f595255d6ccAga Madurska int absDistance = Math.abs(childCenter - center); 208c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 209c587f7dba5a337169e854e235da59f595255d6ccAga Madurska /** if child center is closer than previous closest, set it as closest **/ 210c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (absDistance < absClosest) { 211c587f7dba5a337169e854e235da59f595255d6ccAga Madurska absClosest = absDistance; 212c587f7dba5a337169e854e235da59f595255d6ccAga Madurska closestChild = child; 213c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 214c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 215c587f7dba5a337169e854e235da59f595255d6ccAga Madurska return closestChild; 216c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 217c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 218c587f7dba5a337169e854e235da59f595255d6ccAga Madurska /** 219c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * Computes an average pixel value to pass a single child. 220c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * <p> 221c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * Returns a negative value if it cannot be calculated. 222c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * 223c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * @param layoutManager The {@link RecyclerView.LayoutManager} associated with the attached 224c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * {@link RecyclerView}. 225c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * @param helper The relevant {@link OrientationHelper} for the attached 226c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * {@link RecyclerView.LayoutManager}. 227c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * 228c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * @return A float value that is the average number of pixels needed to scroll by one view in 229c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * the relevant direction. 230c587f7dba5a337169e854e235da59f595255d6ccAga Madurska */ 231c587f7dba5a337169e854e235da59f595255d6ccAga Madurska private float computeDistancePerChild(RecyclerView.LayoutManager layoutManager, 232c587f7dba5a337169e854e235da59f595255d6ccAga Madurska OrientationHelper helper) { 233c587f7dba5a337169e854e235da59f595255d6ccAga Madurska View minPosView = null; 234c587f7dba5a337169e854e235da59f595255d6ccAga Madurska View maxPosView = null; 235c587f7dba5a337169e854e235da59f595255d6ccAga Madurska int minPos = Integer.MAX_VALUE; 236c587f7dba5a337169e854e235da59f595255d6ccAga Madurska int maxPos = Integer.MIN_VALUE; 237c587f7dba5a337169e854e235da59f595255d6ccAga Madurska int childCount = layoutManager.getChildCount(); 238c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (childCount == 0) { 239c587f7dba5a337169e854e235da59f595255d6ccAga Madurska return INVALID_DISTANCE; 240c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 241c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 242c587f7dba5a337169e854e235da59f595255d6ccAga Madurska for (int i = 0; i < childCount; i++) { 243c587f7dba5a337169e854e235da59f595255d6ccAga Madurska View child = layoutManager.getChildAt(i); 244c587f7dba5a337169e854e235da59f595255d6ccAga Madurska final int pos = layoutManager.getPosition(child); 245c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (pos == RecyclerView.NO_POSITION) { 246c587f7dba5a337169e854e235da59f595255d6ccAga Madurska continue; 247c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 248c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (pos < minPos) { 249c587f7dba5a337169e854e235da59f595255d6ccAga Madurska minPos = pos; 250c587f7dba5a337169e854e235da59f595255d6ccAga Madurska minPosView = child; 251c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 252c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (pos > maxPos) { 253c587f7dba5a337169e854e235da59f595255d6ccAga Madurska maxPos = pos; 254c587f7dba5a337169e854e235da59f595255d6ccAga Madurska maxPosView = child; 255c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 256c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 257c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (minPosView == null || maxPosView == null) { 258c587f7dba5a337169e854e235da59f595255d6ccAga Madurska return INVALID_DISTANCE; 259c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 260c587f7dba5a337169e854e235da59f595255d6ccAga Madurska int start = Math.min(helper.getDecoratedStart(minPosView), 261c587f7dba5a337169e854e235da59f595255d6ccAga Madurska helper.getDecoratedStart(maxPosView)); 262c587f7dba5a337169e854e235da59f595255d6ccAga Madurska int end = Math.max(helper.getDecoratedEnd(minPosView), 263c587f7dba5a337169e854e235da59f595255d6ccAga Madurska helper.getDecoratedEnd(maxPosView)); 264c587f7dba5a337169e854e235da59f595255d6ccAga Madurska int distance = end - start; 265c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (distance == 0) { 266c587f7dba5a337169e854e235da59f595255d6ccAga Madurska return INVALID_DISTANCE; 267c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 268c587f7dba5a337169e854e235da59f595255d6ccAga Madurska return 1f * distance / ((maxPos - minPos) + 1); 269c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 270c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 271c587f7dba5a337169e854e235da59f595255d6ccAga Madurska @NonNull 272c587f7dba5a337169e854e235da59f595255d6ccAga Madurska private OrientationHelper getVerticalHelper(@NonNull RecyclerView.LayoutManager layoutManager) { 273c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (mVerticalHelper == null || mVerticalHelper.mLayoutManager != layoutManager) { 274c587f7dba5a337169e854e235da59f595255d6ccAga Madurska mVerticalHelper = OrientationHelper.createVerticalHelper(layoutManager); 275c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 276c587f7dba5a337169e854e235da59f595255d6ccAga Madurska return mVerticalHelper; 277c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 278c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 279c587f7dba5a337169e854e235da59f595255d6ccAga Madurska @NonNull 280c587f7dba5a337169e854e235da59f595255d6ccAga Madurska private OrientationHelper getHorizontalHelper( 281c587f7dba5a337169e854e235da59f595255d6ccAga Madurska @NonNull RecyclerView.LayoutManager layoutManager) { 282c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (mHorizontalHelper == null || mHorizontalHelper.mLayoutManager != layoutManager) { 283c587f7dba5a337169e854e235da59f595255d6ccAga Madurska mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager); 284c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 285c587f7dba5a337169e854e235da59f595255d6ccAga Madurska return mHorizontalHelper; 286c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 287c587f7dba5a337169e854e235da59f595255d6ccAga Madurska} 288