1d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng/* 2d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Copyright (C) 2010 The Android Open Source Project 3d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * 4d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Licensed under the Apache License, Version 2.0 (the "License"); 5d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * you may not use this file except in compliance with the License. 6d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * You may obtain a copy of the License at 7d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * 8d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * http://www.apache.org/licenses/LICENSE-2.0 9d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * 10d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Unless required by applicable law or agreed to in writing, software 11d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * distributed under the License is distributed on an "AS IS" BASIS, 12d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * See the License for the specific language governing permissions and 14d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * limitations under the License. 15d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 16d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 17d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengpackage com.android.contacts.common.list; 18d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 19d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.content.Context; 20d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.graphics.Canvas; 21d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.graphics.Rect; 22d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.graphics.RectF; 23d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.util.AttributeSet; 24d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.view.MotionEvent; 25d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.view.View; 26d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.view.ViewGroup; 27d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.widget.AbsListView; 28d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.widget.AbsListView.OnScrollListener; 29d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.widget.AdapterView; 30d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.widget.AdapterView.OnItemSelectedListener; 31d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.widget.ListAdapter; 32d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 33d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng/** 34d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * A ListView that maintains a header pinned at the top of the list. The 35d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * pinned header can be pushed up and dissolved as needed. 36d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 37d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengpublic class PinnedHeaderListView extends AutoScrollListView 38d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng implements OnScrollListener, OnItemSelectedListener { 39d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 40d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng /** 41d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Adapter interface. The list adapter must implement this interface. 42d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 43d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public interface PinnedHeaderAdapter { 44d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 45d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng /** 46d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Returns the overall number of pinned headers, visible or not. 47d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 48d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int getPinnedHeaderCount(); 49d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 50d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng /** 51d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Creates or updates the pinned header view. 52d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 53d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng View getPinnedHeaderView(int viewIndex, View convertView, ViewGroup parent); 54d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 55d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng /** 56d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Configures the pinned headers to match the visible list items. The 57d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * adapter should call {@link PinnedHeaderListView#setHeaderPinnedAtTop}, 58d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * {@link PinnedHeaderListView#setHeaderPinnedAtBottom}, 59d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * {@link PinnedHeaderListView#setFadingHeader} or 60d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * {@link PinnedHeaderListView#setHeaderInvisible}, for each header that 61d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * needs to change its position or visibility. 62d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 63d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng void configurePinnedHeaders(PinnedHeaderListView listView); 64d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 65d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng /** 66d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Returns the list position to scroll to if the pinned header is touched. 67d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Return -1 if the list does not need to be scrolled. 68d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 69d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int getScrollPositionForHeader(int viewIndex); 70d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 71d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 72d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private static final int MAX_ALPHA = 255; 73d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private static final int TOP = 0; 74d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private static final int BOTTOM = 1; 75d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private static final int FADING = 2; 76d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 77d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private static final int DEFAULT_ANIMATION_DURATION = 20; 78d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 79e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee private static final int DEFAULT_SMOOTH_SCROLL_DURATION = 100; 80e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee 81d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private static final class PinnedHeader { 82d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng View view; 83d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng boolean visible; 84d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int y; 85d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int height; 86d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int alpha; 87d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int state; 88d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 89d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng boolean animating; 90d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng boolean targetVisible; 91d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int sourceY; 92d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int targetY; 93d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng long targetTime; 94d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 95d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 96d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private PinnedHeaderAdapter mAdapter; 97d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private int mSize; 98d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private PinnedHeader[] mHeaders; 99d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private RectF mBounds = new RectF(); 100d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private Rect mClipRect = new Rect(); 101d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private OnScrollListener mOnScrollListener; 102d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private OnItemSelectedListener mOnItemSelectedListener; 103d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private int mScrollState; 104d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 105e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee private boolean mScrollToSectionOnHeaderTouch = false; 106e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee private boolean mHeaderTouched = false; 107e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee 108d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private int mAnimationDuration = DEFAULT_ANIMATION_DURATION; 109d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private boolean mAnimating; 110d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private long mAnimationTargetTime; 1119f0c1621718b382531cbebfe38907e6d3db6ceaeFabrice Di Meglio private int mHeaderPaddingStart; 112d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private int mHeaderWidth; 113d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 114d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public PinnedHeaderListView(Context context) { 1151af4d919420fab10d77356118dd9f75588186a2aYorke Lee this(context, null, android.R.attr.listViewStyle); 116d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 117d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 118d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public PinnedHeaderListView(Context context, AttributeSet attrs) { 1191af4d919420fab10d77356118dd9f75588186a2aYorke Lee this(context, attrs, android.R.attr.listViewStyle); 120d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 121d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 122d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public PinnedHeaderListView(Context context, AttributeSet attrs, int defStyle) { 123d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng super(context, attrs, defStyle); 124d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng super.setOnScrollListener(this); 125d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng super.setOnItemSelectedListener(this); 126d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 127d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 128d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng @Override 129d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng protected void onLayout(boolean changed, int l, int t, int r, int b) { 130d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng super.onLayout(changed, l, t, r, b); 1319f0c1621718b382531cbebfe38907e6d3db6ceaeFabrice Di Meglio mHeaderPaddingStart = getPaddingStart(); 1329f0c1621718b382531cbebfe38907e6d3db6ceaeFabrice Di Meglio mHeaderWidth = r - l - mHeaderPaddingStart - getPaddingEnd(); 133d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 134d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 135d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public void setPinnedHeaderAnimationDuration(int duration) { 136d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mAnimationDuration = duration; 137d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 138d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 139d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng @Override 140d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public void setAdapter(ListAdapter adapter) { 141d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mAdapter = (PinnedHeaderAdapter)adapter; 142d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng super.setAdapter(adapter); 143d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 144d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 145d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng @Override 146d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public void setOnScrollListener(OnScrollListener onScrollListener) { 147d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mOnScrollListener = onScrollListener; 148d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng super.setOnScrollListener(this); 149d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 150d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 151d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng @Override 152d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public void setOnItemSelectedListener(OnItemSelectedListener listener) { 153d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mOnItemSelectedListener = listener; 154d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng super.setOnItemSelectedListener(this); 155d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 156d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 157e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee public void setScrollToSectionOnHeaderTouch(boolean value) { 158e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee mScrollToSectionOnHeaderTouch = value; 159e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee } 160e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee 161d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng @Override 162d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, 163d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int totalItemCount) { 164d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (mAdapter != null) { 165d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int count = mAdapter.getPinnedHeaderCount(); 166d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (count != mSize) { 167d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mSize = count; 168d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (mHeaders == null) { 169d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mHeaders = new PinnedHeader[mSize]; 170d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } else if (mHeaders.length < mSize) { 171d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng PinnedHeader[] headers = mHeaders; 172d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mHeaders = new PinnedHeader[mSize]; 173d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng System.arraycopy(headers, 0, mHeaders, 0, headers.length); 174d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 175d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 176d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 177d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng for (int i = 0; i < mSize; i++) { 178d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (mHeaders[i] == null) { 179d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mHeaders[i] = new PinnedHeader(); 180d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 181d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mHeaders[i].view = mAdapter.getPinnedHeaderView(i, mHeaders[i].view, this); 182d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 183d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 184d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mAnimationTargetTime = System.currentTimeMillis() + mAnimationDuration; 185d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mAdapter.configurePinnedHeaders(this); 186d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng invalidateIfAnimating(); 187d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 188d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 189d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (mOnScrollListener != null) { 190d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mOnScrollListener.onScroll(this, firstVisibleItem, visibleItemCount, totalItemCount); 191d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 192d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 193d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 194d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng @Override 195d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng protected float getTopFadingEdgeStrength() { 196d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng // Disable vertical fading at the top when the pinned header is present 197d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng return mSize > 0 ? 0 : super.getTopFadingEdgeStrength(); 198d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 199d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 200d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng @Override 201d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public void onScrollStateChanged(AbsListView view, int scrollState) { 202d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mScrollState = scrollState; 203d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (mOnScrollListener != null) { 204d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mOnScrollListener.onScrollStateChanged(this, scrollState); 205d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 206d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 207d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 208d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng /** 209d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Ensures that the selected item is positioned below the top-pinned headers 210d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * and above the bottom-pinned ones. 211d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 212d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng @Override 213d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 214d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int height = getHeight(); 215d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 216d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int windowTop = 0; 217d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int windowBottom = height; 218d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 219d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng for (int i = 0; i < mSize; i++) { 220d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng PinnedHeader header = mHeaders[i]; 221d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.visible) { 222d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.state == TOP) { 223d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng windowTop = header.y + header.height; 224d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } else if (header.state == BOTTOM) { 225d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng windowBottom = header.y; 226d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng break; 227d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 228d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 229d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 230d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 231d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng View selectedView = getSelectedView(); 232d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (selectedView != null) { 233d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (selectedView.getTop() < windowTop) { 234d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng setSelectionFromTop(position, windowTop); 235d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } else if (selectedView.getBottom() > windowBottom) { 236d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng setSelectionFromTop(position, windowBottom - selectedView.getHeight()); 237d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 238d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 239d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 240d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (mOnItemSelectedListener != null) { 241d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mOnItemSelectedListener.onItemSelected(parent, view, position, id); 242d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 243d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 244d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 245d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng @Override 246d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public void onNothingSelected(AdapterView<?> parent) { 247d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (mOnItemSelectedListener != null) { 248d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mOnItemSelectedListener.onNothingSelected(parent); 249d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 250d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 251d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 252d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public int getPinnedHeaderHeight(int viewIndex) { 253d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng ensurePinnedHeaderLayout(viewIndex); 254d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng return mHeaders[viewIndex].view.getHeight(); 255d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 256d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 257d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng /** 258d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Set header to be pinned at the top. 259d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * 260d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * @param viewIndex index of the header view 261d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * @param y is position of the header in pixels. 262d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * @param animate true if the transition to the new coordinate should be animated 263d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 264d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public void setHeaderPinnedAtTop(int viewIndex, int y, boolean animate) { 265d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng ensurePinnedHeaderLayout(viewIndex); 266d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng PinnedHeader header = mHeaders[viewIndex]; 267d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.visible = true; 268d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.y = y; 269d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.state = TOP; 270d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 271d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng // TODO perhaps we should animate at the top as well 272d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.animating = false; 273d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 274d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 275d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng /** 276d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Set header to be pinned at the bottom. 277d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * 278d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * @param viewIndex index of the header view 279d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * @param y is position of the header in pixels. 280d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * @param animate true if the transition to the new coordinate should be animated 281d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 282d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public void setHeaderPinnedAtBottom(int viewIndex, int y, boolean animate) { 283d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng ensurePinnedHeaderLayout(viewIndex); 284d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng PinnedHeader header = mHeaders[viewIndex]; 285d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.state = BOTTOM; 286d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.animating) { 287d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.targetTime = mAnimationTargetTime; 288d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.sourceY = header.y; 289d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.targetY = y; 290d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } else if (animate && (header.y != y || !header.visible)) { 291d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.visible) { 292d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.sourceY = header.y; 293d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } else { 294d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.visible = true; 295d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.sourceY = y + header.height; 296d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 297d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.animating = true; 298d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.targetVisible = true; 299d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.targetTime = mAnimationTargetTime; 300d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.targetY = y; 301d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } else { 302d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.visible = true; 303d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.y = y; 304d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 305d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 306d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 307d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng /** 308d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Set header to be pinned at the top of the first visible item. 309d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * 310d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * @param viewIndex index of the header view 311d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * @param position is position of the header in pixels. 312d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 313d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public void setFadingHeader(int viewIndex, int position, boolean fade) { 314d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng ensurePinnedHeaderLayout(viewIndex); 315d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 316d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng View child = getChildAt(position - getFirstVisiblePosition()); 317d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (child == null) return; 318d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 319d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng PinnedHeader header = mHeaders[viewIndex]; 320d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.visible = true; 321d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.state = FADING; 322d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.alpha = MAX_ALPHA; 323d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.animating = false; 324d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 325d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int top = getTotalTopPinnedHeaderHeight(); 326d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.y = top; 327d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (fade) { 328d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int bottom = child.getBottom() - top; 329d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int headerHeight = header.height; 330d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (bottom < headerHeight) { 331d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int portion = bottom - headerHeight; 332d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.alpha = MAX_ALPHA * (headerHeight + portion) / headerHeight; 333d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.y = top + portion; 334d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 335d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 336d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 337d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 338d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng /** 339d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Makes header invisible. 340d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * 341d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * @param viewIndex index of the header view 342d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * @param animate true if the transition to the new coordinate should be animated 343d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 344d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public void setHeaderInvisible(int viewIndex, boolean animate) { 345d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng PinnedHeader header = mHeaders[viewIndex]; 346d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.visible && (animate || header.animating) && header.state == BOTTOM) { 347d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.sourceY = header.y; 348d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (!header.animating) { 349d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.visible = true; 350d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.targetY = getBottom() + header.height; 351d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 352d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.animating = true; 353d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.targetTime = mAnimationTargetTime; 354d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.targetVisible = false; 355d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } else { 356d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.visible = false; 357d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 358d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 359d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 360d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private void ensurePinnedHeaderLayout(int viewIndex) { 361d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng View view = mHeaders[viewIndex].view; 362d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (view.isLayoutRequested()) { 363d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int widthSpec = View.MeasureSpec.makeMeasureSpec(mHeaderWidth, View.MeasureSpec.EXACTLY); 364d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int heightSpec; 365d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); 366d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (layoutParams != null && layoutParams.height > 0) { 367d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng heightSpec = View.MeasureSpec 368d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng .makeMeasureSpec(layoutParams.height, View.MeasureSpec.EXACTLY); 369d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } else { 370d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); 371d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 372d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng view.measure(widthSpec, heightSpec); 373d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int height = view.getMeasuredHeight(); 374d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mHeaders[viewIndex].height = height; 375d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng view.layout(0, 0, mHeaderWidth, height); 376d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 377d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 378d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 379d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng /** 380d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Returns the sum of heights of headers pinned to the top. 381d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 382d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public int getTotalTopPinnedHeaderHeight() { 383d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng for (int i = mSize; --i >= 0;) { 384d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng PinnedHeader header = mHeaders[i]; 385d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.visible && header.state == TOP) { 386d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng return header.y + header.height; 387d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 388d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 389d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng return 0; 390d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 391d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 392d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng /** 393d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Returns the list item position at the specified y coordinate. 394d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 395d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public int getPositionAt(int y) { 396d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng do { 397d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int position = pointToPosition(getPaddingLeft() + 1, y); 398d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (position != -1) { 399d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng return position; 400d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 401d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng // If position == -1, we must have hit a separator. Let's examine 402d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng // a nearby pixel 403d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng y--; 404d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } while (y > 0); 405d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng return 0; 406d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 407d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 408d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng @Override 409d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public boolean onInterceptTouchEvent(MotionEvent ev) { 410e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee mHeaderTouched = false; 4110b8fc4af478f997bfc8bcaa785c9529ad8b797c7Yorke Lee if (super.onInterceptTouchEvent(ev)) { 4120b8fc4af478f997bfc8bcaa785c9529ad8b797c7Yorke Lee return true; 4130b8fc4af478f997bfc8bcaa785c9529ad8b797c7Yorke Lee } 4140b8fc4af478f997bfc8bcaa785c9529ad8b797c7Yorke Lee 415d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (mScrollState == SCROLL_STATE_IDLE) { 416d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng final int y = (int)ev.getY(); 4172be2e46d936b70e4e05e26e207a979d4b71c2b72Yorke Lee final int x = (int)ev.getX(); 418d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng for (int i = mSize; --i >= 0;) { 419d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng PinnedHeader header = mHeaders[i]; 4202be2e46d936b70e4e05e26e207a979d4b71c2b72Yorke Lee // For RTL layouts, this also takes into account that the scrollbar is on the left 4212be2e46d936b70e4e05e26e207a979d4b71c2b72Yorke Lee // side. 4222be2e46d936b70e4e05e26e207a979d4b71c2b72Yorke Lee final int padding = getPaddingLeft(); 4232be2e46d936b70e4e05e26e207a979d4b71c2b72Yorke Lee if (header.visible && header.y <= y && header.y + header.height > y && 4242be2e46d936b70e4e05e26e207a979d4b71c2b72Yorke Lee x >= padding && padding + mHeaderWidth >= x) { 425e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee mHeaderTouched = true; 426e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee if (mScrollToSectionOnHeaderTouch && 427e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee ev.getAction() == MotionEvent.ACTION_DOWN) { 428d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng return smoothScrollToPartition(i); 429d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } else { 430d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng return true; 431d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 432d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 433d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 434d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 435d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 4360b8fc4af478f997bfc8bcaa785c9529ad8b797c7Yorke Lee return false; 437d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 438d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 439e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee @Override 440e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee public boolean onTouchEvent(MotionEvent ev) { 441e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee if (mHeaderTouched) { 442e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee if (ev.getAction() == MotionEvent.ACTION_UP) { 443e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee mHeaderTouched = false; 444e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee } 445e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee return true; 446e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee } 447e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee return super.onTouchEvent(ev); 448e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee }; 449e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee 450d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private boolean smoothScrollToPartition(int partition) { 451d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng final int position = mAdapter.getScrollPositionForHeader(partition); 452d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (position == -1) { 453d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng return false; 454d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 455d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 456d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int offset = 0; 457d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng for (int i = 0; i < partition; i++) { 458d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng PinnedHeader header = mHeaders[i]; 459d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.visible) { 460d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng offset += header.height; 461d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 462d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 463e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee smoothScrollToPositionFromTop(position + getHeaderViewsCount(), offset, 464e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee DEFAULT_SMOOTH_SCROLL_DURATION); 465d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng return true; 466d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 467d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 468d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private void invalidateIfAnimating() { 469d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mAnimating = false; 470d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng for (int i = 0; i < mSize; i++) { 471d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (mHeaders[i].animating) { 472d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mAnimating = true; 473d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng invalidate(); 474d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng return; 475d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 476d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 477d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 478d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 479d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng @Override 480d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng protected void dispatchDraw(Canvas canvas) { 481d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng long currentTime = mAnimating ? System.currentTimeMillis() : 0; 482d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 483d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int top = 0; 484d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int bottom = getBottom(); 485d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng boolean hasVisibleHeaders = false; 486d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng for (int i = 0; i < mSize; i++) { 487d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng PinnedHeader header = mHeaders[i]; 488d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.visible) { 489d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng hasVisibleHeaders = true; 490d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.state == BOTTOM && header.y < bottom) { 491d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng bottom = header.y; 492d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } else if (header.state == TOP || header.state == FADING) { 493d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int newTop = header.y + header.height; 494d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (newTop > top) { 495d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng top = newTop; 496d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 497d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 498d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 499d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 500d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 501d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (hasVisibleHeaders) { 502d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng canvas.save(); 503d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mClipRect.set(0, top, getWidth(), bottom); 504d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng canvas.clipRect(mClipRect); 505d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 506d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 507d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng super.dispatchDraw(canvas); 508d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 509d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (hasVisibleHeaders) { 510d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng canvas.restore(); 511d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 512d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng // First draw top headers, then the bottom ones to handle the Z axis correctly 513d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng for (int i = mSize; --i >= 0;) { 514d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng PinnedHeader header = mHeaders[i]; 515d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.visible && (header.state == TOP || header.state == FADING)) { 516d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng drawHeader(canvas, header, currentTime); 517d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 518d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 519d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 520d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng for (int i = 0; i < mSize; i++) { 521d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng PinnedHeader header = mHeaders[i]; 522d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.visible && header.state == BOTTOM) { 523d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng drawHeader(canvas, header, currentTime); 524d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 525d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 526d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 527d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 528d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng invalidateIfAnimating(); 529d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 530d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 531d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private void drawHeader(Canvas canvas, PinnedHeader header, long currentTime) { 532d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.animating) { 533d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int timeLeft = (int)(header.targetTime - currentTime); 534d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (timeLeft <= 0) { 535d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.y = header.targetY; 536d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.visible = header.targetVisible; 537d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.animating = false; 538d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } else { 539d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.y = header.targetY + (header.sourceY - header.targetY) * timeLeft 540d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng / mAnimationDuration; 541d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 542d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 543d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.visible) { 544d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng View view = header.view; 545d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int saveCount = canvas.save(); 5469f0c1621718b382531cbebfe38907e6d3db6ceaeFabrice Di Meglio canvas.translate(isLayoutRtl() ? 5479f0c1621718b382531cbebfe38907e6d3db6ceaeFabrice Di Meglio getWidth() - mHeaderPaddingStart - mHeaderWidth : mHeaderPaddingStart, 5489f0c1621718b382531cbebfe38907e6d3db6ceaeFabrice Di Meglio header.y); 549d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.state == FADING) { 550d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mBounds.set(0, 0, mHeaderWidth, view.getHeight()); 551d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng canvas.saveLayerAlpha(mBounds, header.alpha, Canvas.ALL_SAVE_FLAG); 552d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 553d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng view.draw(canvas); 554d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng canvas.restoreToCount(saveCount); 555d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 556d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 557d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng} 558