LinearSnapHelper.java revision 1e827caa16440590647019b9c1338f6309c5be7a
1c587f7dba5a337169e854e235da59f595255d6ccAga Madurska/*
2c587f7dba5a337169e854e235da59f595255d6ccAga Madurska * Copyright (C) 2016 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
17c587f7dba5a337169e854e235da59f595255d6ccAga Madurskapackage android.support.v7.widget;
18c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
19c587f7dba5a337169e854e235da59f595255d6ccAga Madurskaimport android.graphics.PointF;
20c587f7dba5a337169e854e235da59f595255d6ccAga Madurskaimport android.support.annotation.NonNull;
21c587f7dba5a337169e854e235da59f595255d6ccAga Madurskaimport android.support.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];
173f0084673d0d7518e246241ffb99737ddf036e64fAga Madurska        if (distance > 0) {
174f0084673d0d7518e246241ffb99737ddf036e64fAga Madurska            return (int) Math.floor(distance / distancePerChild);
175f0084673d0d7518e246241ffb99737ddf036e64fAga Madurska        } else {
176f0084673d0d7518e246241ffb99737ddf036e64fAga Madurska            return (int) Math.ceil(distance / distancePerChild);
177f0084673d0d7518e246241ffb99737ddf036e64fAga Madurska        }
178c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    }
179c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
180c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    /**
181c587f7dba5a337169e854e235da59f595255d6ccAga Madurska     * Return the child view that is currently closest to the center of this parent.
182c587f7dba5a337169e854e235da59f595255d6ccAga Madurska     *
183c587f7dba5a337169e854e235da59f595255d6ccAga Madurska     * @param layoutManager The {@link RecyclerView.LayoutManager} associated with the attached
184c587f7dba5a337169e854e235da59f595255d6ccAga Madurska     *                      {@link RecyclerView}.
185c587f7dba5a337169e854e235da59f595255d6ccAga Madurska     * @param helper The relevant {@link OrientationHelper} for the attached {@link RecyclerView}.
186c587f7dba5a337169e854e235da59f595255d6ccAga Madurska     *
187c587f7dba5a337169e854e235da59f595255d6ccAga Madurska     * @return the child view that is currently closest to the center of this parent.
188c587f7dba5a337169e854e235da59f595255d6ccAga Madurska     */
189c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    @Nullable
190c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    private View findCenterView(RecyclerView.LayoutManager layoutManager,
191c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            OrientationHelper helper) {
192c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        int childCount = layoutManager.getChildCount();
193c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        if (childCount == 0) {
194c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            return null;
195c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        }
196c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
197c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        View closestChild = null;
198c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        final int center;
199c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        if (layoutManager.getClipToPadding()) {
200c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            center = helper.getStartAfterPadding() + helper.getTotalSpace() / 2;
201c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        } else {
202c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            center = helper.getEnd() / 2;
203c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        }
204c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        int absClosest = Integer.MAX_VALUE;
205c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
206c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        for (int i = 0; i < childCount; i++) {
207c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            final View child = layoutManager.getChildAt(i);
2081e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas            int childCenter = helper.getDecoratedStart(child)
2091e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas                    + (helper.getDecoratedMeasurement(child) / 2);
210c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            int absDistance = Math.abs(childCenter - center);
211c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
212c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            /** if child center is closer than previous closest, set it as closest  **/
213c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            if (absDistance < absClosest) {
214c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                absClosest = absDistance;
215c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                closestChild = child;
216c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            }
217c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        }
218c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        return closestChild;
219c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    }
220c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
221c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    /**
222c587f7dba5a337169e854e235da59f595255d6ccAga Madurska     * Computes an average pixel value to pass a single child.
223c587f7dba5a337169e854e235da59f595255d6ccAga Madurska     * <p>
224c587f7dba5a337169e854e235da59f595255d6ccAga Madurska     * Returns a negative value if it cannot be calculated.
225c587f7dba5a337169e854e235da59f595255d6ccAga Madurska     *
226c587f7dba5a337169e854e235da59f595255d6ccAga Madurska     * @param layoutManager The {@link RecyclerView.LayoutManager} associated with the attached
227c587f7dba5a337169e854e235da59f595255d6ccAga Madurska     *                      {@link RecyclerView}.
228c587f7dba5a337169e854e235da59f595255d6ccAga Madurska     * @param helper        The relevant {@link OrientationHelper} for the attached
229c587f7dba5a337169e854e235da59f595255d6ccAga Madurska     *                      {@link RecyclerView.LayoutManager}.
230c587f7dba5a337169e854e235da59f595255d6ccAga Madurska     *
231c587f7dba5a337169e854e235da59f595255d6ccAga Madurska     * @return A float value that is the average number of pixels needed to scroll by one view in
232c587f7dba5a337169e854e235da59f595255d6ccAga Madurska     * the relevant direction.
233c587f7dba5a337169e854e235da59f595255d6ccAga Madurska     */
234c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    private float computeDistancePerChild(RecyclerView.LayoutManager layoutManager,
235c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            OrientationHelper helper) {
236c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        View minPosView = null;
237c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        View maxPosView = null;
238c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        int minPos = Integer.MAX_VALUE;
239c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        int maxPos = Integer.MIN_VALUE;
240c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        int childCount = layoutManager.getChildCount();
241c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        if (childCount == 0) {
242c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            return INVALID_DISTANCE;
243c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        }
244c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
245c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        for (int i = 0; i < childCount; i++) {
246c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            View child = layoutManager.getChildAt(i);
247c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            final int pos = layoutManager.getPosition(child);
248c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            if (pos == RecyclerView.NO_POSITION) {
249c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                continue;
250c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            }
251c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            if (pos < minPos) {
252c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                minPos = pos;
253c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                minPosView = child;
254c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            }
255c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            if (pos > maxPos) {
256c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                maxPos = pos;
257c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                maxPosView = child;
258c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            }
259c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        }
260c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        if (minPosView == null || maxPosView == null) {
261c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            return INVALID_DISTANCE;
262c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        }
263c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        int start = Math.min(helper.getDecoratedStart(minPosView),
264c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                helper.getDecoratedStart(maxPosView));
265c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        int end = Math.max(helper.getDecoratedEnd(minPosView),
266c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                helper.getDecoratedEnd(maxPosView));
267c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        int distance = end - start;
268c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        if (distance == 0) {
269c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            return INVALID_DISTANCE;
270c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        }
271c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        return 1f * distance / ((maxPos - minPos) + 1);
272c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    }
273c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
274c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    @NonNull
275c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    private OrientationHelper getVerticalHelper(@NonNull RecyclerView.LayoutManager layoutManager) {
276c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        if (mVerticalHelper == null || mVerticalHelper.mLayoutManager != layoutManager) {
277c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            mVerticalHelper = OrientationHelper.createVerticalHelper(layoutManager);
278c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        }
279c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        return mVerticalHelper;
280c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    }
281c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
282c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    @NonNull
283c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    private OrientationHelper getHorizontalHelper(
284c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            @NonNull RecyclerView.LayoutManager layoutManager) {
285c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        if (mHorizontalHelper == null || mHorizontalHelper.mLayoutManager != layoutManager) {
286c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager);
287c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        }
288c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        return mHorizontalHelper;
289c587f7dba5a337169e854e235da59f595255d6ccAga Madurska    }
290c587f7dba5a337169e854e235da59f595255d6ccAga Madurska}
291