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.RectF; 22d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.util.AttributeSet; 23d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.view.MotionEvent; 24d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.view.View; 25d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.view.ViewGroup; 26d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.widget.AbsListView; 27d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.widget.AbsListView.OnScrollListener; 28d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.widget.AdapterView; 29d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.widget.AdapterView.OnItemSelectedListener; 30d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengimport android.widget.ListAdapter; 31d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 32864c67691b5a0169b5cbb91469d67d93e24ad9ebYorke Leeimport com.android.contacts.common.util.ViewUtil; 33864c67691b5a0169b5cbb91469d67d93e24ad9ebYorke Lee 34d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng/** 35d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * A ListView that maintains a header pinned at the top of the list. The 36d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * pinned header can be pushed up and dissolved as needed. 37d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 38d6bba124836ec2f528b329759e38fda6297fec49Chiao Chengpublic class PinnedHeaderListView extends AutoScrollListView 39d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng implements OnScrollListener, OnItemSelectedListener { 40d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 41d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng /** 42d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Adapter interface. The list adapter must implement this interface. 43d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 44d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public interface PinnedHeaderAdapter { 45d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 46d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng /** 47d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Returns the overall number of pinned headers, visible or not. 48d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 49d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int getPinnedHeaderCount(); 50d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 51d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng /** 52d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Creates or updates the pinned header view. 53d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 54d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng View getPinnedHeaderView(int viewIndex, View convertView, ViewGroup parent); 55d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 56d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng /** 57d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Configures the pinned headers to match the visible list items. The 58d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * adapter should call {@link PinnedHeaderListView#setHeaderPinnedAtTop}, 59d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * {@link PinnedHeaderListView#setHeaderPinnedAtBottom}, 60d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * {@link PinnedHeaderListView#setFadingHeader} or 61d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * {@link PinnedHeaderListView#setHeaderInvisible}, for each header that 62d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * needs to change its position or visibility. 63d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 64d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng void configurePinnedHeaders(PinnedHeaderListView listView); 65d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 66d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng /** 67d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Returns the list position to scroll to if the pinned header is touched. 68d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Return -1 if the list does not need to be scrolled. 69d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 70d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int getScrollPositionForHeader(int viewIndex); 71d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 72d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 73d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private static final int MAX_ALPHA = 255; 74d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private static final int TOP = 0; 75d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private static final int BOTTOM = 1; 76d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private static final int FADING = 2; 77d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 78d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private static final int DEFAULT_ANIMATION_DURATION = 20; 79d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 80e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee private static final int DEFAULT_SMOOTH_SCROLL_DURATION = 100; 81e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee 82d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private static final class PinnedHeader { 83d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng View view; 84d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng boolean visible; 85d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int y; 86d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int height; 87d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int alpha; 88d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int state; 89d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 90d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng boolean animating; 91d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng boolean targetVisible; 92d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int sourceY; 93d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int targetY; 94d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng long targetTime; 95d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 96d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 97d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private PinnedHeaderAdapter mAdapter; 98d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private int mSize; 99d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private PinnedHeader[] mHeaders; 100d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private RectF mBounds = new RectF(); 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 @Override 136d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public void setAdapter(ListAdapter adapter) { 137d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mAdapter = (PinnedHeaderAdapter)adapter; 138d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng super.setAdapter(adapter); 139d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 140d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 141d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng @Override 142d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public void setOnScrollListener(OnScrollListener onScrollListener) { 143d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mOnScrollListener = onScrollListener; 144d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng super.setOnScrollListener(this); 145d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 146d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 147d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng @Override 148d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public void setOnItemSelectedListener(OnItemSelectedListener listener) { 149d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mOnItemSelectedListener = listener; 150d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng super.setOnItemSelectedListener(this); 151d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 152d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 153e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee public void setScrollToSectionOnHeaderTouch(boolean value) { 154e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee mScrollToSectionOnHeaderTouch = value; 155e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee } 156e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee 157d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng @Override 158d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, 159d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int totalItemCount) { 160d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (mAdapter != null) { 161d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int count = mAdapter.getPinnedHeaderCount(); 162d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (count != mSize) { 163d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mSize = count; 164d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (mHeaders == null) { 165d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mHeaders = new PinnedHeader[mSize]; 166d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } else if (mHeaders.length < mSize) { 167d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng PinnedHeader[] headers = mHeaders; 168d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mHeaders = new PinnedHeader[mSize]; 169d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng System.arraycopy(headers, 0, mHeaders, 0, headers.length); 170d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 171d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 172d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 173d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng for (int i = 0; i < mSize; i++) { 174d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (mHeaders[i] == null) { 175d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mHeaders[i] = new PinnedHeader(); 176d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 177d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mHeaders[i].view = mAdapter.getPinnedHeaderView(i, mHeaders[i].view, this); 178d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 179d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 180d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mAnimationTargetTime = System.currentTimeMillis() + mAnimationDuration; 181d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mAdapter.configurePinnedHeaders(this); 182d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng invalidateIfAnimating(); 183d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 184d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (mOnScrollListener != null) { 185d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mOnScrollListener.onScroll(this, firstVisibleItem, visibleItemCount, totalItemCount); 186d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 187d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 188d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 189d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng @Override 190d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng protected float getTopFadingEdgeStrength() { 191d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng // Disable vertical fading at the top when the pinned header is present 192d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng return mSize > 0 ? 0 : super.getTopFadingEdgeStrength(); 193d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 194d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 195d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng @Override 196d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public void onScrollStateChanged(AbsListView view, int scrollState) { 197d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mScrollState = scrollState; 198d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (mOnScrollListener != null) { 199d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mOnScrollListener.onScrollStateChanged(this, scrollState); 200d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 201d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 202d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 203d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng /** 204d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Ensures that the selected item is positioned below the top-pinned headers 205d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * and above the bottom-pinned ones. 206d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 207d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng @Override 208d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 209d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int height = getHeight(); 210d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 211d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int windowTop = 0; 212d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int windowBottom = height; 213d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 214d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng for (int i = 0; i < mSize; i++) { 215d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng PinnedHeader header = mHeaders[i]; 216d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.visible) { 217d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.state == TOP) { 218d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng windowTop = header.y + header.height; 219d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } else if (header.state == BOTTOM) { 220d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng windowBottom = header.y; 221d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng break; 222d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 223d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 224d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 225d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 226d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng View selectedView = getSelectedView(); 227d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (selectedView != null) { 228d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (selectedView.getTop() < windowTop) { 229d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng setSelectionFromTop(position, windowTop); 230d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } else if (selectedView.getBottom() > windowBottom) { 231d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng setSelectionFromTop(position, windowBottom - selectedView.getHeight()); 232d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 233d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 234d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 235d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (mOnItemSelectedListener != null) { 236d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mOnItemSelectedListener.onItemSelected(parent, view, position, id); 237d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 238d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 239d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 240d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng @Override 241d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public void onNothingSelected(AdapterView<?> parent) { 242d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (mOnItemSelectedListener != null) { 243d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mOnItemSelectedListener.onNothingSelected(parent); 244d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 245d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 246d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 247d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public int getPinnedHeaderHeight(int viewIndex) { 248d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng ensurePinnedHeaderLayout(viewIndex); 249d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng return mHeaders[viewIndex].view.getHeight(); 250d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 251d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 252d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng /** 253d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Set header to be pinned at the top. 254d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * 255d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * @param viewIndex index of the header view 256d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * @param y is position of the header in pixels. 257d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * @param animate true if the transition to the new coordinate should be animated 258d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 259d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public void setHeaderPinnedAtTop(int viewIndex, int y, boolean animate) { 260d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng ensurePinnedHeaderLayout(viewIndex); 261d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng PinnedHeader header = mHeaders[viewIndex]; 262d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.visible = true; 263d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.y = y; 264d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.state = TOP; 265d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 266d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng // TODO perhaps we should animate at the top as well 267d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.animating = false; 268d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 269d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 270d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng /** 271d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Set header to be pinned at the bottom. 272d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * 273d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * @param viewIndex index of the header view 274d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * @param y is position of the header in pixels. 275d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * @param animate true if the transition to the new coordinate should be animated 276d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 277d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public void setHeaderPinnedAtBottom(int viewIndex, int y, boolean animate) { 278d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng ensurePinnedHeaderLayout(viewIndex); 279d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng PinnedHeader header = mHeaders[viewIndex]; 280d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.state = BOTTOM; 281d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.animating) { 282d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.targetTime = mAnimationTargetTime; 283d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.sourceY = header.y; 284d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.targetY = y; 285d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } else if (animate && (header.y != y || !header.visible)) { 286d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.visible) { 287d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.sourceY = header.y; 288d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } else { 289d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.visible = true; 290d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.sourceY = y + header.height; 291d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 292d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.animating = true; 293d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.targetVisible = true; 294d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.targetTime = mAnimationTargetTime; 295d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.targetY = y; 296d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } else { 297d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.visible = true; 298d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.y = y; 299d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 300d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 301d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 302d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng /** 303d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Set header to be pinned at the top of the first visible item. 304d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * 305d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * @param viewIndex index of the header view 306d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * @param position is position of the header in pixels. 307d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 308d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public void setFadingHeader(int viewIndex, int position, boolean fade) { 309d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng ensurePinnedHeaderLayout(viewIndex); 310d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 311d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng View child = getChildAt(position - getFirstVisiblePosition()); 312d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (child == null) return; 313d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 314d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng PinnedHeader header = mHeaders[viewIndex]; 315d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.visible = true; 316d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.state = FADING; 317d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.alpha = MAX_ALPHA; 318d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.animating = false; 319d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 320d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int top = getTotalTopPinnedHeaderHeight(); 321d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.y = top; 322d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (fade) { 323d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int bottom = child.getBottom() - top; 324d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int headerHeight = header.height; 325d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (bottom < headerHeight) { 326d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int portion = bottom - headerHeight; 327d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.alpha = MAX_ALPHA * (headerHeight + portion) / headerHeight; 328d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.y = top + portion; 329d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 330d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 331d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 332d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 333d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng /** 334d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Makes header invisible. 335d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * 336d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * @param viewIndex index of the header view 337d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * @param animate true if the transition to the new coordinate should be animated 338d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 339d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public void setHeaderInvisible(int viewIndex, boolean animate) { 340d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng PinnedHeader header = mHeaders[viewIndex]; 341d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.visible && (animate || header.animating) && header.state == BOTTOM) { 342d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.sourceY = header.y; 343d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (!header.animating) { 344d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.visible = true; 345d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.targetY = getBottom() + header.height; 346d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 347d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.animating = true; 348d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.targetTime = mAnimationTargetTime; 349d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.targetVisible = false; 350d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } else { 351d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.visible = false; 352d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 353d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 354d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 355d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private void ensurePinnedHeaderLayout(int viewIndex) { 356d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng View view = mHeaders[viewIndex].view; 357d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (view.isLayoutRequested()) { 358d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); 3591f1e90d36fe914a7ce51e6f9884199580115d891Andrew Lee int widthSpec; 3601f1e90d36fe914a7ce51e6f9884199580115d891Andrew Lee int heightSpec; 3611f1e90d36fe914a7ce51e6f9884199580115d891Andrew Lee 3621f1e90d36fe914a7ce51e6f9884199580115d891Andrew Lee if (layoutParams != null && layoutParams.width > 0) { 3631f1e90d36fe914a7ce51e6f9884199580115d891Andrew Lee widthSpec = View.MeasureSpec 3641f1e90d36fe914a7ce51e6f9884199580115d891Andrew Lee .makeMeasureSpec(layoutParams.width, View.MeasureSpec.EXACTLY); 3651f1e90d36fe914a7ce51e6f9884199580115d891Andrew Lee } else { 3661f1e90d36fe914a7ce51e6f9884199580115d891Andrew Lee widthSpec = View.MeasureSpec 3671f1e90d36fe914a7ce51e6f9884199580115d891Andrew Lee .makeMeasureSpec(mHeaderWidth, View.MeasureSpec.EXACTLY); 3681f1e90d36fe914a7ce51e6f9884199580115d891Andrew Lee } 3691f1e90d36fe914a7ce51e6f9884199580115d891Andrew Lee 370d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (layoutParams != null && layoutParams.height > 0) { 371d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng heightSpec = View.MeasureSpec 372d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng .makeMeasureSpec(layoutParams.height, View.MeasureSpec.EXACTLY); 373d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } else { 374d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); 375d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 376d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng view.measure(widthSpec, heightSpec); 377d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int height = view.getMeasuredHeight(); 378d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mHeaders[viewIndex].height = height; 3791f1e90d36fe914a7ce51e6f9884199580115d891Andrew Lee view.layout(0, 0, view.getMeasuredWidth(), height); 380d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 381d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 382d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 383d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng /** 384d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Returns the sum of heights of headers pinned to the top. 385d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 386d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public int getTotalTopPinnedHeaderHeight() { 387d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng for (int i = mSize; --i >= 0;) { 388d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng PinnedHeader header = mHeaders[i]; 389d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.visible && header.state == TOP) { 390d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng return header.y + header.height; 391d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 392d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 393d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng return 0; 394d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 395d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 396d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng /** 397d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng * Returns the list item position at the specified y coordinate. 398d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng */ 399d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public int getPositionAt(int y) { 400d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng do { 401d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int position = pointToPosition(getPaddingLeft() + 1, y); 402d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (position != -1) { 403d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng return position; 404d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 405d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng // If position == -1, we must have hit a separator. Let's examine 406d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng // a nearby pixel 407d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng y--; 408d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } while (y > 0); 409d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng return 0; 410d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 411d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 412d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng @Override 413d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng public boolean onInterceptTouchEvent(MotionEvent ev) { 414e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee mHeaderTouched = false; 4150b8fc4af478f997bfc8bcaa785c9529ad8b797c7Yorke Lee if (super.onInterceptTouchEvent(ev)) { 4160b8fc4af478f997bfc8bcaa785c9529ad8b797c7Yorke Lee return true; 4170b8fc4af478f997bfc8bcaa785c9529ad8b797c7Yorke Lee } 4180b8fc4af478f997bfc8bcaa785c9529ad8b797c7Yorke Lee 419d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (mScrollState == SCROLL_STATE_IDLE) { 420d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng final int y = (int)ev.getY(); 4212be2e46d936b70e4e05e26e207a979d4b71c2b72Yorke Lee final int x = (int)ev.getX(); 422d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng for (int i = mSize; --i >= 0;) { 423d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng PinnedHeader header = mHeaders[i]; 4242be2e46d936b70e4e05e26e207a979d4b71c2b72Yorke Lee // For RTL layouts, this also takes into account that the scrollbar is on the left 4252be2e46d936b70e4e05e26e207a979d4b71c2b72Yorke Lee // side. 4262be2e46d936b70e4e05e26e207a979d4b71c2b72Yorke Lee final int padding = getPaddingLeft(); 4272be2e46d936b70e4e05e26e207a979d4b71c2b72Yorke Lee if (header.visible && header.y <= y && header.y + header.height > y && 4281f1e90d36fe914a7ce51e6f9884199580115d891Andrew Lee x >= padding && padding + header.view.getWidth() >= x) { 429e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee mHeaderTouched = true; 430e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee if (mScrollToSectionOnHeaderTouch && 431e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee ev.getAction() == MotionEvent.ACTION_DOWN) { 432d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng return smoothScrollToPartition(i); 433d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } else { 434d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng return true; 435d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 436d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 437d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 438d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 439d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 4400b8fc4af478f997bfc8bcaa785c9529ad8b797c7Yorke Lee return false; 441d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 442d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 443e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee @Override 444e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee public boolean onTouchEvent(MotionEvent ev) { 445e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee if (mHeaderTouched) { 446e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee if (ev.getAction() == MotionEvent.ACTION_UP) { 447e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee mHeaderTouched = false; 448e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee } 449e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee return true; 450e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee } 451e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee return super.onTouchEvent(ev); 452e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee }; 453e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee 454d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private boolean smoothScrollToPartition(int partition) { 4558f0afaec5442f24cf4106f40118ef6b3cc44c7aeJay Shrauner if (mAdapter == null) { 4568f0afaec5442f24cf4106f40118ef6b3cc44c7aeJay Shrauner return false; 4578f0afaec5442f24cf4106f40118ef6b3cc44c7aeJay Shrauner } 458d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng final int position = mAdapter.getScrollPositionForHeader(partition); 459d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (position == -1) { 460d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng return false; 461d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 462d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 463d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int offset = 0; 464d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng for (int i = 0; i < partition; i++) { 465d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng PinnedHeader header = mHeaders[i]; 466d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.visible) { 467d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng offset += header.height; 468d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 469d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 470e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee smoothScrollToPositionFromTop(position + getHeaderViewsCount(), offset, 471e93d60921db4a6909ebfaf7214986bc2f3634a23Yorke Lee DEFAULT_SMOOTH_SCROLL_DURATION); 472d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng return true; 473d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 474d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 475d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private void invalidateIfAnimating() { 476d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mAnimating = false; 477d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng for (int i = 0; i < mSize; i++) { 478d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (mHeaders[i].animating) { 479d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng mAnimating = true; 480d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng invalidate(); 481d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng return; 482d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 483d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 484d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 485d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 486d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng @Override 487d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng protected void dispatchDraw(Canvas canvas) { 488d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng long currentTime = mAnimating ? System.currentTimeMillis() : 0; 489d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 490d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int top = 0; 4911f1e90d36fe914a7ce51e6f9884199580115d891Andrew Lee int right = 0; 492d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int bottom = getBottom(); 493d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng boolean hasVisibleHeaders = false; 494d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng for (int i = 0; i < mSize; i++) { 495d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng PinnedHeader header = mHeaders[i]; 496d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.visible) { 497d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng hasVisibleHeaders = true; 498d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.state == BOTTOM && header.y < bottom) { 499d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng bottom = header.y; 500d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } else if (header.state == TOP || header.state == FADING) { 501d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int newTop = header.y + header.height; 502d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (newTop > top) { 503d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng top = newTop; 504d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 505d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 506d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 507d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 508d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 509d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (hasVisibleHeaders) { 510d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng canvas.save(); 511d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 512d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 513d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng super.dispatchDraw(canvas); 514d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 515d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (hasVisibleHeaders) { 516d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng canvas.restore(); 517d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 51854140f73445acb7f2c36b5ced72fb185a3f99f0aYorke Lee // If the first item is visible and if it has a positive top that is greater than the 51954140f73445acb7f2c36b5ced72fb185a3f99f0aYorke Lee // first header's assigned y-value, use that for the first header's y value. This way, 52054140f73445acb7f2c36b5ced72fb185a3f99f0aYorke Lee // the header inherits any padding applied to the list view. 52126813c713cf99e25b4fb8e08bd8b5868931bcde1Andrew Lee if (mSize > 0 && getFirstVisiblePosition() == 0) { 52226813c713cf99e25b4fb8e08bd8b5868931bcde1Andrew Lee View firstChild = getChildAt(0); 52326813c713cf99e25b4fb8e08bd8b5868931bcde1Andrew Lee PinnedHeader firstHeader = mHeaders[0]; 52426813c713cf99e25b4fb8e08bd8b5868931bcde1Andrew Lee 52526813c713cf99e25b4fb8e08bd8b5868931bcde1Andrew Lee if (firstHeader != null) { 5260cc08556bbf7ba8f3d2b98fff407408582dbcdddAndrew Lee int firstHeaderTop = firstChild != null ? firstChild.getTop() : 0; 52754140f73445acb7f2c36b5ced72fb185a3f99f0aYorke Lee firstHeader.y = Math.max(firstHeader.y, firstHeaderTop); 52826813c713cf99e25b4fb8e08bd8b5868931bcde1Andrew Lee } 52926813c713cf99e25b4fb8e08bd8b5868931bcde1Andrew Lee } 53026813c713cf99e25b4fb8e08bd8b5868931bcde1Andrew Lee 531d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng // First draw top headers, then the bottom ones to handle the Z axis correctly 532d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng for (int i = mSize; --i >= 0;) { 533d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng PinnedHeader header = mHeaders[i]; 534d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.visible && (header.state == TOP || header.state == FADING)) { 535d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng drawHeader(canvas, header, currentTime); 536d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 537d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 538d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 539d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng for (int i = 0; i < mSize; i++) { 540d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng PinnedHeader header = mHeaders[i]; 541d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.visible && header.state == BOTTOM) { 542d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng drawHeader(canvas, header, currentTime); 543d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 544d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 545d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 546d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 547d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng invalidateIfAnimating(); 548d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 549d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng 550d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng private void drawHeader(Canvas canvas, PinnedHeader header, long currentTime) { 551d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.animating) { 552d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int timeLeft = (int)(header.targetTime - currentTime); 553d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (timeLeft <= 0) { 554d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.y = header.targetY; 555d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.visible = header.targetVisible; 556d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.animating = false; 557d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } else { 558d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng header.y = header.targetY + (header.sourceY - header.targetY) * timeLeft 559d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng / mAnimationDuration; 560d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 561d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 562d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.visible) { 563d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng View view = header.view; 564d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng int saveCount = canvas.save(); 5651f1e90d36fe914a7ce51e6f9884199580115d891Andrew Lee int translateX = ViewUtil.isViewLayoutRtl(this) ? 56626813c713cf99e25b4fb8e08bd8b5868931bcde1Andrew Lee getWidth() - mHeaderPaddingStart - view.getWidth() : 5671f1e90d36fe914a7ce51e6f9884199580115d891Andrew Lee mHeaderPaddingStart; 5681f1e90d36fe914a7ce51e6f9884199580115d891Andrew Lee canvas.translate(translateX, header.y); 569d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng if (header.state == FADING) { 57026813c713cf99e25b4fb8e08bd8b5868931bcde1Andrew Lee mBounds.set(0, 0, view.getWidth(), view.getHeight()); 571d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng canvas.saveLayerAlpha(mBounds, header.alpha, Canvas.ALL_SAVE_FLAG); 572d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 573d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng view.draw(canvas); 574d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng canvas.restoreToCount(saveCount); 575d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 576d6bba124836ec2f528b329759e38fda6297fec49Chiao Cheng } 5778f0afaec5442f24cf4106f40118ef6b3cc44c7aeJay Shrauner} 578