Home | History | Annotate | Download | only in widget
      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 
     57     public StickyHeaderListView(Context context) {
     58         super(context);
     59     }
     60 
     61     public StickyHeaderListView(Context context, AttributeSet attrs) {
     62         super(context, attrs);
     63     }
     64 
     65     public StickyHeaderListView(Context context, AttributeSet attrs, int defStyleAttr) {
     66         super(context, attrs, defStyleAttr);
     67     }
     68 
     69     public StickyHeaderListView(Context context, AttributeSet attrs, int defStyleAttr,
     70                                 int defStyleRes) {
     71         super(context, attrs, defStyleAttr, defStyleRes);
     72     }
     73 
     74     @Override
     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 
     82     public void updateStickyView() {
     83         mSticky = findViewWithTag("sticky");
     84         mStickyContainer = findViewWithTag("stickyContainer");
     85     }
     86 
     87     @Override
     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
     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
    127     protected boolean isVerticalScrollBarHidden() {
    128         return super.isVerticalScrollBarHidden() || !mDrawScrollBar;
    129     }
    130 
    131     @Override
    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