ScrollbarHelper.java revision 392b5deade5236541cd95743001236ac86abd051
1d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar/*
2d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * Copyright (C) 2014 The Android Open Source Project
3d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar *
4d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
5d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * you may not use this file except in compliance with the License.
6d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * You may obtain a copy of the License at
7d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar *
8d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
9d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar *
10d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * Unless required by applicable law or agreed to in writing, software
11d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
12d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * See the License for the specific language governing permissions and
14d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * limitations under the License.
15d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar */
16d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyarpackage android.support.v7.widget;
17d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar
18d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyarimport android.view.View;
19d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar
20d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar/**
21d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar * A helper class to do scroll offset calculations.
22d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar */
23d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyarclass ScrollbarHelper {
24d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar
25d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar    /**
26d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar     * @param startChild View closest to start of the list. (top or left)
27d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar     * @param endChild   View closest to end of the list (bottom or right)
28d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar     */
29d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar    static int computeScrollOffset(RecyclerView.State state, OrientationHelper orientation,
30d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar            View startChild, View endChild, RecyclerView.LayoutManager lm,
31d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar            boolean smoothScrollbarEnabled, boolean reverseLayout) {
32d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null ||
33d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar                endChild == null) {
34d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar            return 0;
35d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        }
36d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        final int minPosition = Math.min(lm.getPosition(startChild), lm.getPosition(endChild));
37d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        final int maxPosition = Math.max(lm.getPosition(startChild), lm.getPosition(endChild));
38d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        final int itemsBefore = reverseLayout
39d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar                ? Math.max(0, state.getItemCount() - maxPosition - 1)
40392b5deade5236541cd95743001236ac86abd051Yigit Boyar                : Math.max(0, minPosition);
41d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        if (!smoothScrollbarEnabled) {
42d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar            return itemsBefore;
43d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        }
44d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        final int laidOutArea = Math.abs(orientation.getDecoratedEnd(endChild) -
45d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar                orientation.getDecoratedStart(startChild));
46d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        final int itemRange = Math.abs(lm.getPosition(startChild) - lm.getPosition(endChild)) + 1;
47d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        final float avgSizePerRow = (float) laidOutArea / itemRange;
48d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar
49d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        return Math.round(itemsBefore * avgSizePerRow + (orientation.getStartAfterPadding()
50d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar                - orientation.getDecoratedStart(startChild)));
51d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar    }
52d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar
53d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar    /**
54d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar     * @param startChild View closest to start of the list. (top or left)
55d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar     * @param endChild   View closest to end of the list (bottom or right)
56d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar     */
57d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar    static int computeScrollExtent(RecyclerView.State state, OrientationHelper orientation,
58d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar            View startChild, View endChild, RecyclerView.LayoutManager lm,
59d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar            boolean smoothScrollbarEnabled) {
60d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null ||
61d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar                endChild == null) {
62d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar            return 0;
63d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        }
64d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        if (!smoothScrollbarEnabled) {
65d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar            return Math.abs(lm.getPosition(startChild) - lm.getPosition(endChild)) + 1;
66d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        }
67d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        final int extend = orientation.getDecoratedEnd(endChild)
68d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar                - orientation.getDecoratedStart(startChild);
69d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        return Math.min(orientation.getTotalSpace(), extend);
70d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar    }
71d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar
72d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar    /**
73d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar     * @param startChild View closest to start of the list. (top or left)
74d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar     * @param endChild   View closest to end of the list (bottom or right)
75d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar     */
76d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar    static int computeScrollRange(RecyclerView.State state, OrientationHelper orientation,
77d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar            View startChild, View endChild, RecyclerView.LayoutManager lm,
78d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar            boolean smoothScrollbarEnabled) {
79d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null ||
80d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar                endChild == null) {
81d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar            return 0;
82d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        }
83d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        if (!smoothScrollbarEnabled) {
84d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar            return state.getItemCount();
85d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        }
86d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        // smooth scrollbar enabled. try to estimate better.
87d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        final int laidOutArea = orientation.getDecoratedEnd(endChild) -
88d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar                orientation.getDecoratedStart(startChild);
89d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        final int laidOutRange = Math.abs(lm.getPosition(startChild) - lm.getPosition(endChild))
90d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar                + 1;
91d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        // estimate a size for full list.
92d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        return (int) ((float) laidOutArea / laidOutRange * state.getItemCount());
93d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar    }
94d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar}
95