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