ActionBarView.java revision e002c2fb39f9df1db38a7412832882ae622660d4
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.pm.ApplicationInfo; 23import android.content.pm.PackageManager; 24import android.content.res.Configuration; 25import android.content.res.TypedArray; 26import android.graphics.drawable.Drawable; 27import android.os.Parcel; 28import android.os.Parcelable; 29import android.text.Layout; 30import android.text.TextUtils; 31import android.util.AttributeSet; 32import android.view.CollapsibleActionView; 33import android.view.Gravity; 34import android.view.LayoutInflater; 35import android.view.Menu; 36import android.view.MenuItem; 37import android.view.MotionEvent; 38import android.view.View; 39import android.view.ViewGroup; 40import android.view.ViewParent; 41import android.view.Window; 42import android.view.accessibility.AccessibilityEvent; 43import android.widget.ActionMenuPresenter; 44import android.widget.ActionMenuView; 45import android.widget.AdapterView; 46import android.widget.FrameLayout; 47import android.widget.ImageView; 48import android.widget.LinearLayout; 49import android.widget.ProgressBar; 50import android.widget.Spinner; 51import android.widget.SpinnerAdapter; 52import android.widget.TextView; 53import com.android.internal.R; 54import com.android.internal.transition.ActionBarTransition; 55import com.android.internal.view.menu.ActionMenuItem; 56import com.android.internal.view.menu.MenuBuilder; 57import com.android.internal.view.menu.MenuItemImpl; 58import com.android.internal.view.menu.MenuPresenter; 59import com.android.internal.view.menu.MenuView; 60import com.android.internal.view.menu.SubMenuBuilder; 61 62/** 63 * @hide 64 */ 65public class ActionBarView extends AbsActionBarView implements DecorToolbar { 66 private static final String TAG = "ActionBarView"; 67 68 /** 69 * Display options applied by default 70 */ 71 public static final int DISPLAY_DEFAULT = 0; 72 73 /** 74 * Display options that require re-layout as opposed to a simple invalidate 75 */ 76 private static final int DISPLAY_RELAYOUT_MASK = 77 ActionBar.DISPLAY_SHOW_HOME | 78 ActionBar.DISPLAY_USE_LOGO | 79 ActionBar.DISPLAY_HOME_AS_UP | 80 ActionBar.DISPLAY_SHOW_CUSTOM | 81 ActionBar.DISPLAY_SHOW_TITLE | 82 ActionBar.DISPLAY_TITLE_MULTIPLE_LINES; 83 84 private static final int DEFAULT_CUSTOM_GRAVITY = Gravity.START | Gravity.CENTER_VERTICAL; 85 86 private int mNavigationMode; 87 private int mDisplayOptions = -1; 88 private CharSequence mTitle; 89 private CharSequence mSubtitle; 90 private Drawable mIcon; 91 private Drawable mLogo; 92 private CharSequence mHomeDescription; 93 private int mHomeDescriptionRes; 94 95 private HomeView mHomeLayout; 96 private HomeView mExpandedHomeLayout; 97 private LinearLayout mTitleLayout; 98 private TextView mTitleView; 99 private TextView mSubtitleView; 100 private ViewGroup mUpGoerFive; 101 102 private Spinner mSpinner; 103 private LinearLayout mListNavLayout; 104 private ScrollingTabContainerView mTabScrollView; 105 private View mCustomNavView; 106 private ProgressBar mProgressView; 107 private ProgressBar mIndeterminateProgressView; 108 109 private int mProgressBarPadding; 110 private int mItemPadding; 111 112 private int mTitleStyleRes; 113 private int mSubtitleStyleRes; 114 private int mProgressStyle; 115 private int mIndeterminateProgressStyle; 116 117 private boolean mUserTitle; 118 private boolean mIncludeTabs; 119 private boolean mIsCollapsible; 120 private boolean mWasHomeEnabled; // Was it enabled before action view expansion? 121 122 private MenuBuilder mOptionsMenu; 123 private boolean mMenuPrepared; 124 125 private ActionBarContextView mContextView; 126 127 private ActionMenuItem mLogoNavItem; 128 129 private SpinnerAdapter mSpinnerAdapter; 130 private AdapterView.OnItemSelectedListener mNavItemSelectedListener; 131 132 private Runnable mTabSelector; 133 134 private ExpandedActionViewMenuPresenter mExpandedMenuPresenter; 135 View mExpandedActionView; 136 137 Window.Callback mWindowCallback; 138 139 private final OnClickListener mExpandedActionViewUpListener = new OnClickListener() { 140 @Override 141 public void onClick(View v) { 142 final MenuItemImpl item = mExpandedMenuPresenter.mCurrentExpandedItem; 143 if (item != null) { 144 item.collapseActionView(); 145 } 146 } 147 }; 148 149 private final OnClickListener mUpClickListener = new OnClickListener() { 150 public void onClick(View v) { 151 if (mMenuPrepared) { 152 // Only invoke the window callback if the options menu has been initialized. 153 mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mLogoNavItem); 154 } 155 } 156 }; 157 158 public ActionBarView(Context context, AttributeSet attrs) { 159 super(context, attrs); 160 161 // Background is always provided by the container. 162 setBackgroundResource(0); 163 164 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionBar, 165 com.android.internal.R.attr.actionBarStyle, 0); 166 167 mNavigationMode = a.getInt(R.styleable.ActionBar_navigationMode, 168 ActionBar.NAVIGATION_MODE_STANDARD); 169 mTitle = a.getText(R.styleable.ActionBar_title); 170 mSubtitle = a.getText(R.styleable.ActionBar_subtitle); 171 mLogo = a.getDrawable(R.styleable.ActionBar_logo); 172 mIcon = a.getDrawable(R.styleable.ActionBar_icon); 173 174 final LayoutInflater inflater = LayoutInflater.from(context); 175 176 final int homeResId = a.getResourceId( 177 com.android.internal.R.styleable.ActionBar_homeLayout, 178 com.android.internal.R.layout.action_bar_home); 179 180 mUpGoerFive = (ViewGroup) inflater.inflate( 181 com.android.internal.R.layout.action_bar_up_container, this, false); 182 mHomeLayout = (HomeView) inflater.inflate(homeResId, mUpGoerFive, false); 183 184 mExpandedHomeLayout = (HomeView) inflater.inflate(homeResId, mUpGoerFive, false); 185 mExpandedHomeLayout.setShowUp(true); 186 mExpandedHomeLayout.setOnClickListener(mExpandedActionViewUpListener); 187 mExpandedHomeLayout.setContentDescription(getResources().getText( 188 R.string.action_bar_up_description)); 189 190 // This needs to highlight/be focusable on its own. 191 // TODO: Clean up the handoff between expanded/normal. 192 final Drawable upBackground = mUpGoerFive.getBackground(); 193 if (upBackground != null) { 194 mExpandedHomeLayout.setBackground(upBackground.getConstantState().newDrawable()); 195 } 196 mExpandedHomeLayout.setEnabled(true); 197 mExpandedHomeLayout.setFocusable(true); 198 199 mTitleStyleRes = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0); 200 mSubtitleStyleRes = a.getResourceId(R.styleable.ActionBar_subtitleTextStyle, 0); 201 mProgressStyle = a.getResourceId(R.styleable.ActionBar_progressBarStyle, 0); 202 mIndeterminateProgressStyle = a.getResourceId( 203 R.styleable.ActionBar_indeterminateProgressStyle, 0); 204 205 mProgressBarPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_progressBarPadding, 0); 206 mItemPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_itemPadding, 0); 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 223 mUpGoerFive.setOnClickListener(mUpClickListener); 224 mUpGoerFive.setClickable(true); 225 mUpGoerFive.setFocusable(true); 226 227 if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { 228 setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); 229 } 230 } 231 232 @Override 233 protected void onConfigurationChanged(Configuration newConfig) { 234 super.onConfigurationChanged(newConfig); 235 236 mTitleView = null; 237 mSubtitleView = null; 238 if (mTitleLayout != null && mTitleLayout.getParent() == mUpGoerFive) { 239 mUpGoerFive.removeView(mTitleLayout); 240 } 241 mTitleLayout = null; 242 if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 243 initTitle(); 244 } 245 246 if (mHomeDescriptionRes != 0) { 247 setNavigationContentDescription(mHomeDescriptionRes); 248 } 249 250 if (mTabScrollView != null && mIncludeTabs) { 251 ViewGroup.LayoutParams lp = mTabScrollView.getLayoutParams(); 252 if (lp != null) { 253 lp.width = LayoutParams.WRAP_CONTENT; 254 lp.height = LayoutParams.MATCH_PARENT; 255 } 256 mTabScrollView.setAllowCollapse(true); 257 } 258 } 259 260 /** 261 * Set the window callback used to invoke menu items; used for dispatching home button presses. 262 * @param cb Window callback to dispatch to 263 */ 264 public void setWindowCallback(Window.Callback cb) { 265 mWindowCallback = cb; 266 } 267 268 @Override 269 public void onDetachedFromWindow() { 270 super.onDetachedFromWindow(); 271 removeCallbacks(mTabSelector); 272 if (mActionMenuPresenter != null) { 273 mActionMenuPresenter.hideOverflowMenu(); 274 mActionMenuPresenter.hideSubMenus(); 275 } 276 } 277 278 @Override 279 public boolean shouldDelayChildPressedState() { 280 return false; 281 } 282 283 public void initProgress() { 284 mProgressView = new ProgressBar(mContext, null, 0, mProgressStyle); 285 mProgressView.setId(R.id.progress_horizontal); 286 mProgressView.setMax(10000); 287 mProgressView.setVisibility(GONE); 288 addView(mProgressView); 289 } 290 291 public void initIndeterminateProgress() { 292 mIndeterminateProgressView = new ProgressBar(mContext, null, 0, 293 mIndeterminateProgressStyle); 294 mIndeterminateProgressView.setId(R.id.progress_circular); 295 mIndeterminateProgressView.setVisibility(GONE); 296 addView(mIndeterminateProgressView); 297 } 298 299 @Override 300 public void setSplitToolbar(boolean splitActionBar) { 301 if (mSplitActionBar != splitActionBar) { 302 if (mMenuView != null) { 303 final ViewGroup oldParent = (ViewGroup) mMenuView.getParent(); 304 if (oldParent != null) { 305 oldParent.removeView(mMenuView); 306 } 307 if (splitActionBar) { 308 if (mSplitView != null) { 309 mSplitView.addView(mMenuView); 310 } 311 mMenuView.getLayoutParams().width = LayoutParams.MATCH_PARENT; 312 } else { 313 addView(mMenuView); 314 mMenuView.getLayoutParams().width = LayoutParams.WRAP_CONTENT; 315 } 316 mMenuView.requestLayout(); 317 } 318 if (mSplitView != null) { 319 mSplitView.setVisibility(splitActionBar ? VISIBLE : GONE); 320 } 321 322 if (mActionMenuPresenter != null) { 323 if (!splitActionBar) { 324 mActionMenuPresenter.setExpandedActionViewsExclusive( 325 getResources().getBoolean( 326 com.android.internal.R.bool.action_bar_expanded_action_views_exclusive)); 327 } else { 328 mActionMenuPresenter.setExpandedActionViewsExclusive(false); 329 // Allow full screen width in split mode. 330 mActionMenuPresenter.setWidthLimit( 331 getContext().getResources().getDisplayMetrics().widthPixels, true); 332 // No limit to the item count; use whatever will fit. 333 mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE); 334 } 335 } 336 super.setSplitToolbar(splitActionBar); 337 } 338 } 339 340 public boolean isSplit() { 341 return mSplitActionBar; 342 } 343 344 public boolean canSplit() { 345 return true; 346 } 347 348 public boolean hasEmbeddedTabs() { 349 return mIncludeTabs; 350 } 351 352 @Override 353 public void setEmbeddedTabView(ScrollingTabContainerView tabs) { 354 if (mTabScrollView != null) { 355 removeView(mTabScrollView); 356 } 357 mTabScrollView = tabs; 358 mIncludeTabs = tabs != null; 359 if (mIncludeTabs && mNavigationMode == ActionBar.NAVIGATION_MODE_TABS) { 360 addView(mTabScrollView); 361 ViewGroup.LayoutParams lp = mTabScrollView.getLayoutParams(); 362 lp.width = LayoutParams.WRAP_CONTENT; 363 lp.height = LayoutParams.MATCH_PARENT; 364 tabs.setAllowCollapse(true); 365 } 366 } 367 368 public void setMenuPrepared() { 369 mMenuPrepared = true; 370 } 371 372 public void setMenu(Menu menu, MenuPresenter.Callback cb) { 373 if (menu == mOptionsMenu) return; 374 375 if (mOptionsMenu != null) { 376 mOptionsMenu.removeMenuPresenter(mActionMenuPresenter); 377 mOptionsMenu.removeMenuPresenter(mExpandedMenuPresenter); 378 } 379 380 MenuBuilder builder = (MenuBuilder) menu; 381 mOptionsMenu = builder; 382 if (mMenuView != null) { 383 final ViewGroup oldParent = (ViewGroup) mMenuView.getParent(); 384 if (oldParent != null) { 385 oldParent.removeView(mMenuView); 386 } 387 } 388 if (mActionMenuPresenter == null) { 389 mActionMenuPresenter = new ActionMenuPresenter(mContext); 390 mActionMenuPresenter.setCallback(cb); 391 mActionMenuPresenter.setId(com.android.internal.R.id.action_menu_presenter); 392 mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter(); 393 } 394 395 ActionMenuView menuView; 396 final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, 397 LayoutParams.MATCH_PARENT); 398 if (!mSplitActionBar) { 399 mActionMenuPresenter.setExpandedActionViewsExclusive( 400 getResources().getBoolean( 401 com.android.internal.R.bool.action_bar_expanded_action_views_exclusive)); 402 configPresenters(builder); 403 menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); 404 final ViewGroup oldParent = (ViewGroup) menuView.getParent(); 405 if (oldParent != null && oldParent != this) { 406 oldParent.removeView(menuView); 407 } 408 addView(menuView, layoutParams); 409 } else { 410 mActionMenuPresenter.setExpandedActionViewsExclusive(false); 411 // Allow full screen width in split mode. 412 mActionMenuPresenter.setWidthLimit( 413 getContext().getResources().getDisplayMetrics().widthPixels, true); 414 // No limit to the item count; use whatever will fit. 415 mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE); 416 // Span the whole width 417 layoutParams.width = LayoutParams.MATCH_PARENT; 418 layoutParams.height = LayoutParams.WRAP_CONTENT; 419 configPresenters(builder); 420 menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); 421 if (mSplitView != null) { 422 final ViewGroup oldParent = (ViewGroup) menuView.getParent(); 423 if (oldParent != null && oldParent != mSplitView) { 424 oldParent.removeView(menuView); 425 } 426 menuView.setVisibility(getAnimatedVisibility()); 427 mSplitView.addView(menuView, layoutParams); 428 } else { 429 // We'll add this later if we missed it this time. 430 menuView.setLayoutParams(layoutParams); 431 } 432 } 433 mMenuView = menuView; 434 } 435 436 private void configPresenters(MenuBuilder builder) { 437 if (builder != null) { 438 builder.addMenuPresenter(mActionMenuPresenter); 439 builder.addMenuPresenter(mExpandedMenuPresenter); 440 } else { 441 mActionMenuPresenter.initForMenu(mContext, null); 442 mExpandedMenuPresenter.initForMenu(mContext, null); 443 mActionMenuPresenter.updateMenuView(true); 444 mExpandedMenuPresenter.updateMenuView(true); 445 } 446 } 447 448 public boolean hasExpandedActionView() { 449 return mExpandedMenuPresenter != null && 450 mExpandedMenuPresenter.mCurrentExpandedItem != null; 451 } 452 453 public void collapseActionView() { 454 final MenuItemImpl item = mExpandedMenuPresenter == null ? null : 455 mExpandedMenuPresenter.mCurrentExpandedItem; 456 if (item != null) { 457 item.collapseActionView(); 458 } 459 } 460 461 public void setCustomView(View view) { 462 final boolean showCustom = (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0; 463 if (showCustom) { 464 ActionBarTransition.beginDelayedTransition(this); 465 } 466 if (mCustomNavView != null && showCustom) { 467 removeView(mCustomNavView); 468 } 469 mCustomNavView = view; 470 if (mCustomNavView != null && showCustom) { 471 addView(mCustomNavView); 472 } 473 } 474 475 public CharSequence getTitle() { 476 return mTitle; 477 } 478 479 /** 480 * Set the action bar title. This will always replace or override window titles. 481 * @param title Title to set 482 * 483 * @see #setWindowTitle(CharSequence) 484 */ 485 public void setTitle(CharSequence title) { 486 mUserTitle = true; 487 setTitleImpl(title); 488 } 489 490 /** 491 * Set the window title. A window title will always be replaced or overridden by a user title. 492 * @param title Title to set 493 * 494 * @see #setTitle(CharSequence) 495 */ 496 public void setWindowTitle(CharSequence title) { 497 if (!mUserTitle) { 498 setTitleImpl(title); 499 } 500 } 501 502 private void setTitleImpl(CharSequence title) { 503 ActionBarTransition.beginDelayedTransition(this); 504 mTitle = title; 505 if (mTitleView != null) { 506 mTitleView.setText(title); 507 final boolean visible = mExpandedActionView == null && 508 (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0 && 509 (!TextUtils.isEmpty(mTitle) || !TextUtils.isEmpty(mSubtitle)); 510 mTitleLayout.setVisibility(visible ? VISIBLE : GONE); 511 } 512 if (mLogoNavItem != null) { 513 mLogoNavItem.setTitle(title); 514 } 515 updateHomeAccessibility(mUpGoerFive.isEnabled()); 516 } 517 518 public CharSequence getSubtitle() { 519 return mSubtitle; 520 } 521 522 public void setSubtitle(CharSequence subtitle) { 523 ActionBarTransition.beginDelayedTransition(this); 524 mSubtitle = subtitle; 525 if (mSubtitleView != null) { 526 mSubtitleView.setText(subtitle); 527 mSubtitleView.setVisibility(subtitle != null ? VISIBLE : GONE); 528 final boolean visible = mExpandedActionView == null && 529 (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0 && 530 (!TextUtils.isEmpty(mTitle) || !TextUtils.isEmpty(mSubtitle)); 531 mTitleLayout.setVisibility(visible ? VISIBLE : GONE); 532 } 533 updateHomeAccessibility(mUpGoerFive.isEnabled()); 534 } 535 536 public void setHomeButtonEnabled(boolean enable) { 537 setHomeButtonEnabled(enable, true); 538 } 539 540 private void setHomeButtonEnabled(boolean enable, boolean recordState) { 541 if (recordState) { 542 mWasHomeEnabled = enable; 543 } 544 545 if (mExpandedActionView != null) { 546 // There's an action view currently showing and we want to keep the state 547 // configured for the action view at the moment. If we needed to record the 548 // new state for later we will have done so above. 549 return; 550 } 551 552 mUpGoerFive.setEnabled(enable); 553 mUpGoerFive.setFocusable(enable); 554 // Make sure the home button has an accurate content description for accessibility. 555 updateHomeAccessibility(enable); 556 } 557 558 private void updateHomeAccessibility(boolean homeEnabled) { 559 if (!homeEnabled) { 560 mUpGoerFive.setContentDescription(null); 561 mUpGoerFive.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); 562 } else { 563 mUpGoerFive.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_AUTO); 564 mUpGoerFive.setContentDescription(buildHomeContentDescription()); 565 } 566 } 567 568 /** 569 * Compose a content description for the Home/Up affordance. 570 * 571 * <p>As this encompasses the icon/logo, title and subtitle all in one, we need 572 * a description for the whole wad of stuff that can be localized properly.</p> 573 */ 574 private CharSequence buildHomeContentDescription() { 575 final CharSequence homeDesc; 576 if (mHomeDescription != null) { 577 homeDesc = mHomeDescription; 578 } else { 579 if ((mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0) { 580 homeDesc = mContext.getResources().getText(R.string.action_bar_up_description); 581 } else { 582 homeDesc = mContext.getResources().getText(R.string.action_bar_home_description); 583 } 584 } 585 586 final CharSequence title = getTitle(); 587 final CharSequence subtitle = getSubtitle(); 588 if (!TextUtils.isEmpty(title)) { 589 final String result; 590 if (!TextUtils.isEmpty(subtitle)) { 591 result = getResources().getString( 592 R.string.action_bar_home_subtitle_description_format, 593 title, subtitle, homeDesc); 594 } else { 595 result = getResources().getString(R.string.action_bar_home_description_format, 596 title, homeDesc); 597 } 598 return result; 599 } 600 return homeDesc; 601 } 602 603 public void setDisplayOptions(int options) { 604 final int flagsChanged = mDisplayOptions == -1 ? -1 : options ^ mDisplayOptions; 605 mDisplayOptions = options; 606 607 if ((flagsChanged & DISPLAY_RELAYOUT_MASK) != 0) { 608 ActionBarTransition.beginDelayedTransition(this); 609 610 if ((flagsChanged & ActionBar.DISPLAY_HOME_AS_UP) != 0) { 611 final boolean setUp = (options & ActionBar.DISPLAY_HOME_AS_UP) != 0; 612 mHomeLayout.setShowUp(setUp); 613 614 // Showing home as up implicitly enables interaction with it. 615 // In honeycomb it was always enabled, so make this transition 616 // a bit easier for developers in the common case. 617 // (It would be silly to show it as up without responding to it.) 618 if (setUp) { 619 setHomeButtonEnabled(true); 620 } 621 } 622 623 if ((flagsChanged & ActionBar.DISPLAY_USE_LOGO) != 0) { 624 final boolean logoVis = mLogo != null && (options & ActionBar.DISPLAY_USE_LOGO) != 0; 625 mHomeLayout.setIcon(logoVis ? mLogo : mIcon); 626 } 627 628 if ((flagsChanged & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 629 if ((options & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 630 initTitle(); 631 } else { 632 mUpGoerFive.removeView(mTitleLayout); 633 } 634 } 635 636 final boolean showHome = (options & ActionBar.DISPLAY_SHOW_HOME) != 0; 637 final boolean homeAsUp = (mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0; 638 final boolean titleUp = !showHome && homeAsUp; 639 mHomeLayout.setShowIcon(showHome); 640 641 final int homeVis = (showHome || titleUp) && mExpandedActionView == null ? 642 VISIBLE : GONE; 643 mHomeLayout.setVisibility(homeVis); 644 645 if ((flagsChanged & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomNavView != null) { 646 if ((options & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { 647 addView(mCustomNavView); 648 } else { 649 removeView(mCustomNavView); 650 } 651 } 652 653 if (mTitleLayout != null && 654 (flagsChanged & ActionBar.DISPLAY_TITLE_MULTIPLE_LINES) != 0) { 655 if ((options & ActionBar.DISPLAY_TITLE_MULTIPLE_LINES) != 0) { 656 mTitleView.setSingleLine(false); 657 mTitleView.setMaxLines(2); 658 } else { 659 mTitleView.setMaxLines(1); 660 mTitleView.setSingleLine(true); 661 } 662 } 663 664 requestLayout(); 665 } else { 666 invalidate(); 667 } 668 669 // Make sure the home button has an accurate content description for accessibility. 670 updateHomeAccessibility(mUpGoerFive.isEnabled()); 671 } 672 673 public void setIcon(Drawable icon) { 674 mIcon = icon; 675 if (icon != null && 676 ((mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) == 0 || mLogo == null)) { 677 mHomeLayout.setIcon(icon); 678 } 679 if (mExpandedActionView != null) { 680 mExpandedHomeLayout.setIcon(mIcon.getConstantState().newDrawable(getResources())); 681 } 682 } 683 684 public void setIcon(int resId) { 685 setIcon(resId != 0 ? mContext.getDrawable(resId) : null); 686 } 687 688 public boolean hasIcon() { 689 return mIcon != null; 690 } 691 692 public void setLogo(Drawable logo) { 693 mLogo = logo; 694 if (logo != null && (mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) != 0) { 695 mHomeLayout.setIcon(logo); 696 } 697 } 698 699 public void setLogo(int resId) { 700 setLogo(resId != 0 ? mContext.getDrawable(resId) : null); 701 } 702 703 public boolean hasLogo() { 704 return mLogo != null; 705 } 706 707 public void setNavigationMode(int mode) { 708 final int oldMode = mNavigationMode; 709 if (mode != oldMode) { 710 ActionBarTransition.beginDelayedTransition(this); 711 switch (oldMode) { 712 case ActionBar.NAVIGATION_MODE_LIST: 713 if (mListNavLayout != null) { 714 removeView(mListNavLayout); 715 } 716 break; 717 case ActionBar.NAVIGATION_MODE_TABS: 718 if (mTabScrollView != null && mIncludeTabs) { 719 removeView(mTabScrollView); 720 } 721 } 722 723 switch (mode) { 724 case ActionBar.NAVIGATION_MODE_LIST: 725 if (mSpinner == null) { 726 mSpinner = new Spinner(mContext, null, 727 com.android.internal.R.attr.actionDropDownStyle); 728 mSpinner.setId(com.android.internal.R.id.action_bar_spinner); 729 mListNavLayout = new LinearLayout(mContext, null, 730 com.android.internal.R.attr.actionBarTabBarStyle); 731 LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( 732 LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); 733 params.gravity = Gravity.CENTER; 734 mListNavLayout.addView(mSpinner, params); 735 } 736 if (mSpinner.getAdapter() != mSpinnerAdapter) { 737 mSpinner.setAdapter(mSpinnerAdapter); 738 } 739 mSpinner.setOnItemSelectedListener(mNavItemSelectedListener); 740 addView(mListNavLayout); 741 break; 742 case ActionBar.NAVIGATION_MODE_TABS: 743 if (mTabScrollView != null && mIncludeTabs) { 744 addView(mTabScrollView); 745 } 746 break; 747 } 748 mNavigationMode = mode; 749 requestLayout(); 750 } 751 } 752 753 public void setDropdownParams(SpinnerAdapter adapter, AdapterView.OnItemSelectedListener l) { 754 mSpinnerAdapter = adapter; 755 mNavItemSelectedListener = l; 756 if (mSpinner != null) { 757 mSpinner.setAdapter(adapter); 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 public void setNavigationIcon(int resId) { 1316 mHomeLayout.setUpIndicator(resId); 1317 } 1318 1319 public void setNavigationContentDescription(CharSequence description) { 1320 mHomeDescription = description; 1321 updateHomeAccessibility(mUpGoerFive.isEnabled()); 1322 } 1323 1324 public void setNavigationContentDescription(int resId) { 1325 mHomeDescriptionRes = resId; 1326 mHomeDescription = resId != 0 ? getResources().getText(resId) : null; 1327 updateHomeAccessibility(mUpGoerFive.isEnabled()); 1328 } 1329 1330 static class SavedState extends BaseSavedState { 1331 int expandedMenuItemId; 1332 boolean isOverflowOpen; 1333 1334 SavedState(Parcelable superState) { 1335 super(superState); 1336 } 1337 1338 private SavedState(Parcel in) { 1339 super(in); 1340 expandedMenuItemId = in.readInt(); 1341 isOverflowOpen = in.readInt() != 0; 1342 } 1343 1344 @Override 1345 public void writeToParcel(Parcel out, int flags) { 1346 super.writeToParcel(out, flags); 1347 out.writeInt(expandedMenuItemId); 1348 out.writeInt(isOverflowOpen ? 1 : 0); 1349 } 1350 1351 public static final Parcelable.Creator<SavedState> CREATOR = 1352 new Parcelable.Creator<SavedState>() { 1353 public SavedState createFromParcel(Parcel in) { 1354 return new SavedState(in); 1355 } 1356 1357 public SavedState[] newArray(int size) { 1358 return new SavedState[size]; 1359 } 1360 }; 1361 } 1362 1363 private static class HomeView extends FrameLayout { 1364 private ImageView mUpView; 1365 private ImageView mIconView; 1366 private int mUpWidth; 1367 private int mStartOffset; 1368 private int mUpIndicatorRes; 1369 private Drawable mDefaultUpIndicator; 1370 1371 private static final long DEFAULT_TRANSITION_DURATION = 150; 1372 1373 public HomeView(Context context) { 1374 this(context, null); 1375 } 1376 1377 public HomeView(Context context, AttributeSet attrs) { 1378 super(context, attrs); 1379 LayoutTransition t = getLayoutTransition(); 1380 if (t != null) { 1381 // Set a lower duration than the default 1382 t.setDuration(DEFAULT_TRANSITION_DURATION); 1383 } 1384 } 1385 1386 public void setShowUp(boolean isUp) { 1387 mUpView.setVisibility(isUp ? VISIBLE : GONE); 1388 } 1389 1390 public void setShowIcon(boolean showIcon) { 1391 mIconView.setVisibility(showIcon ? VISIBLE : GONE); 1392 } 1393 1394 public void setIcon(Drawable icon) { 1395 mIconView.setImageDrawable(icon); 1396 } 1397 1398 public void setUpIndicator(Drawable d) { 1399 mUpView.setImageDrawable(d != null ? d : mDefaultUpIndicator); 1400 mUpIndicatorRes = 0; 1401 } 1402 1403 public void setUpIndicator(int resId) { 1404 mUpIndicatorRes = resId; 1405 mUpView.setImageDrawable(resId != 0 ? getContext().getDrawable(resId) : null); 1406 } 1407 1408 @Override 1409 protected void onConfigurationChanged(Configuration newConfig) { 1410 super.onConfigurationChanged(newConfig); 1411 if (mUpIndicatorRes != 0) { 1412 // Reload for config change 1413 setUpIndicator(mUpIndicatorRes); 1414 } 1415 } 1416 1417 @Override 1418 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 1419 onPopulateAccessibilityEvent(event); 1420 return true; 1421 } 1422 1423 @Override 1424 public void onPopulateAccessibilityEvent(AccessibilityEvent event) { 1425 super.onPopulateAccessibilityEvent(event); 1426 final CharSequence cdesc = getContentDescription(); 1427 if (!TextUtils.isEmpty(cdesc)) { 1428 event.getText().add(cdesc); 1429 } 1430 } 1431 1432 @Override 1433 public boolean dispatchHoverEvent(MotionEvent event) { 1434 // Don't allow children to hover; we want this to be treated as a single component. 1435 return onHoverEvent(event); 1436 } 1437 1438 @Override 1439 protected void onFinishInflate() { 1440 mUpView = (ImageView) findViewById(com.android.internal.R.id.up); 1441 mIconView = (ImageView) findViewById(com.android.internal.R.id.home); 1442 mDefaultUpIndicator = mUpView.getDrawable(); 1443 } 1444 1445 public int getStartOffset() { 1446 return mUpView.getVisibility() == GONE ? mStartOffset : 0; 1447 } 1448 1449 public int getUpWidth() { 1450 return mUpWidth; 1451 } 1452 1453 @Override 1454 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 1455 measureChildWithMargins(mUpView, widthMeasureSpec, 0, heightMeasureSpec, 0); 1456 final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams(); 1457 final int upMargins = upLp.leftMargin + upLp.rightMargin; 1458 mUpWidth = mUpView.getMeasuredWidth(); 1459 mStartOffset = mUpWidth + upMargins; 1460 int width = mUpView.getVisibility() == GONE ? 0 : mStartOffset; 1461 int height = upLp.topMargin + mUpView.getMeasuredHeight() + upLp.bottomMargin; 1462 1463 if (mIconView.getVisibility() != GONE) { 1464 measureChildWithMargins(mIconView, widthMeasureSpec, width, heightMeasureSpec, 0); 1465 final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams(); 1466 width += iconLp.leftMargin + mIconView.getMeasuredWidth() + iconLp.rightMargin; 1467 height = Math.max(height, 1468 iconLp.topMargin + mIconView.getMeasuredHeight() + iconLp.bottomMargin); 1469 } else if (upMargins < 0) { 1470 // Remove the measurement effects of negative margins used for offsets 1471 width -= upMargins; 1472 } 1473 1474 final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 1475 final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 1476 final int widthSize = MeasureSpec.getSize(widthMeasureSpec); 1477 final int heightSize = MeasureSpec.getSize(heightMeasureSpec); 1478 1479 switch (widthMode) { 1480 case MeasureSpec.AT_MOST: 1481 width = Math.min(width, widthSize); 1482 break; 1483 case MeasureSpec.EXACTLY: 1484 width = widthSize; 1485 break; 1486 case MeasureSpec.UNSPECIFIED: 1487 default: 1488 break; 1489 } 1490 switch (heightMode) { 1491 case MeasureSpec.AT_MOST: 1492 height = Math.min(height, heightSize); 1493 break; 1494 case MeasureSpec.EXACTLY: 1495 height = heightSize; 1496 break; 1497 case MeasureSpec.UNSPECIFIED: 1498 default: 1499 break; 1500 } 1501 setMeasuredDimension(width, height); 1502 } 1503 1504 @Override 1505 protected void onLayout(boolean changed, int l, int t, int r, int b) { 1506 final int vCenter = (b - t) / 2; 1507 final boolean isLayoutRtl = isLayoutRtl(); 1508 final int width = getWidth(); 1509 int upOffset = 0; 1510 if (mUpView.getVisibility() != GONE) { 1511 final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams(); 1512 final int upHeight = mUpView.getMeasuredHeight(); 1513 final int upWidth = mUpView.getMeasuredWidth(); 1514 upOffset = upLp.leftMargin + upWidth + upLp.rightMargin; 1515 final int upTop = vCenter - upHeight / 2; 1516 final int upBottom = upTop + upHeight; 1517 final int upRight; 1518 final int upLeft; 1519 if (isLayoutRtl) { 1520 upRight = width; 1521 upLeft = upRight - upWidth; 1522 r -= upOffset; 1523 } else { 1524 upRight = upWidth; 1525 upLeft = 0; 1526 l += upOffset; 1527 } 1528 mUpView.layout(upLeft, upTop, upRight, upBottom); 1529 } 1530 1531 final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams(); 1532 final int iconHeight = mIconView.getMeasuredHeight(); 1533 final int iconWidth = mIconView.getMeasuredWidth(); 1534 final int hCenter = (r - l) / 2; 1535 final int iconTop = Math.max(iconLp.topMargin, vCenter - iconHeight / 2); 1536 final int iconBottom = iconTop + iconHeight; 1537 final int iconLeft; 1538 final int iconRight; 1539 int marginStart = iconLp.getMarginStart(); 1540 final int delta = Math.max(marginStart, hCenter - iconWidth / 2); 1541 if (isLayoutRtl) { 1542 iconRight = width - upOffset - delta; 1543 iconLeft = iconRight - iconWidth; 1544 } else { 1545 iconLeft = upOffset + delta; 1546 iconRight = iconLeft + iconWidth; 1547 } 1548 mIconView.layout(iconLeft, iconTop, iconRight, iconBottom); 1549 } 1550 } 1551 1552 private class ExpandedActionViewMenuPresenter implements MenuPresenter { 1553 MenuBuilder mMenu; 1554 MenuItemImpl mCurrentExpandedItem; 1555 1556 @Override 1557 public void initForMenu(Context context, MenuBuilder menu) { 1558 // Clear the expanded action view when menus change. 1559 if (mMenu != null && mCurrentExpandedItem != null) { 1560 mMenu.collapseItemActionView(mCurrentExpandedItem); 1561 } 1562 mMenu = menu; 1563 } 1564 1565 @Override 1566 public MenuView getMenuView(ViewGroup root) { 1567 return null; 1568 } 1569 1570 @Override 1571 public void updateMenuView(boolean cleared) { 1572 // Make sure the expanded item we have is still there. 1573 if (mCurrentExpandedItem != null) { 1574 boolean found = false; 1575 1576 if (mMenu != null) { 1577 final int count = mMenu.size(); 1578 for (int i = 0; i < count; i++) { 1579 final MenuItem item = mMenu.getItem(i); 1580 if (item == mCurrentExpandedItem) { 1581 found = true; 1582 break; 1583 } 1584 } 1585 } 1586 1587 if (!found) { 1588 // The item we had expanded disappeared. Collapse. 1589 collapseItemActionView(mMenu, mCurrentExpandedItem); 1590 } 1591 } 1592 } 1593 1594 @Override 1595 public void setCallback(Callback cb) { 1596 } 1597 1598 @Override 1599 public boolean onSubMenuSelected(SubMenuBuilder subMenu) { 1600 return false; 1601 } 1602 1603 @Override 1604 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { 1605 } 1606 1607 @Override 1608 public boolean flagActionItems() { 1609 return false; 1610 } 1611 1612 @Override 1613 public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) { 1614 ActionBarTransition.beginDelayedTransition(ActionBarView.this); 1615 1616 mExpandedActionView = item.getActionView(); 1617 mExpandedHomeLayout.setIcon(mIcon.getConstantState().newDrawable(getResources())); 1618 mCurrentExpandedItem = item; 1619 if (mExpandedActionView.getParent() != ActionBarView.this) { 1620 addView(mExpandedActionView); 1621 } 1622 if (mExpandedHomeLayout.getParent() != mUpGoerFive) { 1623 mUpGoerFive.addView(mExpandedHomeLayout); 1624 } 1625 mHomeLayout.setVisibility(GONE); 1626 if (mTitleLayout != null) mTitleLayout.setVisibility(GONE); 1627 if (mTabScrollView != null) mTabScrollView.setVisibility(GONE); 1628 if (mSpinner != null) mSpinner.setVisibility(GONE); 1629 if (mCustomNavView != null) mCustomNavView.setVisibility(GONE); 1630 setHomeButtonEnabled(false, false); 1631 requestLayout(); 1632 item.setActionViewExpanded(true); 1633 1634 if (mExpandedActionView instanceof CollapsibleActionView) { 1635 ((CollapsibleActionView) mExpandedActionView).onActionViewExpanded(); 1636 } 1637 1638 return true; 1639 } 1640 1641 @Override 1642 public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) { 1643 ActionBarTransition.beginDelayedTransition(ActionBarView.this); 1644 1645 // Do this before detaching the actionview from the hierarchy, in case 1646 // it needs to dismiss the soft keyboard, etc. 1647 if (mExpandedActionView instanceof CollapsibleActionView) { 1648 ((CollapsibleActionView) mExpandedActionView).onActionViewCollapsed(); 1649 } 1650 1651 removeView(mExpandedActionView); 1652 mUpGoerFive.removeView(mExpandedHomeLayout); 1653 mExpandedActionView = null; 1654 if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_HOME) != 0) { 1655 mHomeLayout.setVisibility(VISIBLE); 1656 } 1657 if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 1658 if (mTitleLayout == null) { 1659 initTitle(); 1660 } else { 1661 mTitleLayout.setVisibility(VISIBLE); 1662 } 1663 } 1664 if (mTabScrollView != null) mTabScrollView.setVisibility(VISIBLE); 1665 if (mSpinner != null) mSpinner.setVisibility(VISIBLE); 1666 if (mCustomNavView != null) mCustomNavView.setVisibility(VISIBLE); 1667 1668 mExpandedHomeLayout.setIcon(null); 1669 mCurrentExpandedItem = null; 1670 setHomeButtonEnabled(mWasHomeEnabled); // Set by expandItemActionView above 1671 requestLayout(); 1672 item.setActionViewExpanded(false); 1673 1674 return true; 1675 } 1676 1677 @Override 1678 public int getId() { 1679 return 0; 1680 } 1681 1682 @Override 1683 public Parcelable onSaveInstanceState() { 1684 return null; 1685 } 1686 1687 @Override 1688 public void onRestoreInstanceState(Parcelable state) { 1689 } 1690 } 1691} 1692