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