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