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.common.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 position. This class
     26  * takes advantage of similar functionality that exists in {@link ListView} and enhances it.
     27  */
     28 public class AutoScrollListView extends ListView {
     29 
     30   /** Position the element at about 1/3 of the list height */
     31   private static final float PREFERRED_SELECTION_OFFSET_FROM_TOP = 0.33f;
     32 
     33   private int mRequestedScrollPosition = -1;
     34   private boolean mSmoothScrollRequested;
     35 
     36   public AutoScrollListView(Context context) {
     37     super(context);
     38   }
     39 
     40   public AutoScrollListView(Context context, AttributeSet attrs) {
     41     super(context, attrs);
     42   }
     43 
     44   public AutoScrollListView(Context context, AttributeSet attrs, int defStyle) {
     45     super(context, attrs, defStyle);
     46   }
     47 
     48   /**
     49    * Brings the specified position to view by optionally performing a jump-scroll maneuver: first it
     50    * jumps to some position near the one requested and then does a smooth scroll to the requested
     51    * position. This creates an impression of full smooth scrolling without actually traversing the
     52    * entire list. If smooth scrolling is not requested, instantly positions the requested item at a
     53    * preferred offset.
     54    */
     55   public void requestPositionToScreen(int position, boolean smoothScroll) {
     56     mRequestedScrollPosition = position;
     57     mSmoothScrollRequested = smoothScroll;
     58     requestLayout();
     59   }
     60 
     61   @Override
     62   protected void layoutChildren() {
     63     super.layoutChildren();
     64     if (mRequestedScrollPosition == -1) {
     65       return;
     66     }
     67 
     68     final int position = mRequestedScrollPosition;
     69     mRequestedScrollPosition = -1;
     70 
     71     int firstPosition = getFirstVisiblePosition() + 1;
     72     int lastPosition = getLastVisiblePosition();
     73     if (position >= firstPosition && position <= lastPosition) {
     74       return; // Already on screen
     75     }
     76 
     77     final int offset = (int) (getHeight() * PREFERRED_SELECTION_OFFSET_FROM_TOP);
     78     if (!mSmoothScrollRequested) {
     79       setSelectionFromTop(position, offset);
     80 
     81       // Since we have changed the scrolling position, we need to redo child layout
     82       // Calling "requestLayout" in the middle of a layout pass has no effect,
     83       // so we call layoutChildren explicitly
     84       super.layoutChildren();
     85 
     86     } else {
     87       // We will first position the list a couple of screens before or after
     88       // the new selection and then scroll smoothly to it.
     89       int twoScreens = (lastPosition - firstPosition) * 2;
     90       int preliminaryPosition;
     91       if (position < firstPosition) {
     92         preliminaryPosition = position + twoScreens;
     93         if (preliminaryPosition >= getCount()) {
     94           preliminaryPosition = getCount() - 1;
     95         }
     96         if (preliminaryPosition < firstPosition) {
     97           setSelection(preliminaryPosition);
     98           super.layoutChildren();
     99         }
    100       } else {
    101         preliminaryPosition = position - twoScreens;
    102         if (preliminaryPosition < 0) {
    103           preliminaryPosition = 0;
    104         }
    105         if (preliminaryPosition > lastPosition) {
    106           setSelection(preliminaryPosition);
    107           super.layoutChildren();
    108         }
    109       }
    110 
    111       smoothScrollToPositionFromTop(position, offset);
    112     }
    113   }
    114 
    115   @Override
    116   protected void onLayout(boolean changed, int l, int t, int r, int b) {
    117     super.onLayout(changed, l, t, r, b);
    118 
    119     // Workaround for a bug and a bug.
    120     if (android.os.Build.VERSION.SDK_INT == Build.VERSION_CODES.N
    121         || android.os.Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1) {
    122       layoutChildren();
    123     }
    124   }
    125 }
    126