StickyHeaderScrollView.java revision 4a6e72df08c92a696d6b5d35b197e86585952ed9
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.annotation.TargetApi;
20import android.content.Context;
21import android.graphics.Canvas;
22import android.graphics.RectF;
23import android.os.Build;
24import android.util.AttributeSet;
25import android.view.View;
26import android.view.WindowInsets;
27
28/**
29 * This class provides sticky header functionality in a scroll view, to use with
30 * SetupWizardIllustration. To use this, add a subview tagged with "sticky", or a subview tagged
31 * with "stickyContainer" and one of its child tagged as "sticky". The sticky container will be
32 * drawn when the sticky element hits the top of the view.
33 *
34 * There are a few things to note:
35 * 1. The two supported scenarios are StickyHeaderScrollView -> subview (stickyContainer) -> sticky,
36 *    and StickyHeaderScrollView -> container -> subview (sticky).
37 *    The arrow (->) represents parent/child relationship and must be immediate child.
38 * 2. If fitsSystemWindows is true, then this will offset the sticking position by the height of
39 *    the system decorations at the top of the screen.
40 *
41 * @see StickyHeaderListView
42 */
43public class StickyHeaderScrollView extends BottomScrollView {
44
45    private View mSticky;
46    private View mStickyContainer;
47    private int mStatusBarInset = 0;
48
49    public StickyHeaderScrollView(Context context) {
50        super(context);
51    }
52
53    public StickyHeaderScrollView(Context context, AttributeSet attrs) {
54        super(context, attrs);
55    }
56
57    public StickyHeaderScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
58        super(context, attrs, defStyleAttr);
59    }
60
61    @Override
62    protected void onLayout(boolean changed, int l, int t, int r, int b) {
63        super.onLayout(changed, l, t, r, b);
64        if (mSticky == null) {
65            updateStickyView();
66        }
67    }
68
69    public void updateStickyView() {
70        mSticky = findViewWithTag("sticky");
71        mStickyContainer = findViewWithTag("stickyContainer");
72    }
73
74    @Override
75    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
76        super.onScrollChanged(l, t, oldl, oldt);
77        if (mSticky != null) {
78            // The view to draw when sticking to the top
79            final View drawTarget = mStickyContainer != null ? mStickyContainer : mSticky;
80            // The offset to draw the view at when sticky
81            final int drawOffset = mStickyContainer != null ? mSticky.getTop() : 0;
82            // Position of the draw target, relative to the outside of the scrollView
83            final int drawTop = drawTarget.getTop() - getScrollY();
84            if (drawTop + drawOffset < mStatusBarInset || !drawTarget.isShown()) {
85                // ScrollView translates the whole canvas so we have to compensate for that
86                drawTarget.setTranslationY(getScrollY() - drawOffset);
87            } else {
88                drawTarget.setTranslationY(0);
89            }
90        }
91    }
92
93    @Override
94    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
95    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
96        if (getFitsSystemWindows()) {
97            mStatusBarInset = insets.getSystemWindowInsetTop();
98            insets = insets.replaceSystemWindowInsets(
99                    insets.getSystemWindowInsetLeft(),
100                    0, /* top */
101                    insets.getSystemWindowInsetRight(),
102                    insets.getSystemWindowInsetBottom()
103            );
104        }
105        return insets;
106    }
107}
108