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