Home | History | Annotate | Download | only in view
      1 /*
      2  * Copyright (C) 2015 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.setupwizardlib.view;
     18 
     19 import android.content.Context;
     20 import android.util.AttributeSet;
     21 import android.view.View;
     22 import android.widget.ScrollView;
     23 
     24 import com.android.setupwizardlib.annotations.VisibleForTesting;
     25 
     26 /**
     27  * An extension of ScrollView that will invoke a listener callback when the ScrollView needs
     28  * scrolling, and when the ScrollView is being scrolled to the bottom. This is often used in Setup
     29  * Wizard as a way to ensure that users see all the content before proceeding.
     30  */
     31 public class BottomScrollView extends ScrollView {
     32 
     33     public interface BottomScrollListener {
     34         void onScrolledToBottom();
     35         void onRequiresScroll();
     36     }
     37 
     38     private BottomScrollListener mListener;
     39     private int mScrollThreshold;
     40     private boolean mRequiringScroll = false;
     41 
     42     private final Runnable mCheckScrollRunnable = new Runnable() {
     43         @Override
     44         public void run() {
     45             checkScroll();
     46         }
     47     };
     48 
     49     public BottomScrollView(Context context) {
     50         super(context);
     51     }
     52 
     53     public BottomScrollView(Context context, AttributeSet attrs) {
     54         super(context, attrs);
     55     }
     56 
     57     public BottomScrollView(Context context, AttributeSet attrs, int defStyle) {
     58         super(context, attrs, defStyle);
     59     }
     60 
     61     public void setBottomScrollListener(BottomScrollListener l) {
     62         mListener = l;
     63     }
     64 
     65     @VisibleForTesting
     66     public int getScrollThreshold() {
     67         return mScrollThreshold;
     68     }
     69 
     70     @Override
     71     protected void onLayout(boolean changed, int l, int t, int r, int b) {
     72         super.onLayout(changed, l, t, r, b);
     73         final View child = getChildAt(0);
     74         if (child != null) {
     75             mScrollThreshold = Math.max(0, child.getMeasuredHeight() - b + t - getPaddingBottom());
     76         }
     77         if (b - t > 0) {
     78             // Post check scroll in the next run loop, so that the callback methods will be invoked
     79             // after the layout pass. This way a new layout pass will be scheduled if view
     80             // properties are changed in the callbacks.
     81             post(mCheckScrollRunnable);
     82         }
     83     }
     84 
     85     @Override
     86     protected void onScrollChanged(int l, int t, int oldl, int oldt) {
     87         super.onScrollChanged(l, t, oldl, oldt);
     88         if (oldt != t) {
     89             checkScroll();
     90         }
     91     }
     92 
     93     private void checkScroll() {
     94         if (mListener != null) {
     95             if (getScrollY() >= mScrollThreshold) {
     96                 mListener.onScrolledToBottom();
     97             } else if (!mRequiringScroll) {
     98                 mRequiringScroll = true;
     99                 mListener.onRequiresScroll();
    100             }
    101         }
    102     }
    103 
    104 }
    105