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; 328a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganovimport android.view.accessibility.AccessibilityNodeInfo; 339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/** 353730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar * 369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Displays a list of tab labels representing each page in the parent's tab 379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * collection. The container object for this widget is 389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * {@link android.widget.TabHost TabHost}. When the user selects a tab, this 399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * object sends a message to the parent container, TabHost, to tell it to switch 409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the displayed page. You typically won't use many methods directly on this 419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * object. The container TabHost is used to add labels, add the callback 429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * handler, and manage callbacks. You might call this object to iterate the list 439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * of tabs, or to tweak the layout of the tab list, but most methods should be 449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * called on the containing TabHost object. 457883c975dd0db021fa3a01226d6cb09c28764c98Romain Guy * 467883c975dd0db021fa3a01226d6cb09c28764c98Romain Guy * @attr ref android.R.styleable#TabWidget_divider 476b1e6969828f05a2f5f55825fdf16b19c9dce2ddRomain Guy * @attr ref android.R.styleable#TabWidget_tabStripEnabled 486b1e6969828f05a2f5f55825fdf16b19c9dce2ddRomain Guy * @attr ref android.R.styleable#TabWidget_tabStripLeft 496b1e6969828f05a2f5f55825fdf16b19c9dce2ddRomain Guy * @attr ref android.R.styleable#TabWidget_tabStripRight 509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class TabWidget extends LinearLayout implements OnFocusChangeListener { 529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private OnTabSelectionChanged mSelectionChangedListener; 5361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy 5497dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne // This value will be set to 0 as soon as the first tab is added to TabHost. 5597dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne private int mSelectedTab = -1; 5661c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy 5765fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy private Drawable mLeftStrip; 5865fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy private Drawable mRightStrip; 5961c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy 6061c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy private boolean mDrawBottomStrips = true; 619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private boolean mStripMoved; 6261c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy 6361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy private final Rect mBounds = new Rect(); 649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 65c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne // When positive, the widths and heights of tabs will be imposed so that they fit in parent 66c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne private int mImposedTabsHeight = -1; 67c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne private int[] mImposedTabWidths; 68c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne 699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public TabWidget(Context context) { 709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project this(context, null); 719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public TabWidget(Context context, AttributeSet attrs) { 749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project this(context, attrs, com.android.internal.R.attr.tabWidgetStyle); 759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public TabWidget(Context context, AttributeSet attrs, int defStyle) { 7811f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey super(context, attrs, defStyle); 799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8011f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey final TypedArray a = context.obtainStyledAttributes( 8111f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey attrs, com.android.internal.R.styleable.TabWidget, defStyle, 0); 829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8311f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey setStripEnabled(a.getBoolean(R.styleable.TabWidget_tabStripEnabled, true)); 8411f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey setLeftStripDrawable(a.getDrawable(R.styleable.TabWidget_tabStripLeft)); 8511f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey setRightStripDrawable(a.getDrawable(R.styleable.TabWidget_tabStripRight)); 8661c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy 879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project a.recycle(); 8861c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy 8961c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy initTabWidget(); 909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 913730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar 929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project protected void onSizeChanged(int w, int h, int oldw, int oldh) { 949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mStripMoved = true; 959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project super.onSizeChanged(w, h, oldw, oldh); 969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 983730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar @Override 993730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar protected int getChildDrawingOrder(int childCount, int i) { 10097dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne if (mSelectedTab == -1) { 1013730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar return i; 10297dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne } else { 10397dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne // Always draw the selected tab last, so that drop shadows are drawn 10497dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne // in the correct z-order. 10597dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne if (i == childCount - 1) { 10697dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne return mSelectedTab; 10797dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne } else if (i >= mSelectedTab) { 10897dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne return i + 1; 10997dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne } else { 11097dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne return i; 11197dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne } 1123730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar } 1133730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar } 1143730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar 1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void initTabWidget() { 11611f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey setChildrenDrawingOrderEnabled(true); 1173730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar 1187609764295d1b3ec0b53d1ae536ee0280f5e0407Mike Cleron final Context context = mContext; 1197609764295d1b3ec0b53d1ae536ee0280f5e0407Mike Cleron final Resources resources = context.getResources(); 12044c1473c03a0021224fa94af9b1e3579ec244b12Gilles Debunne 12144c1473c03a0021224fa94af9b1e3579ec244b12Gilles Debunne // Tests the target Sdk version, as set in the Manifest. Could not be set using styles.xml 12244c1473c03a0021224fa94af9b1e3579ec244b12Gilles Debunne // in a values-v? directory which targets the current platform Sdk version instead. 1237609764295d1b3ec0b53d1ae536ee0280f5e0407Mike Cleron if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.DONUT) { 1247609764295d1b3ec0b53d1ae536ee0280f5e0407Mike Cleron // Donut apps get old color scheme 12565fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy if (mLeftStrip == null) { 12665fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy mLeftStrip = resources.getDrawable( 12761c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy com.android.internal.R.drawable.tab_bottom_left_v4); 12861c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy } 12965fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy if (mRightStrip == null) { 13065fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy mRightStrip = resources.getDrawable( 13161c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy com.android.internal.R.drawable.tab_bottom_right_v4); 13261c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy } 1331ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn } else { 1341ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn // Use modern color scheme for Eclair and beyond 1351ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn if (mLeftStrip == null) { 1361ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn mLeftStrip = resources.getDrawable( 1371ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn com.android.internal.R.drawable.tab_bottom_left); 1381ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn } 1391ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn if (mRightStrip == null) { 1401ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn mRightStrip = resources.getDrawable( 1411ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn com.android.internal.R.drawable.tab_bottom_right); 1421ec7e20237932f14588a90e5d7378acad8efec80Dianne Hackborn } 14311f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey } 1447609764295d1b3ec0b53d1ae536ee0280f5e0407Mike Cleron 1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Deal with focus, as we don't want the focus to go by default 1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // to a tab other than the current tab 1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project setFocusable(true); 1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project setOnFocusChangeListener(this); 1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 151c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne @Override 152c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne void measureChildBeforeLayout(View child, int childIndex, 153c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne int widthMeasureSpec, int totalWidth, 154c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne int heightMeasureSpec, int totalHeight) { 15511f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey if (!isMeasureWithLargestChildEnabled() && mImposedTabsHeight >= 0) { 156c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne widthMeasureSpec = MeasureSpec.makeMeasureSpec( 157c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne totalWidth + mImposedTabWidths[childIndex], MeasureSpec.EXACTLY); 158c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne heightMeasureSpec = MeasureSpec.makeMeasureSpec(mImposedTabsHeight, 159c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne MeasureSpec.EXACTLY); 160c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne } 161c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne 162c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne super.measureChildBeforeLayout(child, childIndex, 163c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne widthMeasureSpec, totalWidth, heightMeasureSpec, totalHeight); 164c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne } 165c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne 1666741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne @Override 1676741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) { 16852a5e6588395c9cea128d245a2572b7d69bbb12cGilles Debunne if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.UNSPECIFIED) { 16952a5e6588395c9cea128d245a2572b7d69bbb12cGilles Debunne super.measureHorizontal(widthMeasureSpec, heightMeasureSpec); 17052a5e6588395c9cea128d245a2572b7d69bbb12cGilles Debunne return; 17152a5e6588395c9cea128d245a2572b7d69bbb12cGilles Debunne } 17252a5e6588395c9cea128d245a2572b7d69bbb12cGilles Debunne 173c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne // First, measure with no constraint 1746741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne final int unspecifiedWidth = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 175c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne mImposedTabsHeight = -1; 176cd59febcea2b75ebe657786133dcfa81fc3d5eb1Gilles Debunne super.measureHorizontal(unspecifiedWidth, heightMeasureSpec); 1776741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne 178c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne int extraWidth = getMeasuredWidth() - MeasureSpec.getSize(widthMeasureSpec); 179c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne if (extraWidth > 0) { 180c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne final int count = getChildCount(); 1816741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne 182c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne int childCount = 0; 1836741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne for (int i = 0; i < count; i++) { 1846741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne final View child = getChildAt(i); 185c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne if (child.getVisibility() == GONE) continue; 186c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne childCount++; 187c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne } 1886741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne 189c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne if (childCount > 0) { 190c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne if (mImposedTabWidths == null || mImposedTabWidths.length != count) { 191c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne mImposedTabWidths = new int[count]; 192c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne } 193c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne for (int i = 0; i < count; i++) { 194c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne final View child = getChildAt(i); 195c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne if (child.getVisibility() == GONE) continue; 196c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne final int childWidth = child.getMeasuredWidth(); 197c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne final int delta = extraWidth / childCount; 198c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne final int newWidth = Math.max(0, childWidth - delta); 199c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne mImposedTabWidths[i] = newWidth; 200c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne // Make sure the extra width is evenly distributed, no int division remainder 201c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne extraWidth -= childWidth - newWidth; // delta may have been clamped 202c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne childCount--; 203c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne mImposedTabsHeight = Math.max(mImposedTabsHeight, child.getMeasuredHeight()); 204c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne } 2056741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne } 206c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne } 207c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne 208c4d3f759dd6ccc5147c1e616958c8fb9415df6b9Gilles Debunne // Measure again, this time with imposed tab widths and respecting initial spec request 20952a5e6588395c9cea128d245a2572b7d69bbb12cGilles Debunne super.measureHorizontal(widthMeasureSpec, heightMeasureSpec); 2106741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne } 2116741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne 2126741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne /** 21353175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra * Returns the tab indicator view at the given index. 21453175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra * 21553175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra * @param index the zero-based index of the tab indicator view to return 21653175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra * @return the tab indicator view at the given index 21753175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra */ 21853175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra public View getChildTabViewAt(int index) { 21953175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra return getChildAt(index); 22053175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra } 22153175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra 22253175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra /** 22353175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra * Returns the number of tab indicator views. 22453175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra * @return the number of tab indicator views. 22553175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra */ 22653175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra public int getTabCount() { 22711f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey return getChildCount(); 22853175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra } 22953175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra 23053175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra /** 23153175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra * Sets the drawable to use as a divider between the tab indicators. 23253175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra * @param drawable the divider drawable 23353175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra */ 2346741c941a76d17c257c396d86c7561aa43005b1aGilles Debunne @Override 23553175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra public void setDividerDrawable(Drawable drawable) { 23611f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey super.setDividerDrawable(drawable); 23753175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra } 23853175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra 23953175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra /** 24053175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra * Sets the drawable to use as a divider between the tab indicators. 24153175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra * @param resId the resource identifier of the drawable to use as a 24253175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra * divider. 24353175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra */ 24453175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra public void setDividerDrawable(int resId) { 24511f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey setDividerDrawable(getResources().getDrawable(resId)); 24653175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra } 24761c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy 24861c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy /** 24961c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy * Sets the drawable to use as the left part of the strip below the 25061c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy * tab indicators. 25161c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy * @param drawable the left strip drawable 25261c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy */ 25361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy public void setLeftStripDrawable(Drawable drawable) { 25465fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy mLeftStrip = drawable; 25542c79880b0c19dfbcd8589d89d35fcedb1a7c9daRomain Guy requestLayout(); 25642c79880b0c19dfbcd8589d89d35fcedb1a7c9daRomain Guy invalidate(); 25761c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy } 25861c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy 25961c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy /** 26061c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy * Sets the drawable to use as the left part of the strip below the 26161c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy * tab indicators. 26261c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy * @param resId the resource identifier of the drawable to use as the 26361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy * left strip drawable 26461c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy */ 26561c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy public void setLeftStripDrawable(int resId) { 26611f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey setLeftStripDrawable(getResources().getDrawable(resId)); 26761c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy } 26861c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy 26961c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy /** 270189f65c12ff673087fda20e33ebcfb603143c0d3Marco Nelissen * Sets the drawable to use as the right part of the strip below the 27161c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy * tab indicators. 272189f65c12ff673087fda20e33ebcfb603143c0d3Marco Nelissen * @param drawable the right strip drawable 27361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy */ 27461c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy public void setRightStripDrawable(Drawable drawable) { 27565fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy mRightStrip = drawable; 27642c79880b0c19dfbcd8589d89d35fcedb1a7c9daRomain Guy requestLayout(); 27711f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey invalidate(); 27811f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey } 27953175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra 28053175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra /** 281189f65c12ff673087fda20e33ebcfb603143c0d3Marco Nelissen * Sets the drawable to use as the right part of the strip below the 28261c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy * tab indicators. 28361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy * @param resId the resource identifier of the drawable to use as the 284189f65c12ff673087fda20e33ebcfb603143c0d3Marco Nelissen * right strip drawable 28561c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy */ 28661c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy public void setRightStripDrawable(int resId) { 28711f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey setRightStripDrawable(getResources().getDrawable(resId)); 28861c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy } 28911f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey 29061c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy /** 29153175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra * Controls whether the bottom strips on the tab indicators are drawn or 29253175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra * not. The default is to draw them. If the user specifies a custom 29353175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra * view for the tab indicators, then the TabHost class calls this method 29453175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra * to disable drawing of the bottom strips. 29561c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy * @param stripEnabled true if the bottom strips should be drawn. 29661c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy */ 29761c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy public void setStripEnabled(boolean stripEnabled) { 29861c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy mDrawBottomStrips = stripEnabled; 29942c79880b0c19dfbcd8589d89d35fcedb1a7c9daRomain Guy invalidate(); 30061c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy } 30161c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy 30261c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy /** 30361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy * Indicates whether the bottom strips on the tab indicators are drawn 30461c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy * or not. 30553175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra */ 30661c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy public boolean isStripEnabled() { 30761c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy return mDrawBottomStrips; 30853175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra } 30953175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra 3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void childDrawableStateChanged(View child) { 312acdef59d66094f11da4a6f57194747dc06f73da2Bjorn Bringert if (getTabCount() > 0 && child == getChildTabViewAt(mSelectedTab)) { 3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // To make sure that the bottom strip is redrawn 3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project invalidate(); 3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project super.childDrawableStateChanged(child); 3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3183730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar 3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void dispatchDraw(Canvas canvas) { 3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project super.dispatchDraw(canvas); 3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 323acdef59d66094f11da4a6f57194747dc06f73da2Bjorn Bringert // Do nothing if there are no tabs. 324acdef59d66094f11da4a6f57194747dc06f73da2Bjorn Bringert if (getTabCount() == 0) return; 325acdef59d66094f11da4a6f57194747dc06f73da2Bjorn Bringert 32653175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra // If the user specified a custom view for the tab indicators, then 32753175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra // do not draw the bottom strips. 32853175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra if (!mDrawBottomStrips) { 32953175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra // Skip drawing the bottom strips. 33053175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra return; 33153175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra } 33253175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra 33361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy final View selectedChild = getChildTabViewAt(mSelectedTab); 33461c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy 33565fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy final Drawable leftStrip = mLeftStrip; 33665fe2c08fa5332f5e2aaa805a029855e876e485eRomain Guy final Drawable rightStrip = mRightStrip; 3373730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar 33861c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy leftStrip.setState(selectedChild.getDrawableState()); 33961c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy rightStrip.setState(selectedChild.getDrawableState()); 3403730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar 3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mStripMoved) { 34261c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy final Rect bounds = mBounds; 34361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy bounds.left = selectedChild.getLeft(); 34461c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy bounds.right = selectedChild.getRight(); 3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final int myHeight = getHeight(); 34661c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy leftStrip.setBounds(Math.min(0, bounds.left - leftStrip.getIntrinsicWidth()), 34761c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy myHeight - leftStrip.getIntrinsicHeight(), bounds.left, myHeight); 34861c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy rightStrip.setBounds(bounds.right, myHeight - rightStrip.getIntrinsicHeight(), 34961c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy Math.max(getWidth(), bounds.right + rightStrip.getIntrinsicWidth()), myHeight); 3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mStripMoved = false; 3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3523730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar 35361c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy leftStrip.draw(canvas); 35461c9d4b4509aeaab1fcad24fb5a63c874d2fd941Romain Guy rightStrip.draw(canvas); 3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Sets the current tab. 3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This method is used to bring a tab to the front of the Widget, 3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * and is used to post to the rest of the UI that a different tab 3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * has been brought to the foreground. 3623730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar * 3633730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar * Note, this is separate from the traditional "focus" that is 3643730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar * employed from the view logic. 3653730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar * 3663730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar * For instance, if we have a list in a tabbed view, a user may be 3673730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar * navigating up and down the list, moving the UI focus (orange 3683730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar * highlighting) through the list items. The cursor movement does 3693730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar * not effect the "selected" tab though, because what is being 3703730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar * scrolled through is all on the same tab. The selected tab only 3713730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar * changes when we navigate between tabs (moving from the list view 3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * to the next tabbed view, in this example). 3733730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar * 3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * To move both the focus AND the selected tab at once, please use 3753730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar * {@link #setCurrentTab}. Normally, the view logic takes care of 3763730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar * adjusting the focus, so unless you're circumventing the UI, 3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you'll probably just focus your interest here. 3783730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar * 3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param index The tab that you want to indicate as the selected 3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * tab (tab brought to the front of the widget) 3813730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar * 3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @see #focusCurrentTab 3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void setCurrentTab(int index) { 3855ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov if (index < 0 || index >= getTabCount() || index == mSelectedTab) { 3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 38997dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne if (mSelectedTab != -1) { 39097dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne getChildTabViewAt(mSelectedTab).setSelected(false); 39197dfd3492f81525fb3560a74abaee9f965f138a9Gilles Debunne } 3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSelectedTab = index; 39353175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra getChildTabViewAt(mSelectedTab).setSelected(true); 3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mStripMoved = true; 3955ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov 3965ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov if (isShown()) { 3975ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); 3985ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov } 3995ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov } 4005ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov 4015ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov @Override 4025ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 403736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov onPopulateAccessibilityEvent(event); 404736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Dispatch only to the selected tab. 4053fb3d7c4e756bd32d5abde0abca9ab52d559bc84Adam Powell if (mSelectedTab != -1) { 4060b0a41d8e26eaf0f1d9d922621494daf40964a9aSvetoslav Ganov View tabView = getChildTabViewAt(mSelectedTab); 4070b0a41d8e26eaf0f1d9d922621494daf40964a9aSvetoslav Ganov if (tabView != null && tabView.getVisibility() == VISIBLE) { 4080b0a41d8e26eaf0f1d9d922621494daf40964a9aSvetoslav Ganov return tabView.dispatchPopulateAccessibilityEvent(event); 4090b0a41d8e26eaf0f1d9d922621494daf40964a9aSvetoslav Ganov } 4103fb3d7c4e756bd32d5abde0abca9ab52d559bc84Adam Powell } 411736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return false; 412736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 413736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 414736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov @Override 41530401328c1026389171d454c934c15875c3f7ff0Svetoslav Ganov public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 41630401328c1026389171d454c934c15875c3f7ff0Svetoslav Ganov super.onInitializeAccessibilityEvent(event); 4178a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov event.setClassName(TabWidget.class.getName()); 418736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov event.setItemCount(getTabCount()); 419736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov event.setCurrentItemIndex(mSelectedTab); 4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4213730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar 4228a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov 4238a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov @Override 4248a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov public void sendAccessibilityEventUnchecked(AccessibilityEvent event) { 4258a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov // this class fires events only when tabs are focused or selected 4268a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED && isFocused()) { 4278a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov event.recycle(); 4288a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov return; 4298a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov } 4308a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov super.sendAccessibilityEventUnchecked(event); 4318a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov } 4328a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov 4338a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov @Override 4348a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 4358a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov super.onInitializeAccessibilityNodeInfo(info); 4368a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov info.setClassName(TabWidget.class.getName()); 4378a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov } 4388a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov 4399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Sets the current tab and focuses the UI on it. 4413730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar * This method makes sure that the focused tab matches the selected 4423730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar * tab, normally at {@link #setCurrentTab}. Normally this would not 4433730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar * be an issue if we go through the UI, since the UI is responsible 4443730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar * for calling TabWidget.onFocusChanged(), but in the case where we 4453730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar * are selecting the tab programmatically, we'll need to make sure 4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * focus keeps up. 4473730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar * 4483730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar * @param index The tab that you want focused (highlighted in orange) 4499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * and selected (tab brought to the front of the widget) 4503730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar * 4519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @see #setCurrentTab 4529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void focusCurrentTab(int index) { 4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final int oldTab = mSelectedTab; 4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // set the tab 4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project setCurrentTab(index); 4583730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar 4599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // change the focus if applicable. 4609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (oldTab != index) { 46153175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra getChildTabViewAt(index).requestFocus(); 4629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4643730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar 4659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 4669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void setEnabled(boolean enabled) { 4679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project super.setEnabled(enabled); 4683730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar 46911f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey final int count = getTabCount(); 47053175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra for (int i = 0; i < count; i++) { 47153175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra View child = getChildTabViewAt(i); 4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project child.setEnabled(enabled); 4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 4779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void addView(View child) { 4789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (child.getLayoutParams() == null) { 4799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final LinearLayout.LayoutParams lp = new LayoutParams( 4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 0, 481980a938c1c9a6a5791a8240e5a1e6638ab28dc77Romain Guy ViewGroup.LayoutParams.MATCH_PARENT, 1.0f); 4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project lp.setMargins(0, 0, 0, 0); 4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project child.setLayoutParams(lp); 4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Ensure you can navigate to the tab with the keyboard, and you can touch it 4879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project child.setFocusable(true); 4889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project child.setClickable(true); 4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project super.addView(child); 4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // TODO: detect this via geometry with a tabwidget listener rather 4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // than potentially interfere with the view's listener 49453175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra child.setOnClickListener(new TabClickListener(getTabCount() - 1)); 4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project child.setOnFocusChangeListener(this); 4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4985ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov @Override 499cd2ca4038a027315832c38c68be5076000bc4b53Jeff Sharkey public void removeAllViews() { 500cd2ca4038a027315832c38c68be5076000bc4b53Jeff Sharkey super.removeAllViews(); 501cd2ca4038a027315832c38c68be5076000bc4b53Jeff Sharkey mSelectedTab = -1; 502cd2ca4038a027315832c38c68be5076000bc4b53Jeff Sharkey } 503cd2ca4038a027315832c38c68be5076000bc4b53Jeff Sharkey 5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Provides a way for {@link TabHost} to be notified that the user clicked on a tab indicator. 5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project void setTabSelectionListener(OnTabSelectionChanged listener) { 5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSelectionChangedListener = listener; 5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 51111f4a48c54f3006778c874662ff04a4d9d157f25Jeff Sharkey /** {@inheritDoc} */ 5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void onFocusChange(View v, boolean hasFocus) { 513acdef59d66094f11da4a6f57194747dc06f73da2Bjorn Bringert if (v == this && hasFocus && getTabCount() > 0) { 51453175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra getChildTabViewAt(mSelectedTab).requestFocus(); 5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5173730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar 5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (hasFocus) { 5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int i = 0; 52053175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra int numTabs = getTabCount(); 52153175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra while (i < numTabs) { 52253175148c9b962c0ffe9cea0f3dc68bc0196e044Jack Veenstra if (getChildTabViewAt(i) == v) { 5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project setCurrentTab(i); 5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSelectionChangedListener.onTabSelectionChanged(i, false); 5255ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov if (isShown()) { 5265ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov // a tab is focused so send an event to announce the tab widget state 5275ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); 5285ac413a1d40b04f12d80d65b7c0168b5b225b3e7Svetoslav Ganov } 5299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project i++; 5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // registered with each tab indicator so we can notify tab host 5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private class TabClickListener implements OnClickListener { 5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private final int mTabIndex; 5409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private TabClickListener(int tabIndex) { 5429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mTabIndex = tabIndex; 5439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void onClick(View v) { 5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSelectionChangedListener.onTabSelectionChanged(mTabIndex, true); 5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Let {@link TabHost} know that the user clicked on a tab indicator. 5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project static interface OnTabSelectionChanged { 5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Informs the TabHost which tab was selected. It also indicates 5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * if the tab was clicked/pressed or just focused into. 5573730bb1cd59bcfa9f5cad8361997b84113ed5923Evan Millar * 5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param tabIndex index of the tab that was selected 5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param clicked whether the selection changed due to a touch/click 5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * or due to focus entering the tab through navigation. Pass true 5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * if it was due to a press/click and false otherwise. 5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project void onTabSelectionChanged(int tabIndex, boolean clicked); 5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 566