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