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