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