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