ActionBarView.java revision e7d468410b3a783560d5158a5798cef1b4b67702
1/* 2 * Copyright (C) 2010 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 com.android.internal.widget; 18 19import com.android.internal.R; 20import com.android.internal.view.menu.ActionMenuItem; 21import com.android.internal.view.menu.ActionMenuView; 22import com.android.internal.view.menu.MenuBuilder; 23 24import android.app.ActionBar; 25import android.app.ActionBar.OnNavigationListener; 26import android.app.Activity; 27import android.content.Context; 28import android.content.pm.ApplicationInfo; 29import android.content.pm.PackageManager; 30import android.content.pm.PackageManager.NameNotFoundException; 31import android.content.res.TypedArray; 32import android.graphics.drawable.Drawable; 33import android.text.TextUtils.TruncateAt; 34import android.util.AttributeSet; 35import android.util.Log; 36import android.view.ActionMode; 37import android.view.Gravity; 38import android.view.LayoutInflater; 39import android.view.Menu; 40import android.view.View; 41import android.view.ViewGroup; 42import android.view.ViewParent; 43import android.view.Window; 44import android.widget.AdapterView; 45import android.widget.HorizontalScrollView; 46import android.widget.ImageView; 47import android.widget.LinearLayout; 48import android.widget.ProgressBar; 49import android.widget.Spinner; 50import android.widget.SpinnerAdapter; 51import android.widget.TextView; 52 53/** 54 * @hide 55 */ 56public class ActionBarView extends ViewGroup { 57 private static final String TAG = "ActionBarView"; 58 59 /** 60 * Display options applied by default 61 */ 62 public static final int DISPLAY_DEFAULT = 0; 63 64 /** 65 * Display options that require re-layout as opposed to a simple invalidate 66 */ 67 private static final int DISPLAY_RELAYOUT_MASK = 68 ActionBar.DISPLAY_SHOW_HOME | 69 ActionBar.DISPLAY_USE_LOGO | 70 ActionBar.DISPLAY_HOME_AS_UP | 71 ActionBar.DISPLAY_SHOW_CUSTOM | 72 ActionBar.DISPLAY_SHOW_TITLE; 73 74 private static final int DEFAULT_CUSTOM_GRAVITY = Gravity.LEFT | Gravity.CENTER_VERTICAL; 75 76 private final int mContentHeight; 77 78 private int mNavigationMode; 79 private int mDisplayOptions = ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_HOME_AS_UP; 80 private CharSequence mTitle; 81 private CharSequence mSubtitle; 82 private Drawable mIcon; 83 private Drawable mLogo; 84 private Drawable mDivider; 85 86 private View mHomeLayout; 87 private View mHomeAsUpView; 88 private ImageView mIconView; 89 private LinearLayout mTitleLayout; 90 private TextView mTitleView; 91 private TextView mSubtitleView; 92 private Spinner mSpinner; 93 private LinearLayout mListNavLayout; 94 private HorizontalScrollView mTabScrollView; 95 private LinearLayout mTabLayout; 96 private View mCustomNavView; 97 private ProgressBar mProgressView; 98 private ProgressBar mIndeterminateProgressView; 99 100 private int mProgressBarPadding; 101 private int mItemPadding; 102 103 private int mTitleStyleRes; 104 private int mSubtitleStyleRes; 105 private int mProgressStyle; 106 private int mIndeterminateProgressStyle; 107 108 private boolean mShowMenu; 109 private boolean mUserTitle; 110 111 private MenuBuilder mOptionsMenu; 112 private ActionMenuView mMenuView; 113 114 private ActionBarContextView mContextView; 115 116 private ActionMenuItem mLogoNavItem; 117 118 private SpinnerAdapter mSpinnerAdapter; 119 private OnNavigationListener mCallback; 120 121 private final AdapterView.OnItemSelectedListener mNavItemSelectedListener = 122 new AdapterView.OnItemSelectedListener() { 123 public void onItemSelected(AdapterView parent, View view, int position, long id) { 124 if (mCallback != null) { 125 mCallback.onNavigationItemSelected(position, id); 126 } 127 } 128 public void onNothingSelected(AdapterView parent) { 129 // Do nothing 130 } 131 }; 132 133 private OnClickListener mTabClickListener = null; 134 135 public ActionBarView(Context context, AttributeSet attrs) { 136 super(context, attrs); 137 138 // Background is always provided by the container. 139 setBackgroundResource(0); 140 141 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionBar); 142 143 ApplicationInfo appInfo = context.getApplicationInfo(); 144 PackageManager pm = context.getPackageManager(); 145 mNavigationMode = a.getInt(R.styleable.ActionBar_navigationMode, 146 ActionBar.NAVIGATION_MODE_STANDARD); 147 mTitle = a.getText(R.styleable.ActionBar_title); 148 mSubtitle = a.getText(R.styleable.ActionBar_subtitle); 149 150 mLogo = a.getDrawable(R.styleable.ActionBar_logo); 151 if (mLogo == null) { 152 if (context instanceof Activity) { 153 try { 154 mLogo = pm.getActivityLogo(((Activity) context).getComponentName()); 155 } catch (NameNotFoundException e) { 156 Log.e(TAG, "Activity component name not found!", e); 157 } 158 } 159 if (mLogo == null) { 160 mLogo = appInfo.loadLogo(pm); 161 } 162 } 163 164 mIcon = a.getDrawable(R.styleable.ActionBar_icon); 165 if (mIcon == null) { 166 if (context instanceof Activity) { 167 try { 168 mIcon = pm.getActivityIcon(((Activity) context).getComponentName()); 169 } catch (NameNotFoundException e) { 170 Log.e(TAG, "Activity component name not found!", e); 171 } 172 } 173 if (mIcon == null) { 174 mIcon = appInfo.loadIcon(pm); 175 } 176 } 177 178 final LayoutInflater inflater = LayoutInflater.from(context); 179 180 final int homeResId = a.getResourceId( 181 com.android.internal.R.styleable.ActionBar_homeLayout, 182 com.android.internal.R.layout.action_bar_home); 183 184 mHomeLayout = inflater.inflate(homeResId, this, false); 185 186 mHomeAsUpView = mHomeLayout.findViewById(com.android.internal.R.id.up); 187 mIconView = (ImageView) mHomeLayout.findViewById(com.android.internal.R.id.home); 188 189 mTitleStyleRes = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0); 190 mSubtitleStyleRes = a.getResourceId(R.styleable.ActionBar_subtitleTextStyle, 0); 191 mProgressStyle = a.getResourceId(R.styleable.ActionBar_progressBarStyle, 0); 192 mIndeterminateProgressStyle = a.getResourceId( 193 R.styleable.ActionBar_indeterminateProgressStyle, 0); 194 195 mProgressBarPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_progressBarPadding, 0); 196 mItemPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_itemPadding, 0); 197 198 setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, DISPLAY_DEFAULT)); 199 200 final int customNavId = a.getResourceId(R.styleable.ActionBar_customNavigationLayout, 0); 201 if (customNavId != 0) { 202 mCustomNavView = (View) inflater.inflate(customNavId, this, false); 203 mNavigationMode = ActionBar.NAVIGATION_MODE_STANDARD; 204 setDisplayOptions(mDisplayOptions | ActionBar.DISPLAY_SHOW_CUSTOM); 205 } 206 207 mContentHeight = a.getLayoutDimension(R.styleable.ActionBar_height, 0); 208 209 mDivider = a.getDrawable(R.styleable.ActionBar_divider); 210 211 a.recycle(); 212 213 mLogoNavItem = new ActionMenuItem(context, 0, android.R.id.home, 0, 0, mTitle); 214 mHomeLayout.setOnClickListener(new OnClickListener() { 215 public void onClick(View v) { 216 Context context = getContext(); 217 if (context instanceof Activity) { 218 Activity activity = (Activity) context; 219 activity.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mLogoNavItem); 220 } 221 } 222 }); 223 mHomeLayout.setClickable(true); 224 mHomeLayout.setFocusable(true); 225 } 226 227 public void initProgress() { 228 mProgressView = new ProgressBar(mContext, null, 0, mProgressStyle); 229 mProgressView.setId(R.id.progress_horizontal); 230 mProgressView.setMax(10000); 231 addView(mProgressView); 232 } 233 234 public void initIndeterminateProgress() { 235 mIndeterminateProgressView = new ProgressBar(mContext, null, 0, 236 mIndeterminateProgressStyle); 237 mIndeterminateProgressView.setId(R.id.progress_circular); 238 addView(mIndeterminateProgressView); 239 } 240 241 @Override 242 public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) { 243 // No starting an action mode for an action bar child! (Where would it go?) 244 return null; 245 } 246 247 public void setCallback(OnNavigationListener callback) { 248 mCallback = callback; 249 } 250 251 public void setMenu(Menu menu) { 252 MenuBuilder builder = (MenuBuilder) menu; 253 mOptionsMenu = builder; 254 if (mMenuView != null) { 255 removeView(mMenuView); 256 } 257 final ActionMenuView menuView = (ActionMenuView) builder.getMenuView( 258 MenuBuilder.TYPE_ACTION_BUTTON, null); 259 final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, 260 LayoutParams.MATCH_PARENT); 261 menuView.setLayoutParams(layoutParams); 262 addView(menuView); 263 mMenuView = menuView; 264 } 265 266 public boolean showOverflowMenu() { 267 if (mMenuView != null) { 268 return mMenuView.showOverflowMenu(); 269 } 270 return false; 271 } 272 273 public void openOverflowMenu() { 274 if (mMenuView != null) { 275 mMenuView.openOverflowMenu(); 276 } 277 } 278 279 public void postShowOverflowMenu() { 280 post(new Runnable() { 281 public void run() { 282 showOverflowMenu(); 283 } 284 }); 285 } 286 287 public boolean hideOverflowMenu() { 288 if (mMenuView != null) { 289 return mMenuView.hideOverflowMenu(); 290 } 291 return false; 292 } 293 294 public boolean isOverflowMenuShowing() { 295 if (mMenuView != null) { 296 return mMenuView.isOverflowMenuShowing(); 297 } 298 return false; 299 } 300 301 public boolean isOverflowMenuOpen() { 302 if (mMenuView != null) { 303 return mMenuView.isOverflowMenuOpen(); 304 } 305 return false; 306 } 307 308 public boolean isOverflowReserved() { 309 return mMenuView != null && mMenuView.isOverflowReserved(); 310 } 311 312 public void setCustomNavigationView(View view) { 313 final boolean showCustom = (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0; 314 if (mCustomNavView != null && showCustom) { 315 removeView(mCustomNavView); 316 } 317 mCustomNavView = view; 318 if (mCustomNavView != null && showCustom) { 319 addView(mCustomNavView); 320 } 321 } 322 323 public CharSequence getTitle() { 324 return mTitle; 325 } 326 327 /** 328 * Set the action bar title. This will always replace or override window titles. 329 * @param title Title to set 330 * 331 * @see #setWindowTitle(CharSequence) 332 */ 333 public void setTitle(CharSequence title) { 334 mUserTitle = true; 335 setTitleImpl(title); 336 } 337 338 /** 339 * Set the window title. A window title will always be replaced or overridden by a user title. 340 * @param title Title to set 341 * 342 * @see #setTitle(CharSequence) 343 */ 344 public void setWindowTitle(CharSequence title) { 345 if (!mUserTitle) { 346 setTitleImpl(title); 347 } 348 } 349 350 private void setTitleImpl(CharSequence title) { 351 mTitle = title; 352 if (mTitleView != null) { 353 mTitleView.setText(title); 354 } 355 if (mLogoNavItem != null) { 356 mLogoNavItem.setTitle(title); 357 } 358 } 359 360 public CharSequence getSubtitle() { 361 return mSubtitle; 362 } 363 364 public void setSubtitle(CharSequence subtitle) { 365 mSubtitle = subtitle; 366 if (mSubtitleView != null) { 367 mSubtitleView.setText(subtitle); 368 mSubtitleView.setVisibility(subtitle != null ? VISIBLE : GONE); 369 } 370 } 371 372 public void setDisplayOptions(int options) { 373 // TODO Remove this once DISPLAY_HIDE_HOME is removed 374 if ((options & ActionBar.DISPLAY_HIDE_HOME) != 0) { 375 options &= ~(ActionBar.DISPLAY_HIDE_HOME | ActionBar.DISPLAY_SHOW_HOME); 376 } 377 // End TODO 378 379 final int flagsChanged = options ^ mDisplayOptions; 380 mDisplayOptions = options; 381 if ((flagsChanged & DISPLAY_RELAYOUT_MASK) != 0) { 382 final int vis = (options & ActionBar.DISPLAY_SHOW_HOME) != 0 ? VISIBLE : GONE; 383 mHomeLayout.setVisibility(vis); 384 385 if ((flagsChanged & ActionBar.DISPLAY_HOME_AS_UP) != 0) { 386 mHomeAsUpView.setVisibility((options & ActionBar.DISPLAY_HOME_AS_UP) != 0 387 ? VISIBLE : INVISIBLE); 388 } 389 390 if ((flagsChanged & ActionBar.DISPLAY_USE_LOGO) != 0) { 391 final boolean logoVis = mLogo != null && (options & ActionBar.DISPLAY_USE_LOGO) != 0; 392 mIconView.setImageDrawable(logoVis ? mLogo : mIcon); 393 } 394 395 if ((flagsChanged & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 396 if ((options & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 397 initTitle(); 398 } else { 399 removeView(mTitleLayout); 400 } 401 } 402 403 if ((flagsChanged & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomNavView != null) { 404 if ((options & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { 405 addView(mCustomNavView); 406 } else { 407 removeView(mCustomNavView); 408 } 409 } 410 411 requestLayout(); 412 } else { 413 invalidate(); 414 } 415 } 416 417 public void setNavigationMode(int mode) { 418 final int oldMode = mNavigationMode; 419 if (mode != oldMode) { 420 switch (oldMode) { 421 case ActionBar.NAVIGATION_MODE_LIST: 422 if (mSpinner != null) { 423 removeView(mListNavLayout); 424 } 425 break; 426 case ActionBar.NAVIGATION_MODE_TABS: 427 if (mTabLayout != null) { 428 removeView(mTabScrollView); 429 } 430 } 431 432 switch (mode) { 433 case ActionBar.NAVIGATION_MODE_LIST: 434 if (mSpinner == null) { 435 mSpinner = new Spinner(mContext, null, 436 com.android.internal.R.attr.actionDropDownStyle); 437 mListNavLayout = new LinearLayout(mContext, null, 438 com.android.internal.R.attr.actionBarTabBarStyle); 439 LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( 440 LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); 441 params.gravity = Gravity.CENTER; 442 mListNavLayout.addView(mSpinner, params); 443 } 444 if (mSpinner.getAdapter() != mSpinnerAdapter) { 445 mSpinner.setAdapter(mSpinnerAdapter); 446 } 447 mSpinner.setOnItemSelectedListener(mNavItemSelectedListener); 448 addView(mListNavLayout); 449 break; 450 case ActionBar.NAVIGATION_MODE_TABS: 451 ensureTabsExist(); 452 addView(mTabScrollView); 453 break; 454 } 455 mNavigationMode = mode; 456 requestLayout(); 457 } 458 } 459 460 private void ensureTabsExist() { 461 if (mTabScrollView == null) { 462 mTabScrollView = new HorizontalScrollView(getContext()); 463 mTabLayout = new LinearLayout(getContext(), null, 464 com.android.internal.R.attr.actionBarTabBarStyle); 465 mTabScrollView.addView(mTabLayout); 466 } 467 } 468 469 public void setDropdownAdapter(SpinnerAdapter adapter) { 470 mSpinnerAdapter = adapter; 471 if (mSpinner != null) { 472 mSpinner.setAdapter(adapter); 473 } 474 } 475 476 public SpinnerAdapter getDropdownAdapter() { 477 return mSpinnerAdapter; 478 } 479 480 public void setDropdownSelectedPosition(int position) { 481 mSpinner.setSelection(position); 482 } 483 484 public int getDropdownSelectedPosition() { 485 return mSpinner.getSelectedItemPosition(); 486 } 487 488 public View getCustomNavigationView() { 489 return mCustomNavView; 490 } 491 492 public int getNavigationMode() { 493 return mNavigationMode; 494 } 495 496 public int getDisplayOptions() { 497 return mDisplayOptions; 498 } 499 500 private TabView createTabView(ActionBar.Tab tab) { 501 final TabView tabView = new TabView(getContext(), tab); 502 tabView.setFocusable(true); 503 504 if (mTabClickListener == null) { 505 mTabClickListener = new TabClickListener(); 506 } 507 tabView.setOnClickListener(mTabClickListener); 508 return tabView; 509 } 510 511 public void addTab(ActionBar.Tab tab, boolean setSelected) { 512 ensureTabsExist(); 513 View tabView = createTabView(tab); 514 mTabLayout.addView(tabView); 515 if (setSelected) { 516 tabView.setSelected(true); 517 } 518 } 519 520 public void addTab(ActionBar.Tab tab, int position, boolean setSelected) { 521 ensureTabsExist(); 522 final TabView tabView = createTabView(tab); 523 mTabLayout.addView(tabView, position); 524 if (setSelected) { 525 tabView.setSelected(true); 526 } 527 } 528 529 public void removeTabAt(int position) { 530 if (mTabLayout != null) { 531 mTabLayout.removeViewAt(position); 532 } 533 } 534 535 public void removeAllTabs() { 536 if (mTabLayout != null) { 537 mTabLayout.removeAllViews(); 538 } 539 } 540 541 @Override 542 protected LayoutParams generateDefaultLayoutParams() { 543 // Used by custom nav views if they don't supply layout params. Everything else 544 // added to an ActionBarView should have them already. 545 return new ActionBar.LayoutParams(DEFAULT_CUSTOM_GRAVITY); 546 } 547 548 @Override 549 protected void onFinishInflate() { 550 super.onFinishInflate(); 551 552 addView(mHomeLayout); 553 554 if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { 555 final ViewParent parent = mCustomNavView.getParent(); 556 if (parent != this) { 557 if (parent instanceof ViewGroup) { 558 ((ViewGroup) parent).removeView(mCustomNavView); 559 } 560 addView(mCustomNavView); 561 } 562 } 563 } 564 565 private void initTitle() { 566 LayoutInflater inflater = LayoutInflater.from(getContext()); 567 mTitleLayout = (LinearLayout) inflater.inflate(R.layout.action_bar_title_item, null); 568 mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title); 569 mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle); 570 571 if (mTitleStyleRes != 0) { 572 mTitleView.setTextAppearance(mContext, mTitleStyleRes); 573 } 574 if (mTitle != null) { 575 mTitleView.setText(mTitle); 576 } 577 578 if (mSubtitleStyleRes != 0) { 579 mSubtitleView.setTextAppearance(mContext, mSubtitleStyleRes); 580 } 581 if (mSubtitle != null) { 582 mSubtitleView.setText(mSubtitle); 583 mSubtitleView.setVisibility(VISIBLE); 584 } 585 586 addView(mTitleLayout); 587 } 588 589 public void setTabSelected(int position) { 590 ensureTabsExist(); 591 final int tabCount = mTabLayout.getChildCount(); 592 for (int i = 0; i < tabCount; i++) { 593 final View child = mTabLayout.getChildAt(i); 594 child.setSelected(i == position); 595 } 596 } 597 598 public void setContextView(ActionBarContextView view) { 599 mContextView = view; 600 } 601 602 @Override 603 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 604 int widthMode = MeasureSpec.getMode(widthMeasureSpec); 605 if (widthMode != MeasureSpec.EXACTLY) { 606 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + 607 "with android:layout_width=\"match_parent\" (or fill_parent)"); 608 } 609 610 int heightMode = MeasureSpec.getMode(heightMeasureSpec); 611 if (heightMode != MeasureSpec.AT_MOST) { 612 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + 613 "with android:layout_height=\"wrap_content\""); 614 } 615 616 int contentWidth = MeasureSpec.getSize(widthMeasureSpec); 617 618 int maxHeight = mContentHeight > 0 ? 619 mContentHeight : MeasureSpec.getSize(heightMeasureSpec); 620 621 final int verticalPadding = getPaddingTop() + getPaddingBottom(); 622 final int paddingLeft = getPaddingLeft(); 623 final int paddingRight = getPaddingRight(); 624 final int height = maxHeight - verticalPadding; 625 final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST); 626 627 int availableWidth = contentWidth - paddingLeft - paddingRight; 628 int leftOfCenter = availableWidth / 2; 629 int rightOfCenter = leftOfCenter; 630 631 if (mHomeLayout.getVisibility() != GONE) { 632 mHomeLayout.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), 633 MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); 634 final int homeWidth = mHomeLayout.getMeasuredWidth(); 635 availableWidth -= homeWidth; 636 leftOfCenter -= homeWidth; 637 } 638 639 if (mMenuView != null) { 640 availableWidth = measureChildView(mMenuView, availableWidth, 641 childSpecHeight, 0); 642 rightOfCenter -= mMenuView.getMeasuredWidth(); 643 } 644 645 boolean showTitle = mTitleLayout != null && 646 (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0; 647 if (showTitle) { 648 availableWidth = measureChildView(mTitleLayout, availableWidth, childSpecHeight, 0); 649 leftOfCenter -= mTitleLayout.getMeasuredWidth(); 650 } 651 652 switch (mNavigationMode) { 653 case ActionBar.NAVIGATION_MODE_LIST: 654 if (mListNavLayout != null) { 655 final int itemPaddingSize = showTitle ? mItemPadding * 2 : mItemPadding; 656 availableWidth -= itemPaddingSize; 657 leftOfCenter -= itemPaddingSize; 658 mListNavLayout.measure( 659 MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), 660 MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); 661 final int listNavWidth = mListNavLayout.getMeasuredWidth(); 662 availableWidth -= listNavWidth; 663 leftOfCenter -= listNavWidth; 664 } 665 break; 666 case ActionBar.NAVIGATION_MODE_TABS: 667 if (mTabScrollView != null) { 668 final int itemPaddingSize = showTitle ? mItemPadding * 2 : mItemPadding; 669 availableWidth -= itemPaddingSize; 670 leftOfCenter -= itemPaddingSize; 671 mTabScrollView.measure( 672 MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), 673 MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); 674 final int tabWidth = mTabScrollView.getMeasuredWidth(); 675 availableWidth -= tabWidth; 676 leftOfCenter -= tabWidth; 677 } 678 break; 679 } 680 681 if (mIndeterminateProgressView != null && 682 mIndeterminateProgressView.getVisibility() != GONE) { 683 availableWidth = measureChildView(mIndeterminateProgressView, availableWidth, 684 childSpecHeight, 0); 685 rightOfCenter -= mIndeterminateProgressView.getMeasuredWidth(); 686 } 687 688 if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomNavView != null) { 689 final LayoutParams lp = generateLayoutParams(mCustomNavView.getLayoutParams()); 690 final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ? 691 (ActionBar.LayoutParams) lp : null; 692 693 int horizontalMargin = 0; 694 int verticalMargin = 0; 695 if (ablp != null) { 696 horizontalMargin = ablp.leftMargin + ablp.rightMargin; 697 verticalMargin = ablp.topMargin + ablp.bottomMargin; 698 } 699 700 // If the action bar is wrapping to its content height, don't allow a custom 701 // view to MATCH_PARENT. 702 int customNavHeightMode; 703 if (mContentHeight <= 0) { 704 customNavHeightMode = MeasureSpec.AT_MOST; 705 } else { 706 customNavHeightMode = lp.height != LayoutParams.WRAP_CONTENT ? 707 MeasureSpec.EXACTLY : MeasureSpec.AT_MOST; 708 } 709 final int customNavHeight = Math.max(0, 710 (lp.height >= 0 ? Math.min(lp.height, height) : height) - verticalMargin); 711 712 final int customNavWidthMode = lp.width != LayoutParams.WRAP_CONTENT ? 713 MeasureSpec.EXACTLY : MeasureSpec.AT_MOST; 714 int customNavWidth = Math.max(0, 715 (lp.width >= 0 ? Math.min(lp.width, availableWidth) : availableWidth) 716 - horizontalMargin); 717 final int hgrav = (ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY) & 718 Gravity.HORIZONTAL_GRAVITY_MASK; 719 720 // Centering a custom view is treated specially; we try to center within the whole 721 // action bar rather than in the available space. 722 if (hgrav == Gravity.CENTER_HORIZONTAL && lp.width == LayoutParams.MATCH_PARENT) { 723 customNavWidth = Math.min(leftOfCenter, rightOfCenter) * 2; 724 } 725 726 mCustomNavView.measure( 727 MeasureSpec.makeMeasureSpec(customNavWidth, customNavWidthMode), 728 MeasureSpec.makeMeasureSpec(customNavHeight, customNavHeightMode)); 729 } 730 731 if (mContentHeight <= 0) { 732 int measuredHeight = 0; 733 final int count = getChildCount(); 734 for (int i = 0; i < count; i++) { 735 View v = getChildAt(i); 736 int paddedViewHeight = v.getMeasuredHeight() + verticalPadding; 737 if (paddedViewHeight > measuredHeight) { 738 measuredHeight = paddedViewHeight; 739 } 740 } 741 setMeasuredDimension(contentWidth, measuredHeight); 742 } else { 743 setMeasuredDimension(contentWidth, maxHeight); 744 } 745 746 if (mContextView != null) { 747 mContextView.setHeight(getMeasuredHeight()); 748 } 749 750 if (mProgressView != null && mProgressView.getVisibility() != GONE) { 751 mProgressView.measure(MeasureSpec.makeMeasureSpec( 752 contentWidth - mProgressBarPadding * 2, MeasureSpec.EXACTLY), 753 MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST)); 754 } 755 } 756 757 private int measureChildView(View child, int availableWidth, int childSpecHeight, int spacing) { 758 child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), 759 childSpecHeight); 760 761 availableWidth -= child.getMeasuredWidth(); 762 availableWidth -= spacing; 763 764 return availableWidth; 765 } 766 767 @Override 768 protected void onLayout(boolean changed, int l, int t, int r, int b) { 769 int x = getPaddingLeft(); 770 final int y = getPaddingTop(); 771 final int contentHeight = b - t - getPaddingTop() - getPaddingBottom(); 772 773 if (mHomeLayout.getVisibility() != GONE) { 774 x += positionChild(mHomeLayout, x, y, contentHeight); 775 } 776 777 final boolean showTitle = mTitleLayout != null && 778 (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0; 779 if (showTitle) { 780 x += positionChild(mTitleLayout, x, y, contentHeight); 781 } 782 783 switch (mNavigationMode) { 784 case ActionBar.NAVIGATION_MODE_STANDARD: 785 break; 786 case ActionBar.NAVIGATION_MODE_LIST: 787 if (mListNavLayout != null) { 788 if (showTitle) x += mItemPadding; 789 x += positionChild(mListNavLayout, x, y, contentHeight) + mItemPadding; 790 } 791 break; 792 case ActionBar.NAVIGATION_MODE_TABS: 793 if (mTabScrollView != null) { 794 if (showTitle) x += mItemPadding; 795 x += positionChild(mTabScrollView, x, y, contentHeight) + mItemPadding; 796 } 797 break; 798 } 799 800 int menuLeft = r - l - getPaddingRight(); 801 if (mMenuView != null) { 802 positionChildInverse(mMenuView, menuLeft, y, contentHeight); 803 menuLeft -= mMenuView.getMeasuredWidth(); 804 } 805 806 if (mIndeterminateProgressView != null && 807 mIndeterminateProgressView.getVisibility() != GONE) { 808 positionChildInverse(mIndeterminateProgressView, menuLeft, y, contentHeight); 809 menuLeft -= mIndeterminateProgressView.getMeasuredWidth(); 810 } 811 812 if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { 813 LayoutParams lp = mCustomNavView.getLayoutParams(); 814 final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ? 815 (ActionBar.LayoutParams) lp : null; 816 817 final int gravity = ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY; 818 final int navWidth = mCustomNavView.getMeasuredWidth(); 819 820 int topMargin = 0; 821 int bottomMargin = 0; 822 if (ablp != null) { 823 x += ablp.leftMargin; 824 menuLeft -= ablp.rightMargin; 825 topMargin = ablp.topMargin; 826 bottomMargin = ablp.bottomMargin; 827 } 828 829 int hgravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK; 830 // See if we actually have room to truly center; if not push against left or right. 831 if (hgravity == Gravity.CENTER_HORIZONTAL) { 832 final int centeredLeft = ((mRight - mLeft) - navWidth) / 2; 833 if (centeredLeft < x) { 834 hgravity = Gravity.LEFT; 835 } else if (centeredLeft + navWidth > menuLeft) { 836 hgravity = Gravity.RIGHT; 837 } 838 } 839 840 int xpos = 0; 841 switch (hgravity) { 842 case Gravity.CENTER_HORIZONTAL: 843 xpos = ((mRight - mLeft) - navWidth) / 2; 844 break; 845 case Gravity.LEFT: 846 xpos = x; 847 break; 848 case Gravity.RIGHT: 849 xpos = menuLeft - navWidth; 850 break; 851 } 852 853 int ypos = 0; 854 switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) { 855 case Gravity.CENTER_VERTICAL: 856 final int paddedTop = mTop + getPaddingTop(); 857 final int paddedBottom = mBottom - getPaddingBottom(); 858 ypos = ((paddedBottom - paddedTop) - mCustomNavView.getMeasuredHeight()) / 2; 859 break; 860 case Gravity.TOP: 861 ypos = getPaddingTop() + topMargin; 862 break; 863 case Gravity.BOTTOM: 864 ypos = getHeight() - getPaddingBottom() - mCustomNavView.getMeasuredHeight() 865 - bottomMargin; 866 break; 867 } 868 x += positionChild(mCustomNavView, xpos, ypos, contentHeight); 869 } 870 871 if (mProgressView != null) { 872 mProgressView.bringToFront(); 873 final int halfProgressHeight = mProgressView.getMeasuredHeight() / 2; 874 mProgressView.layout(mProgressBarPadding, -halfProgressHeight, 875 mProgressBarPadding + mProgressView.getMeasuredWidth(), halfProgressHeight); 876 } 877 } 878 879 private int positionChild(View child, int x, int y, int contentHeight) { 880 int childWidth = child.getMeasuredWidth(); 881 int childHeight = child.getMeasuredHeight(); 882 int childTop = y + (contentHeight - childHeight) / 2; 883 884 child.layout(x, childTop, x + childWidth, childTop + childHeight); 885 886 return childWidth; 887 } 888 889 private int positionChildInverse(View child, int x, int y, int contentHeight) { 890 int childWidth = child.getMeasuredWidth(); 891 int childHeight = child.getMeasuredHeight(); 892 int childTop = y + (contentHeight - childHeight) / 2; 893 894 child.layout(x - childWidth, childTop, x, childTop + childHeight); 895 896 return childWidth; 897 } 898 899 private static class TabView extends LinearLayout { 900 private ActionBar.Tab mTab; 901 902 public TabView(Context context, ActionBar.Tab tab) { 903 super(context, null, com.android.internal.R.attr.actionBarTabStyle); 904 mTab = tab; 905 906 final View custom = tab.getCustomView(); 907 if (custom != null) { 908 addView(custom); 909 } else { 910 // TODO Style tabs based on the theme 911 912 final Drawable icon = tab.getIcon(); 913 final CharSequence text = tab.getText(); 914 915 if (icon != null) { 916 ImageView iconView = new ImageView(context); 917 iconView.setImageDrawable(icon); 918 LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, 919 LayoutParams.WRAP_CONTENT); 920 lp.gravity = Gravity.CENTER_VERTICAL; 921 iconView.setLayoutParams(lp); 922 addView(iconView); 923 } 924 925 if (text != null) { 926 TextView textView = new TextView(context, null, 927 com.android.internal.R.attr.actionBarTabTextStyle); 928 textView.setText(text); 929 textView.setSingleLine(); 930 textView.setEllipsize(TruncateAt.END); 931 LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, 932 LayoutParams.WRAP_CONTENT); 933 lp.gravity = Gravity.CENTER_VERTICAL; 934 textView.setLayoutParams(lp); 935 addView(textView); 936 } 937 } 938 939 setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, 940 LayoutParams.MATCH_PARENT, 1)); 941 } 942 943 public ActionBar.Tab getTab() { 944 return mTab; 945 } 946 } 947 948 private class TabClickListener implements OnClickListener { 949 public void onClick(View view) { 950 TabView tabView = (TabView) view; 951 tabView.getTab().select(); 952 final int tabCount = mTabLayout.getChildCount(); 953 for (int i = 0; i < tabCount; i++) { 954 final View child = mTabLayout.getChildAt(i); 955 child.setSelected(child == view); 956 } 957 } 958 } 959} 960