TabWidget.java revision cd2ca4038a027315832c38c68be5076000bc4b53
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2006 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.widget;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1961c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guyimport android.R;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
217609764295d1b3ec0b53d1ae536ee0280f5e0407Mike Cleronimport android.content.res.Resources;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.TypedArray;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Canvas;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Rect;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.drawable.Drawable;
267609764295d1b3ec0b53d1ae536ee0280f5e0407Mike Cleronimport android.os.Build;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.AttributeSet;
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.View;
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.View.OnFocusChangeListener;
3097dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunneimport android.view.ViewGroup;
315ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganovimport android.view.accessibility.AccessibilityEvent;
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
343730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar *
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Displays a list of tab labels representing each page in the parent's tab
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * collection. The container object for this widget is
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * {@link android.widget.TabHost TabHost}. When the user selects a tab, this
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * object sends a message to the parent container, TabHost, to tell it to switch
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the displayed page. You typically won't use many methods directly on this
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * object. The container TabHost is used to add labels, add the callback
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * handler, and manage callbacks. You might call this object to iterate the list
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * of tabs, or to tweak the layout of the tab list, but most methods should be
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * called on the containing TabHost object.
4441ec65355bd6ded652769725b276d47c54a0d913Scott Main *
4541ec65355bd6ded652769725b276d47c54a0d913Scott Main * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-tabwidget.html">Tab Layout
4641ec65355bd6ded652769725b276d47c54a0d913Scott Main * tutorial</a>.</p>
477883c975dd0db021fa3a01226d6cb09c28764c98Romain Guy *
487883c975dd0db021fa3a01226d6cb09c28764c98Romain Guy * @attr ref android.R.styleable#TabWidget_divider
496b1e6969828f05a2f5f55825fdf16b19c9dce2ddRomain Guy * @attr ref android.R.styleable#TabWidget_tabStripEnabled
506b1e6969828f05a2f5f55825fdf16b19c9dce2ddRomain Guy * @attr ref android.R.styleable#TabWidget_tabStripLeft
516b1e6969828f05a2f5f55825fdf16b19c9dce2ddRomain Guy * @attr ref android.R.styleable#TabWidget_tabStripRight
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class TabWidget extends LinearLayout implements OnFocusChangeListener {
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private OnTabSelectionChanged mSelectionChangedListener;
5561c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy
5697dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne    // This value will be set to 0 as soon as the first tab is added to TabHost.
5797dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne    private int mSelectedTab = -1;
5861c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy
5965fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy    private Drawable mLeftStrip;
6065fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy    private Drawable mRightStrip;
6161c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy
6261c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    private boolean mDrawBottomStrips = true;
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mStripMoved;
6461c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy
6553175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    private Drawable mDividerDrawable;
6661c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy
6761c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    private final Rect mBounds = new Rect();
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
69c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne    // When positive, the widths and heights of tabs will be imposed so that they fit in parent
70c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne    private int mImposedTabsHeight = -1;
71c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne    private int[] mImposedTabWidths;
72c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public TabWidget(Context context) {
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        this(context, null);
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public TabWidget(Context context, AttributeSet attrs) {
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        this(context, attrs, com.android.internal.R.attr.tabWidgetStyle);
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public TabWidget(Context context, AttributeSet attrs, int defStyle) {
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super(context, attrs);
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
843730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar        TypedArray a =
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.TabWidget,
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    defStyle, 0);
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
886b1e6969828f05a2f5f55825fdf16b19c9dce2ddRomain Guy        mDrawBottomStrips = a.getBoolean(R.styleable.TabWidget_tabStripEnabled, true);
8961c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy        mDividerDrawable = a.getDrawable(R.styleable.TabWidget_divider);
906b1e6969828f05a2f5f55825fdf16b19c9dce2ddRomain Guy        mLeftStrip = a.getDrawable(R.styleable.TabWidget_tabStripLeft);
916b1e6969828f05a2f5f55825fdf16b19c9dce2ddRomain Guy        mRightStrip = a.getDrawable(R.styleable.TabWidget_tabStripRight);
9261c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        a.recycle();
9461c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy
9561c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy        initTabWidget();
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
973730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mStripMoved = true;
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super.onSizeChanged(w, h, oldw, oldh);
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1043730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar    @Override
1053730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar    protected int getChildDrawingOrder(int childCount, int i) {
10697dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne        if (mSelectedTab == -1) {
1073730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar            return i;
10897dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne        } else {
10997dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne            // Always draw the selected tab last, so that drop shadows are drawn
11097dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne            // in the correct z-order.
11197dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne            if (i == childCount - 1) {
11297dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne                return mSelectedTab;
11397dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne            } else if (i >= mSelectedTab) {
11497dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne                return i + 1;
11597dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne            } else {
11697dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne                return i;
11797dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne            }
1183730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar        }
1193730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar    }
1203730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void initTabWidget() {
1223730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar        mGroupFlags |= FLAG_USE_CHILD_DRAWING_ORDER;
1233730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar
1247609764295d1b3ec0b53d1ae536ee0280f5e0407Mike Cleron        final Context context = mContext;
1257609764295d1b3ec0b53d1ae536ee0280f5e0407Mike Cleron        final Resources resources = context.getResources();
12644c1473c03a0021224fa94af9b1e3579ec244b12Gilles Debunne
12744c1473c03a0021224fa94af9b1e3579ec244b12Gilles Debunne        // Tests the target Sdk version, as set in the Manifest. Could not be set using styles.xml
12844c1473c03a0021224fa94af9b1e3579ec244b12Gilles Debunne        // in a values-v? directory which targets the current platform Sdk version instead.
1297609764295d1b3ec0b53d1ae536ee0280f5e0407Mike Cleron        if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.DONUT) {
1307609764295d1b3ec0b53d1ae536ee0280f5e0407Mike Cleron            // Donut apps get old color scheme
13165fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy            if (mLeftStrip == null) {
13265fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy                mLeftStrip = resources.getDrawable(
13361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy                        com.android.internal.R.drawable.tab_bottom_left_v4);
13461c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy            }
13565fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy            if (mRightStrip == null) {
13665fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy                mRightStrip = resources.getDrawable(
13761c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy                        com.android.internal.R.drawable.tab_bottom_right_v4);
13861c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy            }
1391ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn        } else {
1401ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn            // Use modern color scheme for Eclair and beyond
1411ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn            if (mLeftStrip == null) {
1421ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn                mLeftStrip = resources.getDrawable(
1431ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn                        com.android.internal.R.drawable.tab_bottom_left);
1441ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn            }
1451ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn            if (mRightStrip == null) {
1461ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn                mRightStrip = resources.getDrawable(
1471ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn                        com.android.internal.R.drawable.tab_bottom_right);
1481ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn            }
1491ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn         }
1507609764295d1b3ec0b53d1ae536ee0280f5e0407Mike Cleron
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Deal with focus, as we don't want the focus to go by default
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // to a tab other than the current tab
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        setFocusable(true);
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        setOnFocusChangeListener(this);
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
157c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne    @Override
158c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne    void measureChildBeforeLayout(View child, int childIndex,
159c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne            int widthMeasureSpec, int totalWidth,
160c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne            int heightMeasureSpec, int totalHeight) {
161c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne
162c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne        if (mImposedTabsHeight >= 0) {
163c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne            widthMeasureSpec = MeasureSpec.makeMeasureSpec(
164c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    totalWidth + mImposedTabWidths[childIndex], MeasureSpec.EXACTLY);
165c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne            heightMeasureSpec = MeasureSpec.makeMeasureSpec(mImposedTabsHeight,
166c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    MeasureSpec.EXACTLY);
167c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne        }
168c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne
169c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne        super.measureChildBeforeLayout(child, childIndex,
170c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                widthMeasureSpec, totalWidth, heightMeasureSpec, totalHeight);
171c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne    }
172c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne
1736741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne    @Override
1746741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne    void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) {
17552a5e6588395c9cea128d245a2572b7d69bbb12cGilles Debunne        if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.UNSPECIFIED) {
17652a5e6588395c9cea128d245a2572b7d69bbb12cGilles Debunne            super.measureHorizontal(widthMeasureSpec, heightMeasureSpec);
17752a5e6588395c9cea128d245a2572b7d69bbb12cGilles Debunne            return;
17852a5e6588395c9cea128d245a2572b7d69bbb12cGilles Debunne        }
17952a5e6588395c9cea128d245a2572b7d69bbb12cGilles Debunne
180c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne        // First, measure with no constraint
1816741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne        final int unspecifiedWidth = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
182c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne        mImposedTabsHeight = -1;
183cd59febcea2b75ebe657786133dcfa81fc3d5eb1Gilles Debunne        super.measureHorizontal(unspecifiedWidth, heightMeasureSpec);
1846741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne
185c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne        int extraWidth = getMeasuredWidth() - MeasureSpec.getSize(widthMeasureSpec);
186c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne        if (extraWidth > 0) {
187c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne            final int count = getChildCount();
1886741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne
189c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne            int childCount = 0;
1906741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne            for (int i = 0; i < count; i++) {
1916741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne                final View child = getChildAt(i);
192c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                if (child.getVisibility() == GONE) continue;
193c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                childCount++;
194c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne            }
1956741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne
196c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne            if (childCount > 0) {
197c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                if (mImposedTabWidths == null || mImposedTabWidths.length != count) {
198c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    mImposedTabWidths = new int[count];
199c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                }
200c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                for (int i = 0; i < count; i++) {
201c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    final View child = getChildAt(i);
202c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    if (child.getVisibility() == GONE) continue;
203c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    final int childWidth = child.getMeasuredWidth();
204c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    final int delta = extraWidth / childCount;
205c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    final int newWidth = Math.max(0, childWidth - delta);
206c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    mImposedTabWidths[i] = newWidth;
207c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    // Make sure the extra width is evenly distributed, no int division remainder
208c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    extraWidth -= childWidth - newWidth; // delta may have been clamped
209c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    childCount--;
210c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                    mImposedTabsHeight = Math.max(mImposedTabsHeight, child.getMeasuredHeight());
211c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne                }
2126741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne            }
213c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne        }
214c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne
215c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne        // Measure again, this time with imposed tab widths and respecting initial spec request
21652a5e6588395c9cea128d245a2572b7d69bbb12cGilles Debunne        super.measureHorizontal(widthMeasureSpec, heightMeasureSpec);
2176741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne    }
2186741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne
2196741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne    /**
22053175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * Returns the tab indicator view at the given index.
22153175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     *
22253175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * @param index the zero-based index of the tab indicator view to return
22353175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * @return the tab indicator view at the given index
22453175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     */
22553175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    public View getChildTabViewAt(int index) {
22653175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        // If we are using dividers, then instead of tab views at 0, 1, 2, ...
22753175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        // we have tab views at 0, 2, 4, ...
22853175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        if (mDividerDrawable != null) {
22953175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra            index *= 2;
23053175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        }
23153175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        return getChildAt(index);
23253175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    }
23353175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra
23453175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    /**
23553175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * Returns the number of tab indicator views.
23653175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * @return the number of tab indicator views.
23753175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     */
23853175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    public int getTabCount() {
23953175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        int children = getChildCount();
24053175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra
24153175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        // If we have dividers, then we will always have an odd number of
24253175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        // children: 1, 3, 5, ... and we want to convert that sequence to
24353175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        // this: 1, 2, 3, ...
24453175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        if (mDividerDrawable != null) {
24553175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra            children = (children + 1) / 2;
24653175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        }
24753175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        return children;
24853175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    }
24953175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra
25053175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    /**
25153175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * Sets the drawable to use as a divider between the tab indicators.
25253175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * @param drawable the divider drawable
25353175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     */
2546741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne    @Override
25553175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    public void setDividerDrawable(Drawable drawable) {
25653175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        mDividerDrawable = drawable;
25742c79880b0c19dfbcd8589d89d35fcedb1a7c9daRomain Guy        requestLayout();
25842c79880b0c19dfbcd8589d89d35fcedb1a7c9daRomain Guy        invalidate();
25953175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    }
26053175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra
26153175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    /**
26253175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * Sets the drawable to use as a divider between the tab indicators.
26353175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * @param resId the resource identifier of the drawable to use as a
26453175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * divider.
26553175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     */
26653175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    public void setDividerDrawable(int resId) {
26753175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        mDividerDrawable = mContext.getResources().getDrawable(resId);
26842c79880b0c19dfbcd8589d89d35fcedb1a7c9daRomain Guy        requestLayout();
26942c79880b0c19dfbcd8589d89d35fcedb1a7c9daRomain Guy        invalidate();
27053175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    }
27161c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy
27261c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    /**
27361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * Sets the drawable to use as the left part of the strip below the
27461c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * tab indicators.
27561c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * @param drawable the left strip drawable
27661c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     */
27761c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    public void setLeftStripDrawable(Drawable drawable) {
27865fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy        mLeftStrip = drawable;
27942c79880b0c19dfbcd8589d89d35fcedb1a7c9daRomain Guy        requestLayout();
28042c79880b0c19dfbcd8589d89d35fcedb1a7c9daRomain Guy        invalidate();
28161c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    }
28261c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy
28361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    /**
28461c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * Sets the drawable to use as the left part of the strip below the
28561c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * tab indicators.
28661c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * @param resId the resource identifier of the drawable to use as the
28761c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * left strip drawable
28861c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     */
28961c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    public void setLeftStripDrawable(int resId) {
29065fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy        mLeftStrip = mContext.getResources().getDrawable(resId);
29142c79880b0c19dfbcd8589d89d35fcedb1a7c9daRomain Guy        requestLayout();
29242c79880b0c19dfbcd8589d89d35fcedb1a7c9daRomain Guy        invalidate();
29361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    }
29461c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy
29561c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    /**
296189f65c12ff673087fda20e33ebcfb603143c0d3Marco Nelissen     * Sets the drawable to use as the right part of the strip below the
29761c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * tab indicators.
298189f65c12ff673087fda20e33ebcfb603143c0d3Marco Nelissen     * @param drawable the right strip drawable
29961c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     */
30061c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    public void setRightStripDrawable(Drawable drawable) {
30165fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy        mRightStrip = drawable;
30242c79880b0c19dfbcd8589d89d35fcedb1a7c9daRomain Guy        requestLayout();
30342c79880b0c19dfbcd8589d89d35fcedb1a7c9daRomain Guy        invalidate();    }
30453175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra
30553175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    /**
306189f65c12ff673087fda20e33ebcfb603143c0d3Marco Nelissen     * Sets the drawable to use as the right part of the strip below the
30761c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * tab indicators.
30861c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * @param resId the resource identifier of the drawable to use as the
309189f65c12ff673087fda20e33ebcfb603143c0d3Marco Nelissen     * right strip drawable
31061c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     */
31161c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    public void setRightStripDrawable(int resId) {
31265fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy        mRightStrip = mContext.getResources().getDrawable(resId);
31342c79880b0c19dfbcd8589d89d35fcedb1a7c9daRomain Guy        requestLayout();
31442c79880b0c19dfbcd8589d89d35fcedb1a7c9daRomain Guy        invalidate();
31561c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    }
31661c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy
31761c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    /**
31853175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * Controls whether the bottom strips on the tab indicators are drawn or
31953175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * not.  The default is to draw them.  If the user specifies a custom
32053175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * view for the tab indicators, then the TabHost class calls this method
32153175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     * to disable drawing of the bottom strips.
32261c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * @param stripEnabled true if the bottom strips should be drawn.
32361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     */
32461c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    public void setStripEnabled(boolean stripEnabled) {
32561c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy        mDrawBottomStrips = stripEnabled;
32642c79880b0c19dfbcd8589d89d35fcedb1a7c9daRomain Guy        invalidate();
32761c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    }
32861c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy
32961c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    /**
33061c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * Indicates whether the bottom strips on the tab indicators are drawn
33161c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy     * or not.
33253175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra     */
33361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy    public boolean isStripEnabled() {
33461c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy        return mDrawBottomStrips;
33553175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra    }
33653175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void childDrawableStateChanged(View child) {
339acdef59d66094f11da4a6f57194747dc06f73da2Bjorn Bringert        if (getTabCount() > 0 && child == getChildTabViewAt(mSelectedTab)) {
3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // To make sure that the bottom strip is redrawn
3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            invalidate();
3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super.childDrawableStateChanged(child);
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3453730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void dispatchDraw(Canvas canvas) {
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super.dispatchDraw(canvas);
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
350acdef59d66094f11da4a6f57194747dc06f73da2Bjorn Bringert        // Do nothing if there are no tabs.
351acdef59d66094f11da4a6f57194747dc06f73da2Bjorn Bringert        if (getTabCount() == 0) return;
352acdef59d66094f11da4a6f57194747dc06f73da2Bjorn Bringert
35353175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        // If the user specified a custom view for the tab indicators, then
35453175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        // do not draw the bottom strips.
35553175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        if (!mDrawBottomStrips) {
35653175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra            // Skip drawing the bottom strips.
35753175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra            return;
35853175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        }
35953175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra
36061c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy        final View selectedChild = getChildTabViewAt(mSelectedTab);
36161c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy
36265fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy        final Drawable leftStrip = mLeftStrip;
36365fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy        final Drawable rightStrip = mRightStrip;
3643730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar
36561c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy        leftStrip.setState(selectedChild.getDrawableState());
36661c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy        rightStrip.setState(selectedChild.getDrawableState());
3673730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mStripMoved) {
36961c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy            final Rect bounds = mBounds;
37061c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy            bounds.left = selectedChild.getLeft();
37161c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy            bounds.right = selectedChild.getRight();
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final int myHeight = getHeight();
37361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy            leftStrip.setBounds(Math.min(0, bounds.left - leftStrip.getIntrinsicWidth()),
37461c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy                    myHeight - leftStrip.getIntrinsicHeight(), bounds.left, myHeight);
37561c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy            rightStrip.setBounds(bounds.right, myHeight - rightStrip.getIntrinsicHeight(),
37661c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy                    Math.max(getWidth(), bounds.right + rightStrip.getIntrinsicWidth()), myHeight);
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mStripMoved = false;
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3793730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar
38061c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy        leftStrip.draw(canvas);
38161c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy        rightStrip.draw(canvas);
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Sets the current tab.
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This method is used to bring a tab to the front of the Widget,
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * and is used to post to the rest of the UI that a different tab
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * has been brought to the foreground.
3893730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     *
3903730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * Note, this is separate from the traditional "focus" that is
3913730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * employed from the view logic.
3923730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     *
3933730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * For instance, if we have a list in a tabbed view, a user may be
3943730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * navigating up and down the list, moving the UI focus (orange
3953730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * highlighting) through the list items.  The cursor movement does
3963730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * not effect the "selected" tab though, because what is being
3973730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * scrolled through is all on the same tab.  The selected tab only
3983730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * changes when we navigate between tabs (moving from the list view
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * to the next tabbed view, in this example).
4003730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     *
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * To move both the focus AND the selected tab at once, please use
4023730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * {@link #setCurrentTab}. Normally, the view logic takes care of
4033730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * adjusting the focus, so unless you're circumventing the UI,
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * you'll probably just focus your interest here.
4053730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     *
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *  @param index The tab that you want to indicate as the selected
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *  tab (tab brought to the front of the widget)
4083730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     *
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *  @see #focusCurrentTab
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setCurrentTab(int index) {
4125ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov        if (index < 0 || index >= getTabCount() || index == mSelectedTab) {
4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
41697dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne        if (mSelectedTab != -1) {
41797dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne            getChildTabViewAt(mSelectedTab).setSelected(false);
41897dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne        }
4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSelectedTab = index;
42053175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        getChildTabViewAt(mSelectedTab).setSelected(true);
4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mStripMoved = true;
4225ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov
4235ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov        if (isShown()) {
4245ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
4255ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov        }
4265ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov    }
4275ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov
4285ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov    @Override
4295ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
430736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        onPopulateAccessibilityEvent(event);
431736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        // Dispatch only to the selected tab.
4323fb3d7c4e756bd32d5abde0abca9ab52d559bc84Adam Powell        if (mSelectedTab != -1) {
433736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            return getChildTabViewAt(mSelectedTab).dispatchPopulateAccessibilityEvent(event);
4343fb3d7c4e756bd32d5abde0abca9ab52d559bc84Adam Powell        }
435736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        return false;
436736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
437736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
438736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    @Override
43930401328c1026389171d454c934c15875c3f7ff0Svetoslav Ganov    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
44030401328c1026389171d454c934c15875c3f7ff0Svetoslav Ganov        super.onInitializeAccessibilityEvent(event);
441736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        event.setItemCount(getTabCount());
442736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        event.setCurrentItemIndex(mSelectedTab);
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4443730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Sets the current tab and focuses the UI on it.
4473730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * This method makes sure that the focused tab matches the selected
4483730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * tab, normally at {@link #setCurrentTab}.  Normally this would not
4493730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * be an issue if we go through the UI, since the UI is responsible
4503730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * for calling TabWidget.onFocusChanged(), but in the case where we
4513730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     * are selecting the tab programmatically, we'll need to make sure
4529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * focus keeps up.
4533730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     *
4543730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     *  @param index The tab that you want focused (highlighted in orange)
4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *  and selected (tab brought to the front of the widget)
4563730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar     *
4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *  @see #setCurrentTab
4589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void focusCurrentTab(int index) {
4609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int oldTab = mSelectedTab;
4619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // set the tab
4639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        setCurrentTab(index);
4643730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar
4659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // change the focus if applicable.
4669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (oldTab != index) {
46753175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra            getChildTabViewAt(index).requestFocus();
4689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4703730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar
4719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setEnabled(boolean enabled) {
4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super.setEnabled(enabled);
47453175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        int count = getTabCount();
4753730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar
47653175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        for (int i = 0; i < count; i++) {
47753175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra            View child = getChildTabViewAt(i);
4789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            child.setEnabled(enabled);
4799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void addView(View child) {
4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (child.getLayoutParams() == null) {
4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final LinearLayout.LayoutParams lp = new LayoutParams(
4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    0,
487980a938c1c9a6a5791a8240e5a1e6638ab28dc77Romain Guy                    ViewGroup.LayoutParams.MATCH_PARENT, 1.0f);
4889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            lp.setMargins(0, 0, 0, 0);
4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            child.setLayoutParams(lp);
4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Ensure you can navigate to the tab with the keyboard, and you can touch it
4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        child.setFocusable(true);
4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        child.setClickable(true);
4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
49653175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        // If we have dividers between the tabs and we already have at least one
49753175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        // tab, then add a divider before adding the next tab.
49853175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        if (mDividerDrawable != null && getTabCount() > 0) {
4995d5cd178e3afe4a0069d392834cdebcc5c35cc08Jack Veenstra            ImageView divider = new ImageView(mContext);
50053175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra            final LinearLayout.LayoutParams lp = new LayoutParams(
50153175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra                    mDividerDrawable.getIntrinsicWidth(),
502980a938c1c9a6a5791a8240e5a1e6638ab28dc77Romain Guy                    LayoutParams.MATCH_PARENT);
50353175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra            lp.setMargins(0, 0, 0, 0);
50453175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra            divider.setLayoutParams(lp);
50553175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra            divider.setBackgroundDrawable(mDividerDrawable);
50653175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra            super.addView(divider);
50753175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        }
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super.addView(child);
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // TODO: detect this via geometry with a tabwidget listener rather
5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // than potentially interfere with the view's listener
51253175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra        child.setOnClickListener(new TabClickListener(getTabCount() - 1));
5139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        child.setOnFocusChangeListener(this);
5149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5165ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov    @Override
517cd2ca4038a027315832c38c68be5076000bc4b53Jeff Sharkey    public void removeAllViews() {
518cd2ca4038a027315832c38c68be5076000bc4b53Jeff Sharkey        super.removeAllViews();
519cd2ca4038a027315832c38c68be5076000bc4b53Jeff Sharkey        mSelectedTab = -1;
520cd2ca4038a027315832c38c68be5076000bc4b53Jeff Sharkey    }
521cd2ca4038a027315832c38c68be5076000bc4b53Jeff Sharkey
522cd2ca4038a027315832c38c68be5076000bc4b53Jeff Sharkey    @Override
5235ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov    public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
5245ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov        // this class fires events only when tabs are focused or selected
5255ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED && isFocused()) {
5265ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov            return;
5275ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov        }
5285ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov        super.sendAccessibilityEventUnchecked(event);
5295ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov    }
5305ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov
5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Provides a way for {@link TabHost} to be notified that the user clicked on a tab indicator.
5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    void setTabSelectionListener(OnTabSelectionChanged listener) {
5359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSelectionChangedListener = listener;
5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void onFocusChange(View v, boolean hasFocus) {
539acdef59d66094f11da4a6f57194747dc06f73da2Bjorn Bringert        if (v == this && hasFocus && getTabCount() > 0) {
54053175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra            getChildTabViewAt(mSelectedTab).requestFocus();
5419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
5429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5433730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar
5449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (hasFocus) {
5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int i = 0;
54653175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra            int numTabs = getTabCount();
54753175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra            while (i < numTabs) {
54853175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra                if (getChildTabViewAt(i) == v) {
5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    setCurrentTab(i);
5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mSelectionChangedListener.onTabSelectionChanged(i, false);
5515ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov                    if (isShown()) {
5525ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov                        // a tab is focused so send an event to announce the tab widget state
5535ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov                        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
5545ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov                    }
5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
5579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // registered with each tab indicator so we can notify tab host
5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private class TabClickListener implements OnClickListener {
5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final int mTabIndex;
5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private TabClickListener(int tabIndex) {
5689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mTabIndex = tabIndex;
5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void onClick(View v) {
5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mSelectionChangedListener.onTabSelectionChanged(mTabIndex, true);
5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Let {@link TabHost} know that the user clicked on a tab indicator.
5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static interface OnTabSelectionChanged {
5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Informs the TabHost which tab was selected. It also indicates
5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * if the tab was clicked/pressed or just focused into.
5833730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar         *
5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param tabIndex index of the tab that was selected
5859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param clicked whether the selection changed due to a touch/click
5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * or due to focus entering the tab through navigation. Pass true
5879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * if it was due to a press/click and false otherwise.
5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
5899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        void onTabSelectionChanged(int tabIndex, boolean clicked);
5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
5939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
594