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