ActionBarView.java revision bd266fef814922448a048107e9d13a2a22a5d957
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 android.animation.LayoutTransition; 20import android.app.ActionBar; 21import android.content.Context; 22import android.content.res.Configuration; 23import android.content.res.TypedArray; 24import android.graphics.drawable.Drawable; 25import android.os.Parcel; 26import android.os.Parcelable; 27import android.text.Layout; 28import android.text.TextUtils; 29import android.util.AttributeSet; 30import android.view.CollapsibleActionView; 31import android.view.Gravity; 32import android.view.LayoutInflater; 33import android.view.Menu; 34import android.view.MenuItem; 35import android.view.MotionEvent; 36import android.view.View; 37import android.view.ViewGroup; 38import android.view.ViewParent; 39import android.view.Window; 40import android.view.accessibility.AccessibilityEvent; 41import android.widget.ActionMenuPresenter; 42import android.widget.ActionMenuView; 43import android.widget.AdapterView; 44import android.widget.FrameLayout; 45import android.widget.ImageView; 46import android.widget.LinearLayout; 47import android.widget.ProgressBar; 48import android.widget.Spinner; 49import android.widget.SpinnerAdapter; 50import android.widget.TextView; 51import com.android.internal.R; 52import com.android.internal.transition.ActionBarTransition; 53import com.android.internal.view.menu.ActionMenuItem; 54import com.android.internal.view.menu.MenuBuilder; 55import com.android.internal.view.menu.MenuItemImpl; 56import com.android.internal.view.menu.MenuPresenter; 57import com.android.internal.view.menu.MenuView; 58import com.android.internal.view.menu.SubMenuBuilder; 59 60/** 61 * @hide 62 */ 63public class ActionBarView extends AbsActionBarView implements DecorToolbar { 64 private static final String TAG = "ActionBarView"; 65 66 /** 67 * Display options applied by default 68 */ 69 public static final int DISPLAY_DEFAULT = 0; 70 71 /** 72 * Display options that require re-layout as opposed to a simple invalidate 73 */ 74 private static final int DISPLAY_RELAYOUT_MASK = 75 ActionBar.DISPLAY_SHOW_HOME | 76 ActionBar.DISPLAY_USE_LOGO | 77 ActionBar.DISPLAY_HOME_AS_UP | 78 ActionBar.DISPLAY_SHOW_CUSTOM | 79 ActionBar.DISPLAY_SHOW_TITLE | 80 ActionBar.DISPLAY_TITLE_MULTIPLE_LINES; 81 82 private static final int DEFAULT_CUSTOM_GRAVITY = Gravity.START | Gravity.CENTER_VERTICAL; 83 84 private int mNavigationMode; 85 private int mDisplayOptions = -1; 86 private CharSequence mTitle; 87 private CharSequence mSubtitle; 88 private Drawable mIcon; 89 private Drawable mLogo; 90 private CharSequence mHomeDescription; 91 private int mHomeDescriptionRes; 92 93 private HomeView mHomeLayout; 94 private HomeView mExpandedHomeLayout; 95 private LinearLayout mTitleLayout; 96 private TextView mTitleView; 97 private TextView mSubtitleView; 98 private ViewGroup mUpGoerFive; 99 100 private Spinner mSpinner; 101 private LinearLayout mListNavLayout; 102 private ScrollingTabContainerView mTabScrollView; 103 private View mCustomNavView; 104 private ProgressBar mProgressView; 105 private ProgressBar mIndeterminateProgressView; 106 107 private int mProgressBarPadding; 108 private int mItemPadding; 109 110 private final int mTitleStyleRes; 111 private final int mSubtitleStyleRes; 112 private final int mProgressStyle; 113 private final int mIndeterminateProgressStyle; 114 115 private boolean mUserTitle; 116 private boolean mIncludeTabs; 117 private boolean mIsCollapsible; 118 private boolean mWasHomeEnabled; // Was it enabled before action view expansion? 119 120 private MenuBuilder mOptionsMenu; 121 private boolean mMenuPrepared; 122 123 private ActionBarContextView mContextView; 124 125 private ActionMenuItem mLogoNavItem; 126 127 private SpinnerAdapter mSpinnerAdapter; 128 private AdapterView.OnItemSelectedListener mNavItemSelectedListener; 129 130 private Runnable mTabSelector; 131 132 private ExpandedActionViewMenuPresenter mExpandedMenuPresenter; 133 View mExpandedActionView; 134 private int mDefaultUpDescription = R.string.action_bar_up_description; 135 136 Window.Callback mWindowCallback; 137 138 private final OnClickListener mExpandedActionViewUpListener = new OnClickListener() { 139 @Override 140 public void onClick(View v) { 141 final MenuItemImpl item = mExpandedMenuPresenter.mCurrentExpandedItem; 142 if (item != null) { 143 item.collapseActionView(); 144 } 145 } 146 }; 147 148 private final OnClickListener mUpClickListener = new OnClickListener() { 149 public void onClick(View v) { 150 if (mMenuPrepared) { 151 // Only invoke the window callback if the options menu has been initialized. 152 mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mLogoNavItem); 153 } 154 } 155 }; 156 157 public ActionBarView(Context context, AttributeSet attrs) { 158 super(context, attrs); 159 160 // Background is always provided by the container. 161 setBackgroundResource(0); 162 163 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionBar, 164 com.android.internal.R.attr.actionBarStyle, 0); 165 166 mNavigationMode = a.getInt(R.styleable.ActionBar_navigationMode, 167 ActionBar.NAVIGATION_MODE_STANDARD); 168 mTitle = a.getText(R.styleable.ActionBar_title); 169 mSubtitle = a.getText(R.styleable.ActionBar_subtitle); 170 mLogo = a.getDrawable(R.styleable.ActionBar_logo); 171 mIcon = a.getDrawable(R.styleable.ActionBar_icon); 172 173 final LayoutInflater inflater = LayoutInflater.from(context); 174 175 final int homeResId = a.getResourceId( 176 com.android.internal.R.styleable.ActionBar_homeLayout, 177 com.android.internal.R.layout.action_bar_home); 178 179 mUpGoerFive = (ViewGroup) inflater.inflate( 180 com.android.internal.R.layout.action_bar_up_container, this, false); 181 mHomeLayout = (HomeView) inflater.inflate(homeResId, mUpGoerFive, false); 182 183 mExpandedHomeLayout = (HomeView) inflater.inflate(homeResId, mUpGoerFive, false); 184 mExpandedHomeLayout.setShowUp(true); 185 mExpandedHomeLayout.setOnClickListener(mExpandedActionViewUpListener); 186 mExpandedHomeLayout.setContentDescription(getResources().getText( 187 mDefaultUpDescription)); 188 189 // This needs to highlight/be focusable on its own. 190 // TODO: Clean up the handoff between expanded/normal. 191 final Drawable upBackground = mUpGoerFive.getBackground(); 192 if (upBackground != null) { 193 mExpandedHomeLayout.setBackground(upBackground.getConstantState().newDrawable()); 194 } 195 mExpandedHomeLayout.setEnabled(true); 196 mExpandedHomeLayout.setFocusable(true); 197 198 mTitleStyleRes = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0); 199 mSubtitleStyleRes = a.getResourceId(R.styleable.ActionBar_subtitleTextStyle, 0); 200 mProgressStyle = a.getResourceId(R.styleable.ActionBar_progressBarStyle, 0); 201 mIndeterminateProgressStyle = a.getResourceId( 202 R.styleable.ActionBar_indeterminateProgressStyle, 0); 203 204 mProgressBarPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_progressBarPadding, 0); 205 mItemPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_itemPadding, 0); 206 207 setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, DISPLAY_DEFAULT)); 208 209 final int customNavId = a.getResourceId(R.styleable.ActionBar_customNavigationLayout, 0); 210 if (customNavId != 0) { 211 mCustomNavView = (View) inflater.inflate(customNavId, this, false); 212 mNavigationMode = ActionBar.NAVIGATION_MODE_STANDARD; 213 setDisplayOptions(mDisplayOptions | ActionBar.DISPLAY_SHOW_CUSTOM); 214 } 215 216 mContentHeight = a.getLayoutDimension(R.styleable.ActionBar_height, 0); 217 218 a.recycle(); 219 220 mLogoNavItem = new ActionMenuItem(context, 0, android.R.id.home, 0, 0, mTitle); 221 222 mUpGoerFive.setOnClickListener(mUpClickListener); 223 mUpGoerFive.setClickable(true); 224 mUpGoerFive.setFocusable(true); 225 226 if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { 227 setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); 228 } 229 } 230 231 @Override 232 protected void onConfigurationChanged(Configuration newConfig) { 233 super.onConfigurationChanged(newConfig); 234 235 mTitleView = null; 236 mSubtitleView = null; 237 if (mTitleLayout != null && mTitleLayout.getParent() == mUpGoerFive) { 238 mUpGoerFive.removeView(mTitleLayout); 239 } 240 mTitleLayout = null; 241 if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 242 initTitle(); 243 } 244 245 if (mHomeDescriptionRes != 0) { 246 setNavigationContentDescription(mHomeDescriptionRes); 247 } 248 249 if (mTabScrollView != null && mIncludeTabs) { 250 ViewGroup.LayoutParams lp = mTabScrollView.getLayoutParams(); 251 if (lp != null) { 252 lp.width = LayoutParams.WRAP_CONTENT; 253 lp.height = LayoutParams.MATCH_PARENT; 254 } 255 mTabScrollView.setAllowCollapse(true); 256 } 257 } 258 259 /** 260 * Set the window callback used to invoke menu items; used for dispatching home button presses. 261 * @param cb Window callback to dispatch to 262 */ 263 public void setWindowCallback(Window.Callback cb) { 264 mWindowCallback = cb; 265 } 266 267 @Override 268 public void onDetachedFromWindow() { 269 super.onDetachedFromWindow(); 270 removeCallbacks(mTabSelector); 271 if (mActionMenuPresenter != null) { 272 mActionMenuPresenter.hideOverflowMenu(); 273 mActionMenuPresenter.hideSubMenus(); 274 } 275 } 276 277 @Override 278 public boolean shouldDelayChildPressedState() { 279 return false; 280 } 281 282 public void initProgress() { 283 mProgressView = new ProgressBar(mContext, null, 0, mProgressStyle); 284 mProgressView.setId(R.id.progress_horizontal); 285 mProgressView.setMax(10000); 286 mProgressView.setVisibility(GONE); 287 addView(mProgressView); 288 } 289 290 public void initIndeterminateProgress() { 291 mIndeterminateProgressView = new ProgressBar(mContext, null, 0, 292 mIndeterminateProgressStyle); 293 mIndeterminateProgressView.setId(R.id.progress_circular); 294 mIndeterminateProgressView.setVisibility(GONE); 295 addView(mIndeterminateProgressView); 296 } 297 298 @Override 299 public void setSplitToolbar(boolean splitActionBar) { 300 if (mSplitActionBar != splitActionBar) { 301 if (mMenuView != null) { 302 final ViewGroup oldParent = (ViewGroup) mMenuView.getParent(); 303 if (oldParent != null) { 304 oldParent.removeView(mMenuView); 305 } 306 if (splitActionBar) { 307 if (mSplitView != null) { 308 mSplitView.addView(mMenuView); 309 } 310 mMenuView.getLayoutParams().width = LayoutParams.MATCH_PARENT; 311 } else { 312 addView(mMenuView); 313 mMenuView.getLayoutParams().width = LayoutParams.WRAP_CONTENT; 314 } 315 mMenuView.requestLayout(); 316 } 317 if (mSplitView != null) { 318 mSplitView.setVisibility(splitActionBar ? VISIBLE : GONE); 319 } 320 321 if (mActionMenuPresenter != null) { 322 if (!splitActionBar) { 323 mActionMenuPresenter.setExpandedActionViewsExclusive( 324 getResources().getBoolean( 325 com.android.internal.R.bool.action_bar_expanded_action_views_exclusive)); 326 } else { 327 mActionMenuPresenter.setExpandedActionViewsExclusive(false); 328 // Allow full screen width in split mode. 329 mActionMenuPresenter.setWidthLimit( 330 getContext().getResources().getDisplayMetrics().widthPixels, true); 331 // No limit to the item count; use whatever will fit. 332 mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE); 333 } 334 } 335 super.setSplitToolbar(splitActionBar); 336 } 337 } 338 339 public boolean isSplit() { 340 return mSplitActionBar; 341 } 342 343 public boolean canSplit() { 344 return true; 345 } 346 347 public boolean hasEmbeddedTabs() { 348 return mIncludeTabs; 349 } 350 351 @Override 352 public void setEmbeddedTabView(ScrollingTabContainerView tabs) { 353 if (mTabScrollView != null) { 354 removeView(mTabScrollView); 355 } 356 mTabScrollView = tabs; 357 mIncludeTabs = tabs != null; 358 if (mIncludeTabs && mNavigationMode == ActionBar.NAVIGATION_MODE_TABS) { 359 addView(mTabScrollView); 360 ViewGroup.LayoutParams lp = mTabScrollView.getLayoutParams(); 361 lp.width = LayoutParams.WRAP_CONTENT; 362 lp.height = LayoutParams.MATCH_PARENT; 363 tabs.setAllowCollapse(true); 364 } 365 } 366 367 public void setMenuPrepared() { 368 mMenuPrepared = true; 369 } 370 371 public void setMenu(Menu menu, MenuPresenter.Callback cb) { 372 if (menu == mOptionsMenu) return; 373 374 if (mOptionsMenu != null) { 375 mOptionsMenu.removeMenuPresenter(mActionMenuPresenter); 376 mOptionsMenu.removeMenuPresenter(mExpandedMenuPresenter); 377 } 378 379 MenuBuilder builder = (MenuBuilder) menu; 380 mOptionsMenu = builder; 381 if (mMenuView != null) { 382 final ViewGroup oldParent = (ViewGroup) mMenuView.getParent(); 383 if (oldParent != null) { 384 oldParent.removeView(mMenuView); 385 } 386 } 387 if (mActionMenuPresenter == null) { 388 mActionMenuPresenter = new ActionMenuPresenter(mContext); 389 mActionMenuPresenter.setCallback(cb); 390 mActionMenuPresenter.setId(com.android.internal.R.id.action_menu_presenter); 391 mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter(); 392 } 393 394 ActionMenuView menuView; 395 final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, 396 LayoutParams.MATCH_PARENT); 397 if (!mSplitActionBar) { 398 mActionMenuPresenter.setExpandedActionViewsExclusive( 399 getResources().getBoolean( 400 com.android.internal.R.bool.action_bar_expanded_action_views_exclusive)); 401 configPresenters(builder); 402 menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); 403 final ViewGroup oldParent = (ViewGroup) menuView.getParent(); 404 if (oldParent != null && oldParent != this) { 405 oldParent.removeView(menuView); 406 } 407 addView(menuView, layoutParams); 408 } else { 409 mActionMenuPresenter.setExpandedActionViewsExclusive(false); 410 // Allow full screen width in split mode. 411 mActionMenuPresenter.setWidthLimit( 412 getContext().getResources().getDisplayMetrics().widthPixels, true); 413 // No limit to the item count; use whatever will fit. 414 mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE); 415 // Span the whole width 416 layoutParams.width = LayoutParams.MATCH_PARENT; 417 layoutParams.height = LayoutParams.WRAP_CONTENT; 418 configPresenters(builder); 419 menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); 420 if (mSplitView != null) { 421 final ViewGroup oldParent = (ViewGroup) menuView.getParent(); 422 if (oldParent != null && oldParent != mSplitView) { 423 oldParent.removeView(menuView); 424 } 425 menuView.setVisibility(getAnimatedVisibility()); 426 mSplitView.addView(menuView, layoutParams); 427 } else { 428 // We'll add this later if we missed it this time. 429 menuView.setLayoutParams(layoutParams); 430 } 431 } 432 mMenuView = menuView; 433 } 434 435 private void configPresenters(MenuBuilder builder) { 436 if (builder != null) { 437 builder.addMenuPresenter(mActionMenuPresenter, mPopupContext); 438 builder.addMenuPresenter(mExpandedMenuPresenter, mPopupContext); 439 } else { 440 mActionMenuPresenter.initForMenu(mPopupContext, null); 441 mExpandedMenuPresenter.initForMenu(mPopupContext, null); 442 mActionMenuPresenter.updateMenuView(true); 443 mExpandedMenuPresenter.updateMenuView(true); 444 } 445 } 446 447 public boolean hasExpandedActionView() { 448 return mExpandedMenuPresenter != null && 449 mExpandedMenuPresenter.mCurrentExpandedItem != null; 450 } 451 452 public void collapseActionView() { 453 final MenuItemImpl item = mExpandedMenuPresenter == null ? null : 454 mExpandedMenuPresenter.mCurrentExpandedItem; 455 if (item != null) { 456 item.collapseActionView(); 457 } 458 } 459 460 public void setCustomView(View view) { 461 final boolean showCustom = (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0; 462 if (showCustom) { 463 ActionBarTransition.beginDelayedTransition(this); 464 } 465 if (mCustomNavView != null && showCustom) { 466 removeView(mCustomNavView); 467 } 468 mCustomNavView = view; 469 if (mCustomNavView != null && showCustom) { 470 addView(mCustomNavView); 471 } 472 } 473 474 public CharSequence getTitle() { 475 return mTitle; 476 } 477 478 /** 479 * Set the action bar title. This will always replace or override window titles. 480 * @param title Title to set 481 * 482 * @see #setWindowTitle(CharSequence) 483 */ 484 public void setTitle(CharSequence title) { 485 mUserTitle = true; 486 setTitleImpl(title); 487 } 488 489 /** 490 * Set the window title. A window title will always be replaced or overridden by a user title. 491 * @param title Title to set 492 * 493 * @see #setTitle(CharSequence) 494 */ 495 public void setWindowTitle(CharSequence title) { 496 if (!mUserTitle) { 497 setTitleImpl(title); 498 } 499 } 500 501 private void setTitleImpl(CharSequence title) { 502 ActionBarTransition.beginDelayedTransition(this); 503 mTitle = title; 504 if (mTitleView != null) { 505 mTitleView.setText(title); 506 final boolean visible = mExpandedActionView == null && 507 (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0 && 508 (!TextUtils.isEmpty(mTitle) || !TextUtils.isEmpty(mSubtitle)); 509 mTitleLayout.setVisibility(visible ? VISIBLE : GONE); 510 } 511 if (mLogoNavItem != null) { 512 mLogoNavItem.setTitle(title); 513 } 514 updateHomeAccessibility(mUpGoerFive.isEnabled()); 515 } 516 517 public CharSequence getSubtitle() { 518 return mSubtitle; 519 } 520 521 public void setSubtitle(CharSequence subtitle) { 522 ActionBarTransition.beginDelayedTransition(this); 523 mSubtitle = subtitle; 524 if (mSubtitleView != null) { 525 mSubtitleView.setText(subtitle); 526 mSubtitleView.setVisibility(subtitle != null ? VISIBLE : GONE); 527 final boolean visible = mExpandedActionView == null && 528 (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0 && 529 (!TextUtils.isEmpty(mTitle) || !TextUtils.isEmpty(mSubtitle)); 530 mTitleLayout.setVisibility(visible ? VISIBLE : GONE); 531 } 532 updateHomeAccessibility(mUpGoerFive.isEnabled()); 533 } 534 535 public void setHomeButtonEnabled(boolean enable) { 536 setHomeButtonEnabled(enable, true); 537 } 538 539 private void setHomeButtonEnabled(boolean enable, boolean recordState) { 540 if (recordState) { 541 mWasHomeEnabled = enable; 542 } 543 544 if (mExpandedActionView != null) { 545 // There's an action view currently showing and we want to keep the state 546 // configured for the action view at the moment. If we needed to record the 547 // new state for later we will have done so above. 548 return; 549 } 550 551 mUpGoerFive.setEnabled(enable); 552 mUpGoerFive.setFocusable(enable); 553 // Make sure the home button has an accurate content description for accessibility. 554 updateHomeAccessibility(enable); 555 } 556 557 private void updateHomeAccessibility(boolean homeEnabled) { 558 if (!homeEnabled) { 559 mUpGoerFive.setContentDescription(null); 560 mUpGoerFive.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); 561 } else { 562 mUpGoerFive.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_AUTO); 563 mUpGoerFive.setContentDescription(buildHomeContentDescription()); 564 } 565 } 566 567 /** 568 * Compose a content description for the Home/Up affordance. 569 * 570 * <p>As this encompasses the icon/logo, title and subtitle all in one, we need 571 * a description for the whole wad of stuff that can be localized properly.</p> 572 */ 573 private CharSequence buildHomeContentDescription() { 574 final CharSequence homeDesc; 575 if (mHomeDescription != null) { 576 homeDesc = mHomeDescription; 577 } else { 578 if ((mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0) { 579 homeDesc = mContext.getResources().getText(mDefaultUpDescription); 580 } else { 581 homeDesc = mContext.getResources().getText(R.string.action_bar_home_description); 582 } 583 } 584 585 final CharSequence title = getTitle(); 586 final CharSequence subtitle = getSubtitle(); 587 if (!TextUtils.isEmpty(title)) { 588 final String result; 589 if (!TextUtils.isEmpty(subtitle)) { 590 result = getResources().getString( 591 R.string.action_bar_home_subtitle_description_format, 592 title, subtitle, homeDesc); 593 } else { 594 result = getResources().getString(R.string.action_bar_home_description_format, 595 title, homeDesc); 596 } 597 return result; 598 } 599 return homeDesc; 600 } 601 602 public void setDisplayOptions(int options) { 603 final int flagsChanged = mDisplayOptions == -1 ? -1 : options ^ mDisplayOptions; 604 mDisplayOptions = options; 605 606 if ((flagsChanged & DISPLAY_RELAYOUT_MASK) != 0) { 607 ActionBarTransition.beginDelayedTransition(this); 608 609 if ((flagsChanged & ActionBar.DISPLAY_HOME_AS_UP) != 0) { 610 final boolean setUp = (options & ActionBar.DISPLAY_HOME_AS_UP) != 0; 611 mHomeLayout.setShowUp(setUp); 612 613 // Showing home as up implicitly enables interaction with it. 614 // In honeycomb it was always enabled, so make this transition 615 // a bit easier for developers in the common case. 616 // (It would be silly to show it as up without responding to it.) 617 if (setUp) { 618 setHomeButtonEnabled(true); 619 } 620 } 621 622 if ((flagsChanged & ActionBar.DISPLAY_USE_LOGO) != 0) { 623 final boolean logoVis = mLogo != null && (options & ActionBar.DISPLAY_USE_LOGO) != 0; 624 mHomeLayout.setIcon(logoVis ? mLogo : mIcon); 625 } 626 627 if ((flagsChanged & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 628 if ((options & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 629 initTitle(); 630 } else { 631 mUpGoerFive.removeView(mTitleLayout); 632 } 633 } 634 635 final boolean showHome = (options & ActionBar.DISPLAY_SHOW_HOME) != 0; 636 final boolean homeAsUp = (mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0; 637 final boolean titleUp = !showHome && homeAsUp; 638 mHomeLayout.setShowIcon(showHome); 639 640 final int homeVis = (showHome || titleUp) && mExpandedActionView == null ? 641 VISIBLE : GONE; 642 mHomeLayout.setVisibility(homeVis); 643 644 if ((flagsChanged & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomNavView != null) { 645 if ((options & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { 646 addView(mCustomNavView); 647 } else { 648 removeView(mCustomNavView); 649 } 650 } 651 652 if (mTitleLayout != null && 653 (flagsChanged & ActionBar.DISPLAY_TITLE_MULTIPLE_LINES) != 0) { 654 if ((options & ActionBar.DISPLAY_TITLE_MULTIPLE_LINES) != 0) { 655 mTitleView.setSingleLine(false); 656 mTitleView.setMaxLines(2); 657 } else { 658 mTitleView.setMaxLines(1); 659 mTitleView.setSingleLine(true); 660 } 661 } 662 663 requestLayout(); 664 } else { 665 invalidate(); 666 } 667 668 // Make sure the home button has an accurate content description for accessibility. 669 updateHomeAccessibility(mUpGoerFive.isEnabled()); 670 } 671 672 public void setIcon(Drawable icon) { 673 mIcon = icon; 674 if (icon != null && 675 ((mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) == 0 || mLogo == null)) { 676 mHomeLayout.setIcon(icon); 677 } 678 if (mExpandedActionView != null) { 679 mExpandedHomeLayout.setIcon(mIcon.getConstantState().newDrawable(getResources())); 680 } 681 } 682 683 public void setIcon(int resId) { 684 setIcon(resId != 0 ? mContext.getDrawable(resId) : null); 685 } 686 687 public boolean hasIcon() { 688 return mIcon != null; 689 } 690 691 public void setLogo(Drawable logo) { 692 mLogo = logo; 693 if (logo != null && (mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) != 0) { 694 mHomeLayout.setIcon(logo); 695 } 696 } 697 698 public void setLogo(int resId) { 699 setLogo(resId != 0 ? mContext.getDrawable(resId) : null); 700 } 701 702 public boolean hasLogo() { 703 return mLogo != null; 704 } 705 706 public void setNavigationMode(int mode) { 707 final int oldMode = mNavigationMode; 708 if (mode != oldMode) { 709 ActionBarTransition.beginDelayedTransition(this); 710 switch (oldMode) { 711 case ActionBar.NAVIGATION_MODE_LIST: 712 if (mListNavLayout != null) { 713 removeView(mListNavLayout); 714 } 715 break; 716 case ActionBar.NAVIGATION_MODE_TABS: 717 if (mTabScrollView != null && mIncludeTabs) { 718 removeView(mTabScrollView); 719 } 720 } 721 722 switch (mode) { 723 case ActionBar.NAVIGATION_MODE_LIST: 724 if (mSpinner == null) { 725 mSpinner = new Spinner(mContext, null, 726 com.android.internal.R.attr.actionDropDownStyle); 727 mSpinner.setId(com.android.internal.R.id.action_bar_spinner); 728 mListNavLayout = new LinearLayout(mContext, null, 729 com.android.internal.R.attr.actionBarTabBarStyle); 730 LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( 731 LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); 732 params.gravity = Gravity.CENTER; 733 mListNavLayout.addView(mSpinner, params); 734 } 735 if (mSpinner.getAdapter() != mSpinnerAdapter) { 736 mSpinner.setAdapter(mSpinnerAdapter); 737 } 738 mSpinner.setOnItemSelectedListener(mNavItemSelectedListener); 739 addView(mListNavLayout); 740 break; 741 case ActionBar.NAVIGATION_MODE_TABS: 742 if (mTabScrollView != null && mIncludeTabs) { 743 addView(mTabScrollView); 744 } 745 break; 746 } 747 mNavigationMode = mode; 748 requestLayout(); 749 } 750 } 751 752 public void setDropdownParams(SpinnerAdapter adapter, AdapterView.OnItemSelectedListener l) { 753 mSpinnerAdapter = adapter; 754 mNavItemSelectedListener = l; 755 if (mSpinner != null) { 756 mSpinner.setAdapter(adapter); 757 mSpinner.setOnItemSelectedListener(l); 758 } 759 } 760 761 public int getDropdownItemCount() { 762 return mSpinnerAdapter != null ? mSpinnerAdapter.getCount() : 0; 763 } 764 765 public void setDropdownSelectedPosition(int position) { 766 mSpinner.setSelection(position); 767 } 768 769 public int getDropdownSelectedPosition() { 770 return mSpinner.getSelectedItemPosition(); 771 } 772 773 public View getCustomView() { 774 return mCustomNavView; 775 } 776 777 public int getNavigationMode() { 778 return mNavigationMode; 779 } 780 781 public int getDisplayOptions() { 782 return mDisplayOptions; 783 } 784 785 @Override 786 public ViewGroup getViewGroup() { 787 return this; 788 } 789 790 @Override 791 protected ViewGroup.LayoutParams generateDefaultLayoutParams() { 792 // Used by custom nav views if they don't supply layout params. Everything else 793 // added to an ActionBarView should have them already. 794 return new ActionBar.LayoutParams(DEFAULT_CUSTOM_GRAVITY); 795 } 796 797 @Override 798 protected void onFinishInflate() { 799 super.onFinishInflate(); 800 801 mUpGoerFive.addView(mHomeLayout, 0); 802 addView(mUpGoerFive); 803 804 if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { 805 final ViewParent parent = mCustomNavView.getParent(); 806 if (parent != this) { 807 if (parent instanceof ViewGroup) { 808 ((ViewGroup) parent).removeView(mCustomNavView); 809 } 810 addView(mCustomNavView); 811 } 812 } 813 } 814 815 private void initTitle() { 816 if (mTitleLayout == null) { 817 LayoutInflater inflater = LayoutInflater.from(getContext()); 818 mTitleLayout = (LinearLayout) inflater.inflate(R.layout.action_bar_title_item, 819 this, false); 820 mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title); 821 mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle); 822 823 if (mTitleStyleRes != 0) { 824 mTitleView.setTextAppearance(mContext, mTitleStyleRes); 825 } 826 if (mTitle != null) { 827 mTitleView.setText(mTitle); 828 } 829 830 if (mSubtitleStyleRes != 0) { 831 mSubtitleView.setTextAppearance(mContext, mSubtitleStyleRes); 832 } 833 if (mSubtitle != null) { 834 mSubtitleView.setText(mSubtitle); 835 mSubtitleView.setVisibility(VISIBLE); 836 } 837 } 838 839 ActionBarTransition.beginDelayedTransition(this); 840 mUpGoerFive.addView(mTitleLayout); 841 if (mExpandedActionView != null || 842 (TextUtils.isEmpty(mTitle) && TextUtils.isEmpty(mSubtitle))) { 843 // Don't show while in expanded mode or with empty text 844 mTitleLayout.setVisibility(GONE); 845 } else { 846 mTitleLayout.setVisibility(VISIBLE); 847 } 848 } 849 850 public void setContextView(ActionBarContextView view) { 851 mContextView = view; 852 } 853 854 public void setCollapsible(boolean collapsible) { 855 mIsCollapsible = collapsible; 856 } 857 858 /** 859 * @return True if any characters in the title were truncated 860 */ 861 public boolean isTitleTruncated() { 862 if (mTitleView == null) { 863 return false; 864 } 865 866 final Layout titleLayout = mTitleView.getLayout(); 867 if (titleLayout == null) { 868 return false; 869 } 870 871 final int lineCount = titleLayout.getLineCount(); 872 for (int i = 0; i < lineCount; i++) { 873 if (titleLayout.getEllipsisCount(i) > 0) { 874 return true; 875 } 876 } 877 return false; 878 } 879 880 @Override 881 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 882 final int childCount = getChildCount(); 883 if (mIsCollapsible) { 884 int visibleChildren = 0; 885 for (int i = 0; i < childCount; i++) { 886 final View child = getChildAt(i); 887 if (child.getVisibility() != GONE && 888 !(child == mMenuView && mMenuView.getChildCount() == 0) && 889 child != mUpGoerFive) { 890 visibleChildren++; 891 } 892 } 893 894 final int upChildCount = mUpGoerFive.getChildCount(); 895 for (int i = 0; i < upChildCount; i++) { 896 final View child = mUpGoerFive.getChildAt(i); 897 if (child.getVisibility() != GONE) { 898 visibleChildren++; 899 } 900 } 901 902 if (visibleChildren == 0) { 903 // No size for an empty action bar when collapsable. 904 setMeasuredDimension(0, 0); 905 return; 906 } 907 } 908 909 int widthMode = MeasureSpec.getMode(widthMeasureSpec); 910 if (widthMode != MeasureSpec.EXACTLY) { 911 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + 912 "with android:layout_width=\"match_parent\" (or fill_parent)"); 913 } 914 915 int heightMode = MeasureSpec.getMode(heightMeasureSpec); 916 if (heightMode != MeasureSpec.AT_MOST) { 917 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + 918 "with android:layout_height=\"wrap_content\""); 919 } 920 921 int contentWidth = MeasureSpec.getSize(widthMeasureSpec); 922 923 int maxHeight = mContentHeight >= 0 ? 924 mContentHeight : MeasureSpec.getSize(heightMeasureSpec); 925 926 final int verticalPadding = getPaddingTop() + getPaddingBottom(); 927 final int paddingLeft = getPaddingLeft(); 928 final int paddingRight = getPaddingRight(); 929 final int height = maxHeight - verticalPadding; 930 final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST); 931 final int exactHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); 932 933 int availableWidth = contentWidth - paddingLeft - paddingRight; 934 int leftOfCenter = availableWidth / 2; 935 int rightOfCenter = leftOfCenter; 936 937 final boolean showTitle = mTitleLayout != null && mTitleLayout.getVisibility() != GONE && 938 (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0; 939 940 HomeView homeLayout = mExpandedActionView != null ? mExpandedHomeLayout : mHomeLayout; 941 942 final ViewGroup.LayoutParams homeLp = homeLayout.getLayoutParams(); 943 int homeWidthSpec; 944 if (homeLp.width < 0) { 945 homeWidthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST); 946 } else { 947 homeWidthSpec = MeasureSpec.makeMeasureSpec(homeLp.width, MeasureSpec.EXACTLY); 948 } 949 950 /* 951 * This is a little weird. 952 * We're only measuring the *home* affordance within the Up container here 953 * on purpose, because we want to give the available space to all other views before 954 * the title text. We'll remeasure the whole up container again later. 955 * We need to measure this container so we know the right offset for the up affordance 956 * no matter what. 957 */ 958 homeLayout.measure(homeWidthSpec, exactHeightSpec); 959 960 int homeWidth = 0; 961 if ((homeLayout.getVisibility() != GONE && homeLayout.getParent() == mUpGoerFive) 962 || showTitle) { 963 homeWidth = homeLayout.getMeasuredWidth(); 964 final int homeOffsetWidth = homeWidth + homeLayout.getStartOffset(); 965 availableWidth = Math.max(0, availableWidth - homeOffsetWidth); 966 leftOfCenter = Math.max(0, availableWidth - homeOffsetWidth); 967 } 968 969 if (mMenuView != null && mMenuView.getParent() == this) { 970 availableWidth = measureChildView(mMenuView, availableWidth, exactHeightSpec, 0); 971 rightOfCenter = Math.max(0, rightOfCenter - mMenuView.getMeasuredWidth()); 972 } 973 974 if (mIndeterminateProgressView != null && 975 mIndeterminateProgressView.getVisibility() != GONE) { 976 availableWidth = measureChildView(mIndeterminateProgressView, availableWidth, 977 childSpecHeight, 0); 978 rightOfCenter = Math.max(0, 979 rightOfCenter - mIndeterminateProgressView.getMeasuredWidth()); 980 } 981 982 if (mExpandedActionView == null) { 983 switch (mNavigationMode) { 984 case ActionBar.NAVIGATION_MODE_LIST: 985 if (mListNavLayout != null) { 986 final int itemPaddingSize = showTitle ? mItemPadding * 2 : mItemPadding; 987 availableWidth = Math.max(0, availableWidth - itemPaddingSize); 988 leftOfCenter = Math.max(0, leftOfCenter - itemPaddingSize); 989 mListNavLayout.measure( 990 MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), 991 MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); 992 final int listNavWidth = mListNavLayout.getMeasuredWidth(); 993 availableWidth = Math.max(0, availableWidth - listNavWidth); 994 leftOfCenter = Math.max(0, leftOfCenter - listNavWidth); 995 } 996 break; 997 case ActionBar.NAVIGATION_MODE_TABS: 998 if (mTabScrollView != null) { 999 final int itemPaddingSize = showTitle ? mItemPadding * 2 : mItemPadding; 1000 availableWidth = Math.max(0, availableWidth - itemPaddingSize); 1001 leftOfCenter = Math.max(0, leftOfCenter - itemPaddingSize); 1002 mTabScrollView.measure( 1003 MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), 1004 MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); 1005 final int tabWidth = mTabScrollView.getMeasuredWidth(); 1006 availableWidth = Math.max(0, availableWidth - tabWidth); 1007 leftOfCenter = Math.max(0, leftOfCenter - tabWidth); 1008 } 1009 break; 1010 } 1011 } 1012 1013 View customView = null; 1014 if (mExpandedActionView != null) { 1015 customView = mExpandedActionView; 1016 } else if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && 1017 mCustomNavView != null) { 1018 customView = mCustomNavView; 1019 } 1020 1021 if (customView != null) { 1022 final ViewGroup.LayoutParams lp = generateLayoutParams(customView.getLayoutParams()); 1023 final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ? 1024 (ActionBar.LayoutParams) lp : null; 1025 1026 int horizontalMargin = 0; 1027 int verticalMargin = 0; 1028 if (ablp != null) { 1029 horizontalMargin = ablp.leftMargin + ablp.rightMargin; 1030 verticalMargin = ablp.topMargin + ablp.bottomMargin; 1031 } 1032 1033 // If the action bar is wrapping to its content height, don't allow a custom 1034 // view to MATCH_PARENT. 1035 int customNavHeightMode; 1036 if (mContentHeight <= 0) { 1037 customNavHeightMode = MeasureSpec.AT_MOST; 1038 } else { 1039 customNavHeightMode = lp.height != LayoutParams.WRAP_CONTENT ? 1040 MeasureSpec.EXACTLY : MeasureSpec.AT_MOST; 1041 } 1042 final int customNavHeight = Math.max(0, 1043 (lp.height >= 0 ? Math.min(lp.height, height) : height) - verticalMargin); 1044 1045 final int customNavWidthMode = lp.width != LayoutParams.WRAP_CONTENT ? 1046 MeasureSpec.EXACTLY : MeasureSpec.AT_MOST; 1047 int customNavWidth = Math.max(0, 1048 (lp.width >= 0 ? Math.min(lp.width, availableWidth) : availableWidth) 1049 - horizontalMargin); 1050 final int hgrav = (ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY) & 1051 Gravity.HORIZONTAL_GRAVITY_MASK; 1052 1053 // Centering a custom view is treated specially; we try to center within the whole 1054 // action bar rather than in the available space. 1055 if (hgrav == Gravity.CENTER_HORIZONTAL && lp.width == LayoutParams.MATCH_PARENT) { 1056 customNavWidth = Math.min(leftOfCenter, rightOfCenter) * 2; 1057 } 1058 1059 customView.measure( 1060 MeasureSpec.makeMeasureSpec(customNavWidth, customNavWidthMode), 1061 MeasureSpec.makeMeasureSpec(customNavHeight, customNavHeightMode)); 1062 availableWidth -= horizontalMargin + customView.getMeasuredWidth(); 1063 } 1064 1065 /* 1066 * Measure the whole up container now, allowing for the full home+title sections. 1067 * (This will re-measure the home view.) 1068 */ 1069 availableWidth = measureChildView(mUpGoerFive, availableWidth + homeWidth, 1070 MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.EXACTLY), 0); 1071 if (mTitleLayout != null) { 1072 leftOfCenter = Math.max(0, leftOfCenter - mTitleLayout.getMeasuredWidth()); 1073 } 1074 1075 if (mContentHeight <= 0) { 1076 int measuredHeight = 0; 1077 for (int i = 0; i < childCount; i++) { 1078 View v = getChildAt(i); 1079 int paddedViewHeight = v.getMeasuredHeight() + verticalPadding; 1080 if (paddedViewHeight > measuredHeight) { 1081 measuredHeight = paddedViewHeight; 1082 } 1083 } 1084 setMeasuredDimension(contentWidth, measuredHeight); 1085 } else { 1086 setMeasuredDimension(contentWidth, maxHeight); 1087 } 1088 1089 if (mContextView != null) { 1090 mContextView.setContentHeight(getMeasuredHeight()); 1091 } 1092 1093 if (mProgressView != null && mProgressView.getVisibility() != GONE) { 1094 mProgressView.measure(MeasureSpec.makeMeasureSpec( 1095 contentWidth - mProgressBarPadding * 2, MeasureSpec.EXACTLY), 1096 MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST)); 1097 } 1098 } 1099 1100 @Override 1101 protected void onLayout(boolean changed, int l, int t, int r, int b) { 1102 final int contentHeight = b - t - getPaddingTop() - getPaddingBottom(); 1103 1104 if (contentHeight <= 0) { 1105 // Nothing to do if we can't see anything. 1106 return; 1107 } 1108 1109 final boolean isLayoutRtl = isLayoutRtl(); 1110 final int direction = isLayoutRtl ? 1 : -1; 1111 int menuStart = isLayoutRtl ? getPaddingLeft() : r - l - getPaddingRight(); 1112 // In LTR mode, we start from left padding and go to the right; in RTL mode, we start 1113 // from the padding right and go to the left (in reverse way) 1114 int x = isLayoutRtl ? r - l - getPaddingRight() : getPaddingLeft(); 1115 final int y = getPaddingTop(); 1116 1117 HomeView homeLayout = mExpandedActionView != null ? mExpandedHomeLayout : mHomeLayout; 1118 final boolean showTitle = mTitleLayout != null && mTitleLayout.getVisibility() != GONE && 1119 (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0; 1120 int startOffset = 0; 1121 if (homeLayout.getParent() == mUpGoerFive) { 1122 if (homeLayout.getVisibility() != GONE) { 1123 startOffset = homeLayout.getStartOffset(); 1124 } else if (showTitle) { 1125 startOffset = homeLayout.getUpWidth(); 1126 } 1127 } 1128 1129 // Position the up container based on where the edge of the home layout should go. 1130 x += positionChild(mUpGoerFive, 1131 next(x, startOffset, isLayoutRtl), y, contentHeight, isLayoutRtl); 1132 x = next(x, startOffset, isLayoutRtl); 1133 1134 if (mExpandedActionView == null) { 1135 switch (mNavigationMode) { 1136 case ActionBar.NAVIGATION_MODE_STANDARD: 1137 break; 1138 case ActionBar.NAVIGATION_MODE_LIST: 1139 if (mListNavLayout != null) { 1140 if (showTitle) { 1141 x = next(x, mItemPadding, isLayoutRtl); 1142 } 1143 x += positionChild(mListNavLayout, x, y, contentHeight, isLayoutRtl); 1144 x = next(x, mItemPadding, isLayoutRtl); 1145 } 1146 break; 1147 case ActionBar.NAVIGATION_MODE_TABS: 1148 if (mTabScrollView != null) { 1149 if (showTitle) x = next(x, mItemPadding, isLayoutRtl); 1150 x += positionChild(mTabScrollView, x, y, contentHeight, isLayoutRtl); 1151 x = next(x, mItemPadding, isLayoutRtl); 1152 } 1153 break; 1154 } 1155 } 1156 1157 if (mMenuView != null && mMenuView.getParent() == this) { 1158 positionChild(mMenuView, menuStart, y, contentHeight, !isLayoutRtl); 1159 menuStart += direction * mMenuView.getMeasuredWidth(); 1160 } 1161 1162 if (mIndeterminateProgressView != null && 1163 mIndeterminateProgressView.getVisibility() != GONE) { 1164 positionChild(mIndeterminateProgressView, menuStart, y, contentHeight, !isLayoutRtl); 1165 menuStart += direction * mIndeterminateProgressView.getMeasuredWidth(); 1166 } 1167 1168 View customView = null; 1169 if (mExpandedActionView != null) { 1170 customView = mExpandedActionView; 1171 } else if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && 1172 mCustomNavView != null) { 1173 customView = mCustomNavView; 1174 } 1175 if (customView != null) { 1176 final int layoutDirection = getLayoutDirection(); 1177 ViewGroup.LayoutParams lp = customView.getLayoutParams(); 1178 final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ? 1179 (ActionBar.LayoutParams) lp : null; 1180 final int gravity = ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY; 1181 final int navWidth = customView.getMeasuredWidth(); 1182 1183 int topMargin = 0; 1184 int bottomMargin = 0; 1185 if (ablp != null) { 1186 x = next(x, ablp.getMarginStart(), isLayoutRtl); 1187 menuStart += direction * ablp.getMarginEnd(); 1188 topMargin = ablp.topMargin; 1189 bottomMargin = ablp.bottomMargin; 1190 } 1191 1192 int hgravity = gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK; 1193 // See if we actually have room to truly center; if not push against left or right. 1194 if (hgravity == Gravity.CENTER_HORIZONTAL) { 1195 final int centeredLeft = ((mRight - mLeft) - navWidth) / 2; 1196 if (isLayoutRtl) { 1197 final int centeredStart = centeredLeft + navWidth; 1198 final int centeredEnd = centeredLeft; 1199 if (centeredStart > x) { 1200 hgravity = Gravity.RIGHT; 1201 } else if (centeredEnd < menuStart) { 1202 hgravity = Gravity.LEFT; 1203 } 1204 } else { 1205 final int centeredStart = centeredLeft; 1206 final int centeredEnd = centeredLeft + navWidth; 1207 if (centeredStart < x) { 1208 hgravity = Gravity.LEFT; 1209 } else if (centeredEnd > menuStart) { 1210 hgravity = Gravity.RIGHT; 1211 } 1212 } 1213 } else if (gravity == Gravity.NO_GRAVITY) { 1214 hgravity = Gravity.START; 1215 } 1216 1217 int xpos = 0; 1218 switch (Gravity.getAbsoluteGravity(hgravity, layoutDirection)) { 1219 case Gravity.CENTER_HORIZONTAL: 1220 xpos = ((mRight - mLeft) - navWidth) / 2; 1221 break; 1222 case Gravity.LEFT: 1223 xpos = isLayoutRtl ? menuStart : x; 1224 break; 1225 case Gravity.RIGHT: 1226 xpos = isLayoutRtl ? x - navWidth : menuStart - navWidth; 1227 break; 1228 } 1229 1230 int vgravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; 1231 1232 if (gravity == Gravity.NO_GRAVITY) { 1233 vgravity = Gravity.CENTER_VERTICAL; 1234 } 1235 1236 int ypos = 0; 1237 switch (vgravity) { 1238 case Gravity.CENTER_VERTICAL: 1239 final int paddedTop = getPaddingTop(); 1240 final int paddedBottom = mBottom - mTop - getPaddingBottom(); 1241 ypos = ((paddedBottom - paddedTop) - customView.getMeasuredHeight()) / 2; 1242 break; 1243 case Gravity.TOP: 1244 ypos = getPaddingTop() + topMargin; 1245 break; 1246 case Gravity.BOTTOM: 1247 ypos = getHeight() - getPaddingBottom() - customView.getMeasuredHeight() 1248 - bottomMargin; 1249 break; 1250 } 1251 final int customWidth = customView.getMeasuredWidth(); 1252 customView.layout(xpos, ypos, xpos + customWidth, 1253 ypos + customView.getMeasuredHeight()); 1254 x = next(x, customWidth, isLayoutRtl); 1255 } 1256 1257 if (mProgressView != null) { 1258 mProgressView.bringToFront(); 1259 final int halfProgressHeight = mProgressView.getMeasuredHeight() / 2; 1260 mProgressView.layout(mProgressBarPadding, -halfProgressHeight, 1261 mProgressBarPadding + mProgressView.getMeasuredWidth(), halfProgressHeight); 1262 } 1263 } 1264 1265 @Override 1266 public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { 1267 return new ActionBar.LayoutParams(getContext(), attrs); 1268 } 1269 1270 @Override 1271 public ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { 1272 if (lp == null) { 1273 lp = generateDefaultLayoutParams(); 1274 } 1275 return lp; 1276 } 1277 1278 @Override 1279 public Parcelable onSaveInstanceState() { 1280 Parcelable superState = super.onSaveInstanceState(); 1281 SavedState state = new SavedState(superState); 1282 1283 if (mExpandedMenuPresenter != null && mExpandedMenuPresenter.mCurrentExpandedItem != null) { 1284 state.expandedMenuItemId = mExpandedMenuPresenter.mCurrentExpandedItem.getItemId(); 1285 } 1286 1287 state.isOverflowOpen = isOverflowMenuShowing(); 1288 1289 return state; 1290 } 1291 1292 @Override 1293 public void onRestoreInstanceState(Parcelable p) { 1294 SavedState state = (SavedState) p; 1295 1296 super.onRestoreInstanceState(state.getSuperState()); 1297 1298 if (state.expandedMenuItemId != 0 && 1299 mExpandedMenuPresenter != null && mOptionsMenu != null) { 1300 final MenuItem item = mOptionsMenu.findItem(state.expandedMenuItemId); 1301 if (item != null) { 1302 item.expandActionView(); 1303 } 1304 } 1305 1306 if (state.isOverflowOpen) { 1307 postShowOverflowMenu(); 1308 } 1309 } 1310 1311 public void setNavigationIcon(Drawable indicator) { 1312 mHomeLayout.setUpIndicator(indicator); 1313 } 1314 1315 @Override 1316 public void setDefaultNavigationIcon(Drawable icon) { 1317 mHomeLayout.setDefaultUpIndicator(icon); 1318 } 1319 1320 public void setNavigationIcon(int resId) { 1321 mHomeLayout.setUpIndicator(resId); 1322 } 1323 1324 public void setNavigationContentDescription(CharSequence description) { 1325 mHomeDescription = description; 1326 updateHomeAccessibility(mUpGoerFive.isEnabled()); 1327 } 1328 1329 public void setNavigationContentDescription(int resId) { 1330 mHomeDescriptionRes = resId; 1331 mHomeDescription = resId != 0 ? getResources().getText(resId) : null; 1332 updateHomeAccessibility(mUpGoerFive.isEnabled()); 1333 } 1334 1335 @Override 1336 public void setDefaultNavigationContentDescription(int defaultNavigationContentDescription) { 1337 if (mDefaultUpDescription == defaultNavigationContentDescription) { 1338 return; 1339 } 1340 mDefaultUpDescription = defaultNavigationContentDescription; 1341 updateHomeAccessibility(mUpGoerFive.isEnabled()); 1342 } 1343 1344 static class SavedState extends BaseSavedState { 1345 int expandedMenuItemId; 1346 boolean isOverflowOpen; 1347 1348 SavedState(Parcelable superState) { 1349 super(superState); 1350 } 1351 1352 private SavedState(Parcel in) { 1353 super(in); 1354 expandedMenuItemId = in.readInt(); 1355 isOverflowOpen = in.readInt() != 0; 1356 } 1357 1358 @Override 1359 public void writeToParcel(Parcel out, int flags) { 1360 super.writeToParcel(out, flags); 1361 out.writeInt(expandedMenuItemId); 1362 out.writeInt(isOverflowOpen ? 1 : 0); 1363 } 1364 1365 public static final Parcelable.Creator<SavedState> CREATOR = 1366 new Parcelable.Creator<SavedState>() { 1367 public SavedState createFromParcel(Parcel in) { 1368 return new SavedState(in); 1369 } 1370 1371 public SavedState[] newArray(int size) { 1372 return new SavedState[size]; 1373 } 1374 }; 1375 } 1376 1377 private static class HomeView extends FrameLayout { 1378 private ImageView mUpView; 1379 private ImageView mIconView; 1380 private int mUpWidth; 1381 private int mStartOffset; 1382 private int mUpIndicatorRes; 1383 private Drawable mDefaultUpIndicator; 1384 private Drawable mUpIndicator; 1385 1386 private static final long DEFAULT_TRANSITION_DURATION = 150; 1387 1388 public HomeView(Context context) { 1389 this(context, null); 1390 } 1391 1392 public HomeView(Context context, AttributeSet attrs) { 1393 super(context, attrs); 1394 LayoutTransition t = getLayoutTransition(); 1395 if (t != null) { 1396 // Set a lower duration than the default 1397 t.setDuration(DEFAULT_TRANSITION_DURATION); 1398 } 1399 } 1400 1401 public void setShowUp(boolean isUp) { 1402 mUpView.setVisibility(isUp ? VISIBLE : GONE); 1403 } 1404 1405 public void setShowIcon(boolean showIcon) { 1406 mIconView.setVisibility(showIcon ? VISIBLE : GONE); 1407 } 1408 1409 public void setIcon(Drawable icon) { 1410 mIconView.setImageDrawable(icon); 1411 } 1412 1413 public void setUpIndicator(Drawable d) { 1414 mUpIndicator = d; 1415 mUpIndicatorRes = 0; 1416 updateUpIndicator(); 1417 } 1418 1419 public void setDefaultUpIndicator(Drawable d) { 1420 mDefaultUpIndicator = d; 1421 updateUpIndicator(); 1422 } 1423 1424 public void setUpIndicator(int resId) { 1425 mUpIndicatorRes = resId; 1426 mUpIndicator = null; 1427 updateUpIndicator(); 1428 } 1429 1430 private void updateUpIndicator() { 1431 if (mUpIndicator != null) { 1432 mUpView.setImageDrawable(mUpIndicator); 1433 } else if (mUpIndicatorRes != 0) { 1434 mUpView.setImageDrawable(getContext().getDrawable(mUpIndicatorRes)); 1435 } else { 1436 mUpView.setImageDrawable(mDefaultUpIndicator); 1437 } 1438 } 1439 1440 @Override 1441 protected void onConfigurationChanged(Configuration newConfig) { 1442 super.onConfigurationChanged(newConfig); 1443 if (mUpIndicatorRes != 0) { 1444 // Reload for config change 1445 updateUpIndicator(); 1446 } 1447 } 1448 1449 @Override 1450 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 1451 onPopulateAccessibilityEvent(event); 1452 return true; 1453 } 1454 1455 @Override 1456 public void onPopulateAccessibilityEvent(AccessibilityEvent event) { 1457 super.onPopulateAccessibilityEvent(event); 1458 final CharSequence cdesc = getContentDescription(); 1459 if (!TextUtils.isEmpty(cdesc)) { 1460 event.getText().add(cdesc); 1461 } 1462 } 1463 1464 @Override 1465 public boolean dispatchHoverEvent(MotionEvent event) { 1466 // Don't allow children to hover; we want this to be treated as a single component. 1467 return onHoverEvent(event); 1468 } 1469 1470 @Override 1471 protected void onFinishInflate() { 1472 mUpView = (ImageView) findViewById(com.android.internal.R.id.up); 1473 mIconView = (ImageView) findViewById(com.android.internal.R.id.home); 1474 mDefaultUpIndicator = mUpView.getDrawable(); 1475 } 1476 1477 public int getStartOffset() { 1478 return mUpView.getVisibility() == GONE ? mStartOffset : 0; 1479 } 1480 1481 public int getUpWidth() { 1482 return mUpWidth; 1483 } 1484 1485 @Override 1486 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 1487 measureChildWithMargins(mUpView, widthMeasureSpec, 0, heightMeasureSpec, 0); 1488 final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams(); 1489 final int upMargins = upLp.leftMargin + upLp.rightMargin; 1490 mUpWidth = mUpView.getMeasuredWidth(); 1491 mStartOffset = mUpWidth + upMargins; 1492 int width = mUpView.getVisibility() == GONE ? 0 : mStartOffset; 1493 int height = upLp.topMargin + mUpView.getMeasuredHeight() + upLp.bottomMargin; 1494 1495 if (mIconView.getVisibility() != GONE) { 1496 measureChildWithMargins(mIconView, widthMeasureSpec, width, heightMeasureSpec, 0); 1497 final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams(); 1498 width += iconLp.leftMargin + mIconView.getMeasuredWidth() + iconLp.rightMargin; 1499 height = Math.max(height, 1500 iconLp.topMargin + mIconView.getMeasuredHeight() + iconLp.bottomMargin); 1501 } else if (upMargins < 0) { 1502 // Remove the measurement effects of negative margins used for offsets 1503 width -= upMargins; 1504 } 1505 1506 final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 1507 final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 1508 final int widthSize = MeasureSpec.getSize(widthMeasureSpec); 1509 final int heightSize = MeasureSpec.getSize(heightMeasureSpec); 1510 1511 switch (widthMode) { 1512 case MeasureSpec.AT_MOST: 1513 width = Math.min(width, widthSize); 1514 break; 1515 case MeasureSpec.EXACTLY: 1516 width = widthSize; 1517 break; 1518 case MeasureSpec.UNSPECIFIED: 1519 default: 1520 break; 1521 } 1522 switch (heightMode) { 1523 case MeasureSpec.AT_MOST: 1524 height = Math.min(height, heightSize); 1525 break; 1526 case MeasureSpec.EXACTLY: 1527 height = heightSize; 1528 break; 1529 case MeasureSpec.UNSPECIFIED: 1530 default: 1531 break; 1532 } 1533 setMeasuredDimension(width, height); 1534 } 1535 1536 @Override 1537 protected void onLayout(boolean changed, int l, int t, int r, int b) { 1538 final int vCenter = (b - t) / 2; 1539 final boolean isLayoutRtl = isLayoutRtl(); 1540 final int width = getWidth(); 1541 int upOffset = 0; 1542 if (mUpView.getVisibility() != GONE) { 1543 final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams(); 1544 final int upHeight = mUpView.getMeasuredHeight(); 1545 final int upWidth = mUpView.getMeasuredWidth(); 1546 upOffset = upLp.leftMargin + upWidth + upLp.rightMargin; 1547 final int upTop = vCenter - upHeight / 2; 1548 final int upBottom = upTop + upHeight; 1549 final int upRight; 1550 final int upLeft; 1551 if (isLayoutRtl) { 1552 upRight = width; 1553 upLeft = upRight - upWidth; 1554 r -= upOffset; 1555 } else { 1556 upRight = upWidth; 1557 upLeft = 0; 1558 l += upOffset; 1559 } 1560 mUpView.layout(upLeft, upTop, upRight, upBottom); 1561 } 1562 1563 final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams(); 1564 final int iconHeight = mIconView.getMeasuredHeight(); 1565 final int iconWidth = mIconView.getMeasuredWidth(); 1566 final int hCenter = (r - l) / 2; 1567 final int iconTop = Math.max(iconLp.topMargin, vCenter - iconHeight / 2); 1568 final int iconBottom = iconTop + iconHeight; 1569 final int iconLeft; 1570 final int iconRight; 1571 int marginStart = iconLp.getMarginStart(); 1572 final int delta = Math.max(marginStart, hCenter - iconWidth / 2); 1573 if (isLayoutRtl) { 1574 iconRight = width - upOffset - delta; 1575 iconLeft = iconRight - iconWidth; 1576 } else { 1577 iconLeft = upOffset + delta; 1578 iconRight = iconLeft + iconWidth; 1579 } 1580 mIconView.layout(iconLeft, iconTop, iconRight, iconBottom); 1581 } 1582 } 1583 1584 private class ExpandedActionViewMenuPresenter implements MenuPresenter { 1585 MenuBuilder mMenu; 1586 MenuItemImpl mCurrentExpandedItem; 1587 1588 @Override 1589 public void initForMenu(Context context, MenuBuilder menu) { 1590 // Clear the expanded action view when menus change. 1591 if (mMenu != null && mCurrentExpandedItem != null) { 1592 mMenu.collapseItemActionView(mCurrentExpandedItem); 1593 } 1594 mMenu = menu; 1595 } 1596 1597 @Override 1598 public MenuView getMenuView(ViewGroup root) { 1599 return null; 1600 } 1601 1602 @Override 1603 public void updateMenuView(boolean cleared) { 1604 // Make sure the expanded item we have is still there. 1605 if (mCurrentExpandedItem != null) { 1606 boolean found = false; 1607 1608 if (mMenu != null) { 1609 final int count = mMenu.size(); 1610 for (int i = 0; i < count; i++) { 1611 final MenuItem item = mMenu.getItem(i); 1612 if (item == mCurrentExpandedItem) { 1613 found = true; 1614 break; 1615 } 1616 } 1617 } 1618 1619 if (!found) { 1620 // The item we had expanded disappeared. Collapse. 1621 collapseItemActionView(mMenu, mCurrentExpandedItem); 1622 } 1623 } 1624 } 1625 1626 @Override 1627 public void setCallback(Callback cb) { 1628 } 1629 1630 @Override 1631 public boolean onSubMenuSelected(SubMenuBuilder subMenu) { 1632 return false; 1633 } 1634 1635 @Override 1636 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { 1637 } 1638 1639 @Override 1640 public boolean flagActionItems() { 1641 return false; 1642 } 1643 1644 @Override 1645 public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) { 1646 ActionBarTransition.beginDelayedTransition(ActionBarView.this); 1647 1648 mExpandedActionView = item.getActionView(); 1649 mExpandedHomeLayout.setIcon(mIcon.getConstantState().newDrawable(getResources())); 1650 mCurrentExpandedItem = item; 1651 if (mExpandedActionView.getParent() != ActionBarView.this) { 1652 addView(mExpandedActionView); 1653 } 1654 if (mExpandedHomeLayout.getParent() != mUpGoerFive) { 1655 mUpGoerFive.addView(mExpandedHomeLayout); 1656 } 1657 mHomeLayout.setVisibility(GONE); 1658 if (mTitleLayout != null) mTitleLayout.setVisibility(GONE); 1659 if (mTabScrollView != null) mTabScrollView.setVisibility(GONE); 1660 if (mSpinner != null) mSpinner.setVisibility(GONE); 1661 if (mCustomNavView != null) mCustomNavView.setVisibility(GONE); 1662 setHomeButtonEnabled(false, false); 1663 requestLayout(); 1664 item.setActionViewExpanded(true); 1665 1666 if (mExpandedActionView instanceof CollapsibleActionView) { 1667 ((CollapsibleActionView) mExpandedActionView).onActionViewExpanded(); 1668 } 1669 1670 return true; 1671 } 1672 1673 @Override 1674 public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) { 1675 ActionBarTransition.beginDelayedTransition(ActionBarView.this); 1676 1677 // Do this before detaching the actionview from the hierarchy, in case 1678 // it needs to dismiss the soft keyboard, etc. 1679 if (mExpandedActionView instanceof CollapsibleActionView) { 1680 ((CollapsibleActionView) mExpandedActionView).onActionViewCollapsed(); 1681 } 1682 1683 removeView(mExpandedActionView); 1684 mUpGoerFive.removeView(mExpandedHomeLayout); 1685 mExpandedActionView = null; 1686 if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_HOME) != 0) { 1687 mHomeLayout.setVisibility(VISIBLE); 1688 } 1689 if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 1690 if (mTitleLayout == null) { 1691 initTitle(); 1692 } else { 1693 mTitleLayout.setVisibility(VISIBLE); 1694 } 1695 } 1696 if (mTabScrollView != null) mTabScrollView.setVisibility(VISIBLE); 1697 if (mSpinner != null) mSpinner.setVisibility(VISIBLE); 1698 if (mCustomNavView != null) mCustomNavView.setVisibility(VISIBLE); 1699 1700 mExpandedHomeLayout.setIcon(null); 1701 mCurrentExpandedItem = null; 1702 setHomeButtonEnabled(mWasHomeEnabled); // Set by expandItemActionView above 1703 requestLayout(); 1704 item.setActionViewExpanded(false); 1705 1706 return true; 1707 } 1708 1709 @Override 1710 public int getId() { 1711 return 0; 1712 } 1713 1714 @Override 1715 public Parcelable onSaveInstanceState() { 1716 return null; 1717 } 1718 1719 @Override 1720 public void onRestoreInstanceState(Parcelable state) { 1721 } 1722 } 1723} 1724