1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.contacts.list;
18 
19 import android.content.Context;
20 import android.os.Build;
21 import android.util.AttributeSet;
22 import android.widget.ListView;
23 
24 /**
25  * A ListView that can be asked to scroll (smoothly or otherwise) to a specific
26  * position.  This class takes advantage of similar functionality that exists
27  * in {@link ListView} and enhances it.
28  */
29 public class AutoScrollListView extends ListView {
30 
31     /**
32      * Position the element at about 1/3 of the list height
33      */
34     private static final float PREFERRED_SELECTION_OFFSET_FROM_TOP = 0.33f;
35 
36     private int mRequestedScrollPosition = -1;
37     private boolean mSmoothScrollRequested;
38 
AutoScrollListView(Context context)39     public AutoScrollListView(Context context) {
40         super(context);
41     }
42 
AutoScrollListView(Context context, AttributeSet attrs)43     public AutoScrollListView(Context context, AttributeSet attrs) {
44         super(context, attrs);
45     }
46 
AutoScrollListView(Context context, AttributeSet attrs, int defStyle)47     public AutoScrollListView(Context context, AttributeSet attrs, int defStyle) {
48         super(context, attrs, defStyle);
49     }
50 
51     /**
52      * Brings the specified position to view by optionally performing a jump-scroll maneuver:
53      * first it jumps to some position near the one requested and then does a smooth
54      * scroll to the requested position.  This creates an impression of full smooth
55      * scrolling without actually traversing the entire list.  If smooth scrolling is
56      * not requested, instantly positions the requested item at a preferred offset.
57      */
requestPositionToScreen(int position, boolean smoothScroll)58     public void requestPositionToScreen(int position, boolean smoothScroll) {
59         mRequestedScrollPosition = position;
60         mSmoothScrollRequested = smoothScroll;
61         requestLayout();
62     }
63 
64     @Override
layoutChildren()65     protected void layoutChildren() {
66         super.layoutChildren();
67         if (mRequestedScrollPosition == -1) {
68             return;
69         }
70 
71         final int position = mRequestedScrollPosition;
72         mRequestedScrollPosition = -1;
73 
74         int firstPosition = getFirstVisiblePosition() + 1;
75         int lastPosition = getLastVisiblePosition();
76         if (position >= firstPosition && position <= lastPosition) {
77             return; // Already on screen
78         }
79 
80         final int offset = (int) (getHeight() * PREFERRED_SELECTION_OFFSET_FROM_TOP);
81         if (!mSmoothScrollRequested) {
82             setSelectionFromTop(position, offset);
83 
84             // Since we have changed the scrolling position, we need to redo child layout
85             // Calling "requestLayout" in the middle of a layout pass has no effect,
86             // so we call layoutChildren explicitly
87             super.layoutChildren();
88 
89         } else {
90             // We will first position the list a couple of screens before or after
91             // the new selection and then scroll smoothly to it.
92             int twoScreens = (lastPosition - firstPosition) * 2;
93             int preliminaryPosition;
94             if (position < firstPosition) {
95                 preliminaryPosition = position + twoScreens;
96                 if (preliminaryPosition >= getCount()) {
97                     preliminaryPosition = getCount() - 1;
98                 }
99                 if (preliminaryPosition < firstPosition) {
100                     setSelection(preliminaryPosition);
101                     super.layoutChildren();
102                 }
103             } else {
104                 preliminaryPosition = position - twoScreens;
105                 if (preliminaryPosition < 0) {
106                     preliminaryPosition = 0;
107                 }
108                 if (preliminaryPosition > lastPosition) {
109                     setSelection(preliminaryPosition);
110                     super.layoutChildren();
111                 }
112             }
113 
114 
115             smoothScrollToPositionFromTop(position, offset);
116         }
117     }
118 
119     @Override
onLayout(boolean changed, int l, int t, int r, int b)120     protected void onLayout(boolean changed, int l, int t, int r, int b) {
121         super.onLayout(changed, l, t, r, b);
122 
123         // Workaround for b/31160338.
124         if (android.os.Build.VERSION.SDK_INT == Build.VERSION_CODES.N
125             || android.os.Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1) {
126             layoutChildren();
127         }
128     }
129 }
130