179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes/*
279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes * Copyright (C) 2006 The Android Open Source Project
379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes *
479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes * Licensed under the Apache License, Version 2.0 (the "License");
579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes * you may not use this file except in compliance with the License.
679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes * You may obtain a copy of the License at
779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes *
879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes *      http://www.apache.org/licenses/LICENSE-2.0
979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes *
1079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes * Unless required by applicable law or agreed to in writing, software
1179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes * distributed under the License is distributed on an "AS IS" BASIS,
1279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes * See the License for the specific language governing permissions and
1479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes * limitations under the License.
1579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes */
1679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
17da10fdd1400ecfd8d7f2e55651dd528d0614dfc5Jeff Brownpackage android.support.v7.internal.widget;
18da10fdd1400ecfd8d7f2e55651dd528d0614dfc5Jeff Brown
1979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banesimport android.content.Context;
2079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banesimport android.database.DataSetObserver;
2179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banesimport android.graphics.Rect;
2279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banesimport android.os.Parcel;
2379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banesimport android.os.Parcelable;
2479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banesimport android.util.AttributeSet;
2579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banesimport android.util.SparseArray;
2679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banesimport android.view.View;
2779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banesimport android.view.ViewGroup;
2879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banesimport android.widget.SpinnerAdapter;
2979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
3079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes/**
3179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes * An abstract base class for spinner widgets. SDK users will probably not
3279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes * need to use this class.
3379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes */
3479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banesabstract class AbsSpinnerICS extends AdapterViewICS<SpinnerAdapter> {
3579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    SpinnerAdapter mAdapter;
3679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
3779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    int mHeightMeasureSpec;
3879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    int mWidthMeasureSpec;
3979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    boolean mBlockLayoutRequests;
4079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
4179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    int mSelectionLeftPadding = 0;
4279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    int mSelectionTopPadding = 0;
4379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    int mSelectionRightPadding = 0;
4479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    int mSelectionBottomPadding = 0;
4579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    final Rect mSpinnerPadding = new Rect();
4679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
4779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    final RecycleBin mRecycler = new RecycleBin();
4879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    private DataSetObserver mDataSetObserver;
4979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
5079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    /** Temporary frame to hold a child View's frame rectangle */
5179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    private Rect mTouchFrame;
5279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
5379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    AbsSpinnerICS(Context context) {
5479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        super(context);
5579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        initAbsSpinner();
5679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    }
5779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
5879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    AbsSpinnerICS(Context context, AttributeSet attrs) {
5979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        this(context, attrs, 0);
6079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    }
6179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
6279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    AbsSpinnerICS(Context context, AttributeSet attrs, int defStyle) {
6379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        super(context, attrs, defStyle);
6479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        initAbsSpinner();
6579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    }
6679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
6779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    /**
6879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     * Common code for different constructor flavors
6979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     */
7079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    private void initAbsSpinner() {
7179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        setFocusable(true);
7279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        setWillNotDraw(false);
7379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    }
7479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
7579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    /**
7679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     * The Adapter is used to provide the data which backs this Spinner.
7779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     * It also provides methods to transform spinner items based on their position
7879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     * relative to the selected item.
7979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     * @param adapter The SpinnerAdapter to use for this Spinner
8079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     */
8179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    @Override
8279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    public void setAdapter(SpinnerAdapter adapter) {
8379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        if (null != mAdapter) {
8479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            mAdapter.unregisterDataSetObserver(mDataSetObserver);
8579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            resetList();
8679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        }
8779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
8879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        mAdapter = adapter;
8979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
9079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        mOldSelectedPosition = INVALID_POSITION;
9179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        mOldSelectedRowId = INVALID_ROW_ID;
9279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
9379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        if (mAdapter != null) {
9479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            mOldItemCount = mItemCount;
9579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            mItemCount = mAdapter.getCount();
9679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            checkFocus();
9779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
9879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            mDataSetObserver = new AdapterDataSetObserver();
9979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            mAdapter.registerDataSetObserver(mDataSetObserver);
10079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
10179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            int position = mItemCount > 0 ? 0 : INVALID_POSITION;
10279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
10379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            setSelectedPositionInt(position);
10479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            setNextSelectedPositionInt(position);
10579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
10679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            if (mItemCount == 0) {
10779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                // Nothing selected
10879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                checkSelectionChanged();
10979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            }
11079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
11179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        } else {
11279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            checkFocus();
11379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            resetList();
11479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            // Nothing selected
11579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            checkSelectionChanged();
11679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        }
11779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
11879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        requestLayout();
11979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    }
12079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
12179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    /**
12279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     * Clear out all children from the list
12379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     */
12479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    void resetList() {
12579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        mDataChanged = false;
12679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        mNeedSync = false;
12779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
12879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        removeAllViewsInLayout();
12979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        mOldSelectedPosition = INVALID_POSITION;
13079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        mOldSelectedRowId = INVALID_ROW_ID;
13179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
13279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        setSelectedPositionInt(INVALID_POSITION);
13379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        setNextSelectedPositionInt(INVALID_POSITION);
13479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        invalidate();
13579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    }
13679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
13779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    /**
13879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     * @see android.view.View#measure(int, int)
13979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     *
14079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     * Figure out the dimensions of this Spinner. The width comes from
14179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     * the widthMeasureSpec as Spinnners can't have their width set to
14279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     * UNSPECIFIED. The height is based on the height of the selected item
14379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     * plus padding.
14479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     */
14579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    @Override
14679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
14779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
14879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        int widthSize;
14979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        int heightSize;
15079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
15179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        final int paddingLeft = getPaddingLeft();
15279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        final int paddingTop = getPaddingTop();
15379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        final int paddingRight = getPaddingRight();
15479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        final int paddingBottom = getPaddingBottom();
15579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
15679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        mSpinnerPadding.left = paddingLeft > mSelectionLeftPadding ? paddingLeft
15779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                : mSelectionLeftPadding;
15879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        mSpinnerPadding.top = paddingTop > mSelectionTopPadding ? paddingTop
15979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                : mSelectionTopPadding;
16079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        mSpinnerPadding.right = paddingRight > mSelectionRightPadding ? paddingRight
16179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                : mSelectionRightPadding;
16279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        mSpinnerPadding.bottom = paddingBottom > mSelectionBottomPadding ? paddingBottom
16379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                : mSelectionBottomPadding;
16479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
16579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        if (mDataChanged) {
16679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            handleDataChanged();
16779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        }
16879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
16979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        int preferredHeight = 0;
17079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        int preferredWidth = 0;
17179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        boolean needsMeasuring = true;
17279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
17379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        int selectedPosition = getSelectedItemPosition();
17479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        if (selectedPosition >= 0 && mAdapter != null && selectedPosition < mAdapter.getCount()) {
17579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            // Try looking in the recycler. (Maybe we were measured once already)
17679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            View view = mRecycler.get(selectedPosition);
17779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            if (view == null) {
17879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                // Make a new one
17979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                view = mAdapter.getView(selectedPosition, null, this);
18079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            }
18179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
18279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            if (view != null) {
18379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                // Put in recycler for re-measuring and/or layout
18479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                mRecycler.put(selectedPosition, view);
18579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            }
18679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
18779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            if (view != null) {
18879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                if (view.getLayoutParams() == null) {
18979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                    mBlockLayoutRequests = true;
19079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                    view.setLayoutParams(generateDefaultLayoutParams());
19179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                    mBlockLayoutRequests = false;
19279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                }
19379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                measureChild(view, widthMeasureSpec, heightMeasureSpec);
19479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
19579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                preferredHeight = getChildHeight(view) + mSpinnerPadding.top + mSpinnerPadding.bottom;
19679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                preferredWidth = getChildWidth(view) + mSpinnerPadding.left + mSpinnerPadding.right;
19779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
19879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                needsMeasuring = false;
19979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            }
20079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        }
20179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
20279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        if (needsMeasuring) {
20379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            // No views -- just use padding
20479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            preferredHeight = mSpinnerPadding.top + mSpinnerPadding.bottom;
20579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            if (widthMode == MeasureSpec.UNSPECIFIED) {
20679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                preferredWidth = mSpinnerPadding.left + mSpinnerPadding.right;
20779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            }
20879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        }
20979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
21079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        preferredHeight = Math.max(preferredHeight, getSuggestedMinimumHeight());
21179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        preferredWidth = Math.max(preferredWidth, getSuggestedMinimumWidth());
21279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
21379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        heightSize = resolveSize(preferredHeight, heightMeasureSpec);
21479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        widthSize = resolveSize(preferredWidth, widthMeasureSpec);
21579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
21679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        setMeasuredDimension(widthSize, heightSize);
21779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        mHeightMeasureSpec = heightMeasureSpec;
21879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        mWidthMeasureSpec = widthMeasureSpec;
21979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    }
22079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
22179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    int getChildHeight(View child) {
22279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        return child.getMeasuredHeight();
22379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    }
22479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
22579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    int getChildWidth(View child) {
22679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        return child.getMeasuredWidth();
22779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    }
22879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
22979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    @Override
23079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
23179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        return new ViewGroup.LayoutParams(
23279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                ViewGroup.LayoutParams.MATCH_PARENT,
23379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                ViewGroup.LayoutParams.WRAP_CONTENT);
23479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    }
23579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
23679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    void recycleAllViews() {
23779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        final int childCount = getChildCount();
23879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        final AbsSpinnerICS.RecycleBin recycleBin = mRecycler;
23979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        final int position = mFirstPosition;
24079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
24179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        // All views go in recycler
24279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        for (int i = 0; i < childCount; i++) {
24379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            View v = getChildAt(i);
24479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            int index = position + i;
24579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            recycleBin.put(index, v);
24679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        }
24779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    }
24879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
24979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    /**
25079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     * Jump directly to a specific item in the adapter data.
25179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     */
25279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    public void setSelection(int position, boolean animate) {
25379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        // Animate only if requested position is already on screen somewhere
25479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        boolean shouldAnimate = animate && mFirstPosition <= position &&
25579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                position <= mFirstPosition + getChildCount() - 1;
25679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        setSelectionInt(position, shouldAnimate);
25779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    }
25879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
25979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    @Override
26079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    public void setSelection(int position) {
26179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        setNextSelectedPositionInt(position);
26279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        requestLayout();
26379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        invalidate();
26479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    }
26579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
26679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
26779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    /**
26879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     * Makes the item at the supplied position selected.
26979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     *
27079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     * @param position Position to select
27179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     * @param animate Should the transition be animated
27279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     *
27379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     */
27479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    void setSelectionInt(int position, boolean animate) {
27579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        if (position != mOldSelectedPosition) {
27679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            mBlockLayoutRequests = true;
27779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            int delta  = position - mSelectedPosition;
27879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            setNextSelectedPositionInt(position);
27979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            layout(delta, animate);
28079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            mBlockLayoutRequests = false;
28179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        }
28279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    }
28379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
28479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    abstract void layout(int delta, boolean animate);
28579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
28679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    @Override
28779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    public View getSelectedView() {
28879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        if (mItemCount > 0 && mSelectedPosition >= 0) {
28979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            return getChildAt(mSelectedPosition - mFirstPosition);
29079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        } else {
29179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            return null;
29279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        }
29379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    }
29479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
29579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    /**
29679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     * Override to prevent spamming ourselves with layout requests
29779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     * as we place views
29879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     *
29979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     * @see android.view.View#requestLayout()
30079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     */
30179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    @Override
30279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    public void requestLayout() {
30379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        if (!mBlockLayoutRequests) {
30479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            super.requestLayout();
30579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        }
30679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    }
30779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
30879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    @Override
30979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    public SpinnerAdapter getAdapter() {
31079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        return mAdapter;
31179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    }
31279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
31379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    @Override
31479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    public int getCount() {
31579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        return mItemCount;
31679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    }
31779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
31879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    /**
31979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     * Maps a point to a position in the list.
32079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     *
32179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     * @param x X in local coordinate
32279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     * @param y Y in local coordinate
32379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     * @return The position of the item which contains the specified point, or
32479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     *         {@link #INVALID_POSITION} if the point does not intersect an item.
32579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes     */
32679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    public int pointToPosition(int x, int y) {
32779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        Rect frame = mTouchFrame;
32879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        if (frame == null) {
32979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            mTouchFrame = new Rect();
33079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            frame = mTouchFrame;
33179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        }
33279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
33379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        final int count = getChildCount();
33479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        for (int i = count - 1; i >= 0; i--) {
33579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            View child = getChildAt(i);
33679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            if (child.getVisibility() == View.VISIBLE) {
33779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                child.getHitRect(frame);
33879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                if (frame.contains(x, y)) {
33979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                    return mFirstPosition + i;
34079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                }
34179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            }
34279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        }
34379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        return INVALID_POSITION;
34479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    }
34579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
34679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    static class SavedState extends BaseSavedState {
34779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        long selectedId;
34879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        int position;
34979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
35079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        /**
35179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes         * Constructor called from {@link AbsSpinnerICS#onSaveInstanceState()}
35279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes         */
35379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        SavedState(Parcelable superState) {
35479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            super(superState);
35579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        }
35679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
35779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        /**
35879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes         * Constructor called from {@link #CREATOR}
35979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes         */
36079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        private SavedState(Parcel in) {
36179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            super(in);
36279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            selectedId = in.readLong();
36379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            position = in.readInt();
36479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        }
36579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
36679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        @Override
36779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        public void writeToParcel(Parcel out, int flags) {
36879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            super.writeToParcel(out, flags);
36979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            out.writeLong(selectedId);
37079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            out.writeInt(position);
37179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        }
37279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
37379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        @Override
37479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        public String toString() {
37579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            return "AbsSpinner.SavedState{"
37679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                    + Integer.toHexString(System.identityHashCode(this))
37779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                    + " selectedId=" + selectedId
37879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                    + " position=" + position + "}";
37979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        }
38079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
38179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        public static final Parcelable.Creator<SavedState> CREATOR
38279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                = new Parcelable.Creator<SavedState>() {
38379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            public SavedState createFromParcel(Parcel in) {
38479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                return new SavedState(in);
38579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            }
38679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
38779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            public SavedState[] newArray(int size) {
38879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                return new SavedState[size];
38979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            }
39079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        };
39179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    }
39279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
39379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    @Override
39479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    public Parcelable onSaveInstanceState() {
39579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        Parcelable superState = super.onSaveInstanceState();
39679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        SavedState ss = new SavedState(superState);
39779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        ss.selectedId = getSelectedItemId();
39879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        if (ss.selectedId >= 0) {
39979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            ss.position = getSelectedItemPosition();
40079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        } else {
40179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            ss.position = INVALID_POSITION;
40279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        }
40379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        return ss;
40479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    }
40579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
40679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    @Override
40779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    public void onRestoreInstanceState(Parcelable state) {
40879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        SavedState ss = (SavedState) state;
40979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
41079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        super.onRestoreInstanceState(ss.getSuperState());
41179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
41279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        if (ss.selectedId >= 0) {
41379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            mDataChanged = true;
41479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            mNeedSync = true;
41579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            mSyncRowId = ss.selectedId;
41679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            mSyncPosition = ss.position;
41779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            mSyncMode = SYNC_SELECTED_POSITION;
41879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            requestLayout();
41979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        }
42079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    }
42179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
42279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    class RecycleBin {
42379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        private final SparseArray<View> mScrapHeap = new SparseArray<View>();
42479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
42579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        public void put(int position, View v) {
42679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            mScrapHeap.put(position, v);
42779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        }
42879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
42979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        View get(int position) {
43079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            // System.out.print("Looking for " + position);
43179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            View result = mScrapHeap.get(position);
43279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            if (result != null) {
43379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                // System.out.println(" HIT");
43479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                mScrapHeap.delete(position);
43579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            } else {
43679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                // System.out.println(" MISS");
43779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            }
43879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            return result;
43979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        }
44079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes
44179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        void clear() {
44279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            final SparseArray<View> scrapHeap = mScrapHeap;
44379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            final int count = scrapHeap.size();
44479e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            for (int i = 0; i < count; i++) {
44579e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                final View view = scrapHeap.valueAt(i);
44679e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                if (view != null) {
44779e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                    removeDetachedView(view, true);
44879e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes                }
44979e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            }
45079e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes            scrapHeap.clear();
45179e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes        }
45279e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes    }
45379e7a9ef79920c7daa10c90339db126a4c3c592eChris Banes}