Home | History | Annotate | Download | only in list
      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 
     39     public AutoScrollListView(Context context) {
     40         super(context);
     41     }
     42 
     43     public AutoScrollListView(Context context, AttributeSet attrs) {
     44         super(context, attrs);
     45     }
     46 
     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      */
     58     public void requestPositionToScreen(int position, boolean smoothScroll) {
     59         mRequestedScrollPosition = position;
     60         mSmoothScrollRequested = smoothScroll;
     61         requestLayout();
     62     }
     63 
     64     @Override
     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
    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