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