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