TabWidget.java revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
1/* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.widget; 18 19import android.content.Context; 20import android.content.res.TypedArray; 21import android.graphics.Canvas; 22import android.graphics.Rect; 23import android.graphics.drawable.Drawable; 24import android.util.AttributeSet; 25import android.util.Log; 26import android.view.View; 27import android.view.ViewGroup; 28import android.view.View.OnFocusChangeListener; 29 30 31 32/** 33 * 34 * Displays a list of tab labels representing each page in the parent's tab 35 * collection. The container object for this widget is 36 * {@link android.widget.TabHost TabHost}. When the user selects a tab, this 37 * object sends a message to the parent container, TabHost, to tell it to switch 38 * the displayed page. You typically won't use many methods directly on this 39 * object. The container TabHost is used to add labels, add the callback 40 * handler, and manage callbacks. You might call this object to iterate the list 41 * of tabs, or to tweak the layout of the tab list, but most methods should be 42 * called on the containing TabHost object. 43 */ 44public class TabWidget extends LinearLayout implements OnFocusChangeListener { 45 46 47 private OnTabSelectionChanged mSelectionChangedListener; 48 private int mSelectedTab = 0; 49 private Drawable mBottomLeftStrip; 50 private Drawable mBottomRightStrip; 51 private boolean mStripMoved; 52 53 public TabWidget(Context context) { 54 this(context, null); 55 } 56 57 public TabWidget(Context context, AttributeSet attrs) { 58 this(context, attrs, com.android.internal.R.attr.tabWidgetStyle); 59 } 60 61 public TabWidget(Context context, AttributeSet attrs, int defStyle) { 62 super(context, attrs); 63 initTabWidget(); 64 65 TypedArray a = 66 context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.TabWidget, 67 defStyle, 0); 68 69 a.recycle(); 70 } 71 72 @Override 73 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 74 mStripMoved = true; 75 super.onSizeChanged(w, h, oldw, oldh); 76 } 77 78 private void initTabWidget() { 79 setOrientation(LinearLayout.HORIZONTAL); 80 mBottomLeftStrip = mContext.getResources().getDrawable( 81 com.android.internal.R.drawable.tab_bottom_left); 82 mBottomRightStrip = mContext.getResources().getDrawable( 83 com.android.internal.R.drawable.tab_bottom_right); 84 // Deal with focus, as we don't want the focus to go by default 85 // to a tab other than the current tab 86 setFocusable(true); 87 setOnFocusChangeListener(this); 88 } 89 90 @Override 91 public void childDrawableStateChanged(View child) { 92 if (child == getChildAt(mSelectedTab)) { 93 // To make sure that the bottom strip is redrawn 94 invalidate(); 95 } 96 super.childDrawableStateChanged(child); 97 } 98 99 @Override 100 public void dispatchDraw(Canvas canvas) { 101 super.dispatchDraw(canvas); 102 103 View selectedChild = getChildAt(mSelectedTab); 104 105 mBottomLeftStrip.setState(selectedChild.getDrawableState()); 106 mBottomRightStrip.setState(selectedChild.getDrawableState()); 107 108 if (mStripMoved) { 109 Rect selBounds = new Rect(); // Bounds of the selected tab indicator 110 selBounds.left = selectedChild.getLeft(); 111 selBounds.right = selectedChild.getRight(); 112 final int myHeight = getHeight(); 113 mBottomLeftStrip.setBounds( 114 Math.min(0, selBounds.left 115 - mBottomLeftStrip.getIntrinsicWidth()), 116 myHeight - mBottomLeftStrip.getIntrinsicHeight(), 117 selBounds.left, 118 getHeight()); 119 mBottomRightStrip.setBounds( 120 selBounds.right, 121 myHeight - mBottomRightStrip.getIntrinsicHeight(), 122 Math.max(getWidth(), 123 selBounds.right + mBottomRightStrip.getIntrinsicWidth()), 124 myHeight); 125 mStripMoved = false; 126 } 127 128 mBottomLeftStrip.draw(canvas); 129 mBottomRightStrip.draw(canvas); 130 } 131 132 /** 133 * Sets the current tab. 134 * This method is used to bring a tab to the front of the Widget, 135 * and is used to post to the rest of the UI that a different tab 136 * has been brought to the foreground. 137 * 138 * Note, this is separate from the traditional "focus" that is 139 * employed from the view logic. 140 * 141 * For instance, if we have a list in a tabbed view, a user may be 142 * navigating up and down the list, moving the UI focus (orange 143 * highlighting) through the list items. The cursor movement does 144 * not effect the "selected" tab though, because what is being 145 * scrolled through is all on the same tab. The selected tab only 146 * changes when we navigate between tabs (moving from the list view 147 * to the next tabbed view, in this example). 148 * 149 * To move both the focus AND the selected tab at once, please use 150 * {@link #setCurrentTab}. Normally, the view logic takes care of 151 * adjusting the focus, so unless you're circumventing the UI, 152 * you'll probably just focus your interest here. 153 * 154 * @param index The tab that you want to indicate as the selected 155 * tab (tab brought to the front of the widget) 156 * 157 * @see #focusCurrentTab 158 */ 159 public void setCurrentTab(int index) { 160 if (index < 0 || index >= getChildCount()) { 161 return; 162 } 163 164 getChildAt(mSelectedTab).setSelected(false); 165 mSelectedTab = index; 166 getChildAt(mSelectedTab).setSelected(true); 167 mStripMoved = true; 168 } 169 170 /** 171 * Sets the current tab and focuses the UI on it. 172 * This method makes sure that the focused tab matches the selected 173 * tab, normally at {@link #setCurrentTab}. Normally this would not 174 * be an issue if we go through the UI, since the UI is responsible 175 * for calling TabWidget.onFocusChanged(), but in the case where we 176 * are selecting the tab programmatically, we'll need to make sure 177 * focus keeps up. 178 * 179 * @param index The tab that you want focused (highlighted in orange) 180 * and selected (tab brought to the front of the widget) 181 * 182 * @see #setCurrentTab 183 */ 184 public void focusCurrentTab(int index) { 185 final int oldTab = mSelectedTab; 186 187 // set the tab 188 setCurrentTab(index); 189 190 // change the focus if applicable. 191 if (oldTab != index) { 192 getChildAt(index).requestFocus(); 193 } 194 } 195 196 @Override 197 public void setEnabled(boolean enabled) { 198 super.setEnabled(enabled); 199 int count = getChildCount(); 200 201 for (int i=0; i<count; i++) { 202 View child = getChildAt(i); 203 child.setEnabled(enabled); 204 } 205 } 206 207 @Override 208 public void addView(View child) { 209 if (child.getLayoutParams() == null) { 210 final LinearLayout.LayoutParams lp = new LayoutParams( 211 0, 212 ViewGroup.LayoutParams.WRAP_CONTENT, 1); 213 lp.setMargins(0, 0, 0, 0); 214 child.setLayoutParams(lp); 215 } 216 217 // Ensure you can navigate to the tab with the keyboard, and you can touch it 218 child.setFocusable(true); 219 child.setClickable(true); 220 221 super.addView(child); 222 223 // TODO: detect this via geometry with a tabwidget listener rather 224 // than potentially interfere with the view's listener 225 child.setOnClickListener(new TabClickListener(getChildCount() - 1)); 226 child.setOnFocusChangeListener(this); 227 } 228 229 230 231 232 /** 233 * Provides a way for {@link TabHost} to be notified that the user clicked on a tab indicator. 234 */ 235 void setTabSelectionListener(OnTabSelectionChanged listener) { 236 mSelectionChangedListener = listener; 237 } 238 239 public void onFocusChange(View v, boolean hasFocus) { 240 if (v == this && hasFocus) { 241 getChildAt(mSelectedTab).requestFocus(); 242 return; 243 } 244 245 if (hasFocus) { 246 int i = 0; 247 while (i < getChildCount()) { 248 if (getChildAt(i) == v) { 249 setCurrentTab(i); 250 mSelectionChangedListener.onTabSelectionChanged(i, false); 251 break; 252 } 253 i++; 254 } 255 } 256 } 257 258 // registered with each tab indicator so we can notify tab host 259 private class TabClickListener implements OnClickListener { 260 261 private final int mTabIndex; 262 263 private TabClickListener(int tabIndex) { 264 mTabIndex = tabIndex; 265 } 266 267 public void onClick(View v) { 268 mSelectionChangedListener.onTabSelectionChanged(mTabIndex, true); 269 } 270 } 271 272 /** 273 * Let {@link TabHost} know that the user clicked on a tab indicator. 274 */ 275 static interface OnTabSelectionChanged { 276 /** 277 * Informs the TabHost which tab was selected. It also indicates 278 * if the tab was clicked/pressed or just focused into. 279 * 280 * @param tabIndex index of the tab that was selected 281 * @param clicked whether the selection changed due to a touch/click 282 * or due to focus entering the tab through navigation. Pass true 283 * if it was due to a press/click and false otherwise. 284 */ 285 void onTabSelectionChanged(int tabIndex, boolean clicked); 286 } 287 288} 289 290