• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 
18 package com.android.settings.widget;
19 
20 import android.content.Context;
21 import android.graphics.Canvas;
22 import android.graphics.RectF;
23 import android.util.AttributeSet;
24 import android.util.Log;
25 import android.view.MotionEvent;
26 import android.view.View;
27 import android.view.WindowInsets;
28 import android.widget.ListView;
29 
30 /**
31  * This class provides sticky header functionality in a list view, to use with
32  * SetupWizardIllustration. To use this, add a header tagged with "sticky", or a header tagged with
33  * "stickyContainer" and one of its child tagged as "sticky". The sticky container will be drawn
34  * when the sticky element hits the top of the view.
35  *
36  * There are a few things to note:
37  * 1. The two supported scenarios are StickyHeaderListView -> Header (stickyContainer) -> sticky,
38  *    and StickyHeaderListView -> Header (sticky). The arrow (->) represents parent/child
39  *    relationship and must be immediate child.
40  * 2. The view does not work well with padding. b/16190933
41  * 3. If fitsSystemWindows is true, then this will offset the sticking position by the height of
42  *    the system decorations at the top of the screen.
43  *
44  * @see SetupWizardIllustration
45  * @see com.google.android.setupwizard.util.StickyHeaderScrollView
46  *
47  * Copied from com.google.android.setupwizard.util.StickyHeaderListView
48  */
49 public class StickyHeaderListView extends ListView {
50 
51     private View mSticky;
52     private View mStickyContainer;
53     private boolean mDrawScrollBar;
54     private int mStatusBarInset = 0;
55     private RectF mStickyRect = new RectF();
56 
StickyHeaderListView(Context context)57     public StickyHeaderListView(Context context) {
58         super(context);
59     }
60 
StickyHeaderListView(Context context, AttributeSet attrs)61     public StickyHeaderListView(Context context, AttributeSet attrs) {
62         super(context, attrs);
63     }
64 
StickyHeaderListView(Context context, AttributeSet attrs, int defStyleAttr)65     public StickyHeaderListView(Context context, AttributeSet attrs, int defStyleAttr) {
66         super(context, attrs, defStyleAttr);
67     }
68 
StickyHeaderListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)69     public StickyHeaderListView(Context context, AttributeSet attrs, int defStyleAttr,
70                                 int defStyleRes) {
71         super(context, attrs, defStyleAttr, defStyleRes);
72     }
73 
74     @Override
onLayout(boolean changed, int l, int t, int r, int b)75     protected void onLayout(boolean changed, int l, int t, int r, int b) {
76         super.onLayout(changed, l, t, r, b);
77         if (mSticky == null) {
78             updateStickyView();
79         }
80     }
81 
updateStickyView()82     public void updateStickyView() {
83         mSticky = findViewWithTag("sticky");
84         mStickyContainer = findViewWithTag("stickyContainer");
85     }
86 
87     @Override
dispatchTouchEvent(MotionEvent ev)88     public boolean dispatchTouchEvent(MotionEvent ev) {
89         if (mStickyRect.contains(ev.getX(), ev.getY())) {
90             ev.offsetLocation(-mStickyRect.left, -mStickyRect.top);
91             return mStickyContainer.dispatchTouchEvent(ev);
92         } else {
93             return super.dispatchTouchEvent(ev);
94         }
95     }
96 
97     @Override
draw(Canvas canvas)98     public void draw(Canvas canvas) {
99         mDrawScrollBar = false;
100         super.draw(canvas);
101         if (mSticky != null) {
102             final int saveCount = canvas.save();
103             // The view to draw when sticking to the top
104             final View drawTarget = mStickyContainer != null ? mStickyContainer : mSticky;
105             // The offset to draw the view at when sticky
106             final int drawOffset = mStickyContainer != null ? mSticky.getTop() : 0;
107             // Position of the draw target, relative to the outside of the scrollView
108             final int drawTop = drawTarget.getTop();
109             if (drawTop + drawOffset < mStatusBarInset || !drawTarget.isShown()) {
110                 // ListView does not translate the canvas, so we can simply draw at the top
111                 canvas.translate(0, -drawOffset + mStatusBarInset);
112                 canvas.clipRect(0, 0, drawTarget.getWidth(), drawTarget.getHeight());
113                 drawTarget.draw(canvas);
114                 mStickyRect.set(0, -drawOffset + mStatusBarInset, drawTarget.getWidth(),
115                         drawTarget.getHeight() - drawOffset + mStatusBarInset);
116             } else {
117                 mStickyRect.setEmpty();
118             }
119             canvas.restoreToCount(saveCount);
120         }
121         // Draw the scrollbars last so they are on top of the header
122         mDrawScrollBar = true;
123         onDrawScrollBars(canvas);
124     }
125 
126     @Override
isVerticalScrollBarHidden()127     protected boolean isVerticalScrollBarHidden() {
128         return super.isVerticalScrollBarHidden() || !mDrawScrollBar;
129     }
130 
131     @Override
onApplyWindowInsets(WindowInsets insets)132     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
133         if (getFitsSystemWindows()) {
134             mStatusBarInset = insets.getSystemWindowInsetTop();
135             insets.consumeSystemWindowInsets(false, true, false, false);
136         }
137         return insets;
138     }
139 }
140