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