1753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov/*
2753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov * Copyright (C) 2010 The Android Open Source Project
3753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov *
4753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov * Licensed under the Apache License, Version 2.0 (the "License");
5753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov * you may not use this file except in compliance with the License.
6753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov * You may obtain a copy of the License at
7753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov *
8753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov *      http://www.apache.org/licenses/LICENSE-2.0
9753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov *
10753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov * Unless required by applicable law or agreed to in writing, software
11753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov * distributed under the License is distributed on an "AS IS" BASIS,
12753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov * See the License for the specific language governing permissions and
14753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov * limitations under the License.
15753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov */
16753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov
17753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikovpackage com.android.contacts.widget;
18753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov
19753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikovimport android.content.Context;
20753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikovimport android.util.AttributeSet;
21753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikovimport android.widget.ListView;
22753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov
23753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov/**
24753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov * A ListView that can be asked to scroll (smoothly or otherwise) to a specific
25753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov * position.  This class takes advantage of similar functionality that exists
26753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov * in {@link ListView} and enhances it.
27753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov */
28753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikovpublic class AutoScrollListView extends ListView {
29753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov
30753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov    /**
31753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov     * Position the element at about 1/3 of the list height
32753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov     */
33753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov    private static final float PREFERRED_SELECTION_OFFSET_FROM_TOP = 0.33f;
34753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov
35ca0a6010b56801ff603f3c7c08327e30f90bfac0Dmitri Plotnikov    private int mRequestedScrollPosition = -1;
36753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov    private boolean mSmoothScrollRequested;
37753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov
38753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov    public AutoScrollListView(Context context) {
39753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov        super(context);
40753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov    }
41753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov
42753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov    public AutoScrollListView(Context context, AttributeSet attrs) {
43753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov        super(context, attrs);
44753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov    }
45753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov
46753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov    public AutoScrollListView(Context context, AttributeSet attrs, int defStyle) {
47753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov        super(context, attrs, defStyle);
48753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov    }
49753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov
50753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov    /**
51753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov     * Brings the specified position to view by optionally performing a jump-scroll maneuver:
52753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov     * first it jumps to some position near the one requested and then does a smooth
53753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov     * scroll to the requested position.  This creates an impression of full smooth
54753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov     * scrolling without actually traversing the entire list.  If smooth scrolling is
55753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov     * not requested, instantly positions the requested item at a preferred offset.
56753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov     */
57753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov    public void requestPositionToScreen(int position, boolean smoothScroll) {
58753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov        mRequestedScrollPosition = position;
59753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov        mSmoothScrollRequested = smoothScroll;
60753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov        requestLayout();
61753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov    }
62753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov
63753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov    @Override
64753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov    protected void layoutChildren() {
65753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov        super.layoutChildren();
66753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov        if (mRequestedScrollPosition == -1) {
67753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov            return;
68753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov        }
69753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov
70753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov        final int position = mRequestedScrollPosition;
71753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov        mRequestedScrollPosition = -1;
72753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov
73753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov        int firstPosition = getFirstVisiblePosition() + 1;
74753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov        int lastPosition = getLastVisiblePosition();
75753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov        if (position >= firstPosition && position <= lastPosition) {
76753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov            return; // Already on screen
77753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov        }
78753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov
79753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov        final int offset = (int) (getHeight() * PREFERRED_SELECTION_OFFSET_FROM_TOP);
80753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov        if (!mSmoothScrollRequested) {
81753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov            setSelectionFromTop(position, offset);
82753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov
83753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov            // Since we have changed the scrolling position, we need to redo child layout
84753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov            // Calling "requestLayout" in the middle of a layout pass has no effect,
85753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov            // so we call layoutChildren explicitly
86753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov            super.layoutChildren();
87753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov
88753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov        } else {
89753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov            // We will first position the list a couple of screens before or after
90753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov            // the new selection and then scroll smoothly to it.
91753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov            int twoScreens = (lastPosition - firstPosition) * 2;
92753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov            int preliminaryPosition;
93753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov            if (position < firstPosition) {
94753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov                preliminaryPosition = position + twoScreens;
95753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov                if (preliminaryPosition >= getCount()) {
96753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov                    preliminaryPosition = getCount() - 1;
97753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov                }
98753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov                if (preliminaryPosition < firstPosition) {
99753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov                    setSelection(preliminaryPosition);
100753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov                    super.layoutChildren();
101753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov                }
102753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov            } else {
103753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov                preliminaryPosition = position - twoScreens;
104753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov                if (preliminaryPosition < 0) {
105753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov                    preliminaryPosition = 0;
106753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov                }
107753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov                if (preliminaryPosition > lastPosition) {
108753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov                    setSelection(preliminaryPosition);
109753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov                    super.layoutChildren();
110753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov                }
111753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov            }
112753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov
113753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov
114753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov            smoothScrollToPositionFromTop(position, offset);
115753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov        }
116753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov    }
117753f8f6fde5a4f74181310593acebee94a014fe2Dmitri Plotnikov}
118