PagerSnapHelper.java revision 396f55a04df1b3fdfa3e7192ce14f050aed9a6d9
1849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas/*
2849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * Copyright (C) 2016 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
17849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikaspackage android.support.v7.widget;
18849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas
19849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikasimport android.graphics.PointF;
20849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikasimport android.support.annotation.NonNull;
21849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikasimport android.support.annotation.Nullable;
22396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikasimport android.util.DisplayMetrics;
23849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikasimport android.view.View;
24849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas
25849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas/**
26849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * Implementation of the {@link SnapHelper} supporting pager style snapping in either vertical or
27849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas * horizontal orientation.
28396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas *
29396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas * <p>
30396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas *
31396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas * PagerSnapHelper can help achieve a similar behavior to {@link android.support.v4.view.ViewPager}.
32396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas * Set both {@link RecyclerView} and {@link android.support.v7.widget.RecyclerView.Adapter} to have
33396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas * MATCH_PARENT height and width and then attach PagerSnapHelper to the {@link RecyclerView} using
34396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas * {@link #attachToRecyclerView(RecyclerView)}.
35849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas */
36849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikaspublic class PagerSnapHelper extends SnapHelper {
37396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas    private static final int MAX_SCROLL_ON_FLING_DURATION = 100; // ms
38396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas
39849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    // Orientation helpers are lazily created per LayoutManager.
40849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    @Nullable
41849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    private OrientationHelper mVerticalHelper;
42849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    @Nullable
43849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    private OrientationHelper mHorizontalHelper;
44849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas
45849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    @Nullable
46849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    @Override
47849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager,
48849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            @NonNull View targetView) {
49849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        int[] out = new int[2];
50849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        if (layoutManager.canScrollHorizontally()) {
51849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            out[0] = distanceToCenter(layoutManager, targetView,
52849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas                    getHorizontalHelper(layoutManager));
53849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        } else {
54849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            out[0] = 0;
55849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        }
56849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas
57849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        if (layoutManager.canScrollVertically()) {
58849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            out[1] = distanceToCenter(layoutManager, targetView,
59849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas                    getVerticalHelper(layoutManager));
60849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        } else {
61849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            out[1] = 0;
62849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        }
63849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        return out;
64849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    }
65849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas
66849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    @Nullable
67849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    @Override
68849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    public View findSnapView(RecyclerView.LayoutManager layoutManager) {
69849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        if (layoutManager.canScrollVertically()) {
70849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            return findCenterView(layoutManager, getVerticalHelper(layoutManager));
71849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        } else if (layoutManager.canScrollHorizontally()) {
72849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            return findCenterView(layoutManager, getHorizontalHelper(layoutManager));
73849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        }
74849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        return null;
75849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    }
76849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas
77849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    @Override
78849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX,
79849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            int velocityY) {
80849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        final int itemCount = layoutManager.getItemCount();
81849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        if (itemCount == 0) {
82849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            return RecyclerView.NO_POSITION;
83849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        }
84849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas
85849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        View mStartMostChildView = null;
86849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        if (layoutManager.canScrollVertically()) {
87849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            mStartMostChildView = findStartView(layoutManager, getVerticalHelper(layoutManager));
88849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        } else if (layoutManager.canScrollHorizontally()) {
89849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            mStartMostChildView = findStartView(layoutManager, getHorizontalHelper(layoutManager));
90849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        }
91849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas
92849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        if (mStartMostChildView == null) {
93849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            return RecyclerView.NO_POSITION;
94849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        }
95849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        final int centerPosition = layoutManager.getPosition(mStartMostChildView);
96849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        if (centerPosition == RecyclerView.NO_POSITION) {
97849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            return RecyclerView.NO_POSITION;
98849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        }
99849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas
100849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        final boolean forwardDirection;
101849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        if (layoutManager.canScrollHorizontally()) {
102849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            forwardDirection = velocityX > 0;
103849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        } else {
104849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            forwardDirection = velocityY > 0;
105849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        }
106849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        boolean reverseLayout = false;
107849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        if ((layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) {
108849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            RecyclerView.SmoothScroller.ScrollVectorProvider vectorProvider =
109849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas                    (RecyclerView.SmoothScroller.ScrollVectorProvider) layoutManager;
110849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            PointF vectorForEnd = vectorProvider.computeScrollVectorForPosition(itemCount - 1);
111849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            if (vectorForEnd != null) {
112849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas                reverseLayout = vectorForEnd.x < 0 || vectorForEnd.y < 0;
113849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            }
114849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        }
115849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        return reverseLayout
116849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas                ? (forwardDirection ? centerPosition - 1 : centerPosition)
117849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas                : (forwardDirection ? centerPosition + 1 : centerPosition);
118849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    }
119849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas
120396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas    @Override
121396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas    protected LinearSmoothScroller createSnapScroller(RecyclerView.LayoutManager layoutManager) {
122396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas        if (!(layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) {
123396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas            return null;
124396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas        }
125396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas        return new LinearSmoothScroller(mRecyclerView.getContext()) {
126396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas            @Override
127396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas            protected void onTargetFound(View targetView, RecyclerView.State state, Action action) {
128396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas                int[] snapDistances = calculateDistanceToFinalSnap(mRecyclerView.getLayoutManager(),
129396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas                        targetView);
130396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas                final int dx = snapDistances[0];
131396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas                final int dy = snapDistances[1];
132396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas                final int time = calculateTimeForDeceleration(Math.max(Math.abs(dx), Math.abs(dy)));
133396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas                if (time > 0) {
134396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas                    action.update(dx, dy, time, mDecelerateInterpolator);
135396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas                }
136396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas            }
137396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas
138396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas            @Override
139396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas            protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
140396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas                return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
141396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas            }
142396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas
143396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas            @Override
144396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas            protected int calculateTimeForScrolling(int dx) {
145396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas                return Math.min(MAX_SCROLL_ON_FLING_DURATION, super.calculateTimeForScrolling(dx));
146396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas            }
147396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas        };
148396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas    }
149396f55a04df1b3fdfa3e7192ce14f050aed9a6d9Aurimas Liutikas
150849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    private int distanceToCenter(@NonNull RecyclerView.LayoutManager layoutManager,
151849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            @NonNull View targetView, OrientationHelper helper) {
152849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        final int childCenter = helper.getDecoratedStart(targetView)
153849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas                + (helper.getDecoratedMeasurement(targetView) / 2);
154849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        final int containerCenter;
155849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        if (layoutManager.getClipToPadding()) {
156849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            containerCenter = helper.getStartAfterPadding() + helper.getTotalSpace() / 2;
157849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        } else {
158849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            containerCenter = helper.getEnd() / 2;
159849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        }
160849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        return childCenter - containerCenter;
161849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    }
162849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas
163849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    /**
164849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas     * Return the child view that is currently closest to the center of this parent.
165849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas     *
166849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas     * @param layoutManager The {@link RecyclerView.LayoutManager} associated with the attached
167849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas     *                      {@link RecyclerView}.
168849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas     * @param helper The relevant {@link OrientationHelper} for the attached {@link RecyclerView}.
169849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas     *
170849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas     * @return the child view that is currently closest to the center of this parent.
171849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas     */
172849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    @Nullable
173849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    private View findCenterView(RecyclerView.LayoutManager layoutManager,
174849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            OrientationHelper helper) {
175849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        int childCount = layoutManager.getChildCount();
176849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        if (childCount == 0) {
177849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            return null;
178849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        }
179849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas
180849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        View closestChild = null;
181849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        final int center;
182849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        if (layoutManager.getClipToPadding()) {
183849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            center = helper.getStartAfterPadding() + helper.getTotalSpace() / 2;
184849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        } else {
185849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            center = helper.getEnd() / 2;
186849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        }
187849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        int absClosest = Integer.MAX_VALUE;
188849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas
189849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        for (int i = 0; i < childCount; i++) {
190849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            final View child = layoutManager.getChildAt(i);
191849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            int childCenter = helper.getDecoratedStart(child)
192849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas                    + (helper.getDecoratedMeasurement(child) / 2);
193849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            int absDistance = Math.abs(childCenter - center);
194849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas
195849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            /** if child center is closer than previous closest, set it as closest  **/
196849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            if (absDistance < absClosest) {
197849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas                absClosest = absDistance;
198849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas                closestChild = child;
199849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            }
200849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        }
201849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        return closestChild;
202849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    }
203849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas
204849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    /**
205849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas     * Return the child view that is currently closest to the start of this parent.
206849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas     *
207849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas     * @param layoutManager The {@link RecyclerView.LayoutManager} associated with the attached
208849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas     *                      {@link RecyclerView}.
209849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas     * @param helper The relevant {@link OrientationHelper} for the attached {@link RecyclerView}.
210849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas     *
211849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas     * @return the child view that is currently closest to the start of this parent.
212849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas     */
213849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    @Nullable
214849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    private View findStartView(RecyclerView.LayoutManager layoutManager,
215849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            OrientationHelper helper) {
216849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        int childCount = layoutManager.getChildCount();
217849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        if (childCount == 0) {
218849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            return null;
219849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        }
220849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas
221849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        View closestChild = null;
222849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        int startest = Integer.MAX_VALUE;
223849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas
224849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        for (int i = 0; i < childCount; i++) {
225849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            final View child = layoutManager.getChildAt(i);
226849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            int childStart = helper.getDecoratedStart(child);
227849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas
228849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            /** if child is more to start than previous closest, set it as closest  **/
229849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            if (childStart < startest) {
230849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas                startest = childStart;
231849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas                closestChild = child;
232849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            }
233849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        }
234849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        return closestChild;
235849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    }
236849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas
237849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    @NonNull
238849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    private OrientationHelper getVerticalHelper(@NonNull RecyclerView.LayoutManager layoutManager) {
239849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        if (mVerticalHelper == null || mVerticalHelper.mLayoutManager != layoutManager) {
240849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            mVerticalHelper = OrientationHelper.createVerticalHelper(layoutManager);
241849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        }
242849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        return mVerticalHelper;
243849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    }
244849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas
245849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    @NonNull
246849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    private OrientationHelper getHorizontalHelper(
247849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            @NonNull RecyclerView.LayoutManager layoutManager) {
248849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        if (mHorizontalHelper == null || mHorizontalHelper.mLayoutManager != layoutManager) {
249849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas            mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager);
250849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        }
251849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas        return mHorizontalHelper;
252849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas    }
253849542dfdc5e83411c8b959251eb6f2a1556fc9dAurimas Liutikas}
254