1849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas/* 2ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * Copyright 2018 The Android Open Source Project 3849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * 4849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * Licensed under the Apache License, Version 2.0 (the "License"); 5849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * you may not use this file except in compliance with the License. 6849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * You may obtain a copy of the License at 7849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * 8849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * http://www.apache.org/licenses/LICENSE-2.0 9849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * 10849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * Unless required by applicable law or agreed to in writing, software 11849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * distributed under the License is distributed on an "AS IS" BASIS, 12849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * See the License for the specific language governing permissions and 14849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * limitations under the License. 15849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas */ 16849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas 17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.recyclerview.widget; 18849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas 19849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikasimport android.graphics.PointF; 20396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikasimport android.util.DisplayMetrics; 21849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikasimport android.view.View; 22849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas 23c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikasimport androidx.annotation.NonNull; 24c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikasimport androidx.annotation.Nullable; 25ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.viewpager.widget.ViewPager; 26ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas 27849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas/** 28849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * Implementation of the {@link SnapHelper} supporting pager style snapping in either vertical or 29849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * horizontal orientation. 30396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas * 31396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas * <p> 32396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas * 33ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * PagerSnapHelper can help achieve a similar behavior to {@link ViewPager}. 344e05ba710eb6a2aea1e5f91eecb730e1931b3f40Aurimas Liutikas * Set both {@link RecyclerView} and the items of the 35ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * {@link RecyclerView.Adapter} to have 364e05ba710eb6a2aea1e5f91eecb730e1931b3f40Aurimas Liutikas * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT} height and width and then attach 374e05ba710eb6a2aea1e5f91eecb730e1931b3f40Aurimas Liutikas * PagerSnapHelper to the {@link RecyclerView} using {@link #attachToRecyclerView(RecyclerView)}. 38849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas */ 39849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikaspublic class PagerSnapHelper extends SnapHelper { 40396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas private static final int MAX_SCROLL_ON_FLING_DURATION = 100; // ms 41396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas 42849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas // Orientation helpers are lazily created per LayoutManager. 43849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas @Nullable 44849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas private OrientationHelper mVerticalHelper; 45849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas @Nullable 46849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas private OrientationHelper mHorizontalHelper; 47849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas 48849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas @Nullable 49849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas @Override 50849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager, 51849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas @NonNull View targetView) { 52849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas int[] out = new int[2]; 53849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas if (layoutManager.canScrollHorizontally()) { 54849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas out[0] = distanceToCenter(layoutManager, targetView, 55849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas getHorizontalHelper(layoutManager)); 56849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } else { 57849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas out[0] = 0; 58849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 59849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas 60849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas if (layoutManager.canScrollVertically()) { 61849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas out[1] = distanceToCenter(layoutManager, targetView, 62849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas getVerticalHelper(layoutManager)); 63849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } else { 64849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas out[1] = 0; 65849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 66849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas return out; 67849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 68849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas 69849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas @Nullable 70849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas @Override 71849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas public View findSnapView(RecyclerView.LayoutManager layoutManager) { 72849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas if (layoutManager.canScrollVertically()) { 73849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas return findCenterView(layoutManager, getVerticalHelper(layoutManager)); 74849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } else if (layoutManager.canScrollHorizontally()) { 75849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas return findCenterView(layoutManager, getHorizontalHelper(layoutManager)); 76849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 77849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas return null; 78849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 79849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas 80849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas @Override 81849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX, 82849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas int velocityY) { 83849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas final int itemCount = layoutManager.getItemCount(); 84849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas if (itemCount == 0) { 85849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas return RecyclerView.NO_POSITION; 86849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 87849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas 88849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas View mStartMostChildView = null; 89849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas if (layoutManager.canScrollVertically()) { 90849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas mStartMostChildView = findStartView(layoutManager, getVerticalHelper(layoutManager)); 91849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } else if (layoutManager.canScrollHorizontally()) { 92849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas mStartMostChildView = findStartView(layoutManager, getHorizontalHelper(layoutManager)); 93849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 94849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas 95849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas if (mStartMostChildView == null) { 96849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas return RecyclerView.NO_POSITION; 97849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 98849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas final int centerPosition = layoutManager.getPosition(mStartMostChildView); 99849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas if (centerPosition == RecyclerView.NO_POSITION) { 100849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas return RecyclerView.NO_POSITION; 101849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 102849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas 103849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas final boolean forwardDirection; 104849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas if (layoutManager.canScrollHorizontally()) { 105849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas forwardDirection = velocityX > 0; 106849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } else { 107849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas forwardDirection = velocityY > 0; 108849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 109849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas boolean reverseLayout = false; 110849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas if ((layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) { 111849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas RecyclerView.SmoothScroller.ScrollVectorProvider vectorProvider = 112849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas (RecyclerView.SmoothScroller.ScrollVectorProvider) layoutManager; 113849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas PointF vectorForEnd = vectorProvider.computeScrollVectorForPosition(itemCount - 1); 114849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas if (vectorForEnd != null) { 115849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas reverseLayout = vectorForEnd.x < 0 || vectorForEnd.y < 0; 116849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 117849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 118849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas return reverseLayout 119849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas ? (forwardDirection ? centerPosition - 1 : centerPosition) 120849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas : (forwardDirection ? centerPosition + 1 : centerPosition); 121849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 122849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas 123396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas @Override 124396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas protected LinearSmoothScroller createSnapScroller(RecyclerView.LayoutManager layoutManager) { 125396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas if (!(layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) { 126396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas return null; 127396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas } 128396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas return new LinearSmoothScroller(mRecyclerView.getContext()) { 129396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas @Override 130396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas protected void onTargetFound(View targetView, RecyclerView.State state, Action action) { 131396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas int[] snapDistances = calculateDistanceToFinalSnap(mRecyclerView.getLayoutManager(), 132396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas targetView); 133396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas final int dx = snapDistances[0]; 134396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas final int dy = snapDistances[1]; 135396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas final int time = calculateTimeForDeceleration(Math.max(Math.abs(dx), Math.abs(dy))); 136396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas if (time > 0) { 137396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas action.update(dx, dy, time, mDecelerateInterpolator); 138396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas } 139396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas } 140396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas 141396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas @Override 142396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) { 143396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas return MILLISECONDS_PER_INCH / displayMetrics.densityDpi; 144396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas } 145396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas 146396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas @Override 147396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas protected int calculateTimeForScrolling(int dx) { 148396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas return Math.min(MAX_SCROLL_ON_FLING_DURATION, super.calculateTimeForScrolling(dx)); 149396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas } 150396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas }; 151396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas } 152396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas 153849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas private int distanceToCenter(@NonNull RecyclerView.LayoutManager layoutManager, 154849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas @NonNull View targetView, OrientationHelper helper) { 155849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas final int childCenter = helper.getDecoratedStart(targetView) 156849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas + (helper.getDecoratedMeasurement(targetView) / 2); 157849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas final int containerCenter; 158849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas if (layoutManager.getClipToPadding()) { 159849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas containerCenter = helper.getStartAfterPadding() + helper.getTotalSpace() / 2; 160849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } else { 161849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas containerCenter = helper.getEnd() / 2; 162849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 163849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas return childCenter - containerCenter; 164849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 165849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas 166849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas /** 167849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * Return the child view that is currently closest to the center of this parent. 168849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * 169849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * @param layoutManager The {@link RecyclerView.LayoutManager} associated with the attached 170849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * {@link RecyclerView}. 171849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * @param helper The relevant {@link OrientationHelper} for the attached {@link RecyclerView}. 172849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * 173849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * @return the child view that is currently closest to the center of this parent. 174849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas */ 175849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas @Nullable 176849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas private View findCenterView(RecyclerView.LayoutManager layoutManager, 177849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas OrientationHelper helper) { 178849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas int childCount = layoutManager.getChildCount(); 179849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas if (childCount == 0) { 180849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas return null; 181849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 182849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas 183849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas View closestChild = null; 184849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas final int center; 185849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas if (layoutManager.getClipToPadding()) { 186849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas center = helper.getStartAfterPadding() + helper.getTotalSpace() / 2; 187849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } else { 188849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas center = helper.getEnd() / 2; 189849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 190849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas int absClosest = Integer.MAX_VALUE; 191849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas 192849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas for (int i = 0; i < childCount; i++) { 193849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas final View child = layoutManager.getChildAt(i); 194849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas int childCenter = helper.getDecoratedStart(child) 195849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas + (helper.getDecoratedMeasurement(child) / 2); 196849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas int absDistance = Math.abs(childCenter - center); 197849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas 198849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas /** if child center is closer than previous closest, set it as closest **/ 199849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas if (absDistance < absClosest) { 200849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas absClosest = absDistance; 201849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas closestChild = child; 202849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 203849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 204849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas return closestChild; 205849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 206849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas 207849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas /** 208849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * Return the child view that is currently closest to the start of this parent. 209849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * 210849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * @param layoutManager The {@link RecyclerView.LayoutManager} associated with the attached 211849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * {@link RecyclerView}. 212849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * @param helper The relevant {@link OrientationHelper} for the attached {@link RecyclerView}. 213849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * 214849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * @return the child view that is currently closest to the start of this parent. 215849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas */ 216849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas @Nullable 217849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas private View findStartView(RecyclerView.LayoutManager layoutManager, 218849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas OrientationHelper helper) { 219849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas int childCount = layoutManager.getChildCount(); 220849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas if (childCount == 0) { 221849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas return null; 222849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 223849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas 224849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas View closestChild = null; 225849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas int startest = Integer.MAX_VALUE; 226849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas 227849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas for (int i = 0; i < childCount; i++) { 228849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas final View child = layoutManager.getChildAt(i); 229849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas int childStart = helper.getDecoratedStart(child); 230849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas 231849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas /** if child is more to start than previous closest, set it as closest **/ 232849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas if (childStart < startest) { 233849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas startest = childStart; 234849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas closestChild = child; 235849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 236849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 237849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas return closestChild; 238849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 239849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas 240849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas @NonNull 241849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas private OrientationHelper getVerticalHelper(@NonNull RecyclerView.LayoutManager layoutManager) { 242849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas if (mVerticalHelper == null || mVerticalHelper.mLayoutManager != layoutManager) { 243849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas mVerticalHelper = OrientationHelper.createVerticalHelper(layoutManager); 244849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 245849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas return mVerticalHelper; 246849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 247849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas 248849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas @NonNull 249849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas private OrientationHelper getHorizontalHelper( 250849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas @NonNull RecyclerView.LayoutManager layoutManager) { 251849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas if (mHorizontalHelper == null || mHorizontalHelper.mLayoutManager != layoutManager) { 252849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager); 253849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 254849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas return mHorizontalHelper; 255849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas } 256849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas} 257