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