ActionBarView.java revision c3076425bba5c129408338be4e138f8be16855a6
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.app.ActionBar; 30import android.app.ActionBar.OnNavigationListener; 31import android.app.Activity; 32import android.content.Context; 33import android.content.pm.ApplicationInfo; 34import android.content.pm.PackageManager; 35import android.content.pm.PackageManager.NameNotFoundException; 36import android.content.res.Resources; 37import android.content.res.TypedArray; 38import android.graphics.drawable.Drawable; 39import android.os.Parcel; 40import android.os.Parcelable; 41import android.text.TextUtils; 42import android.util.AttributeSet; 43import android.util.DisplayMetrics; 44import android.util.Log; 45import android.view.Gravity; 46import android.view.LayoutInflater; 47import android.view.Menu; 48import android.view.MenuItem; 49import android.view.View; 50import android.view.ViewGroup; 51import android.view.ViewParent; 52import android.view.Window; 53import android.widget.AdapterView; 54import android.widget.FrameLayout; 55import android.widget.ImageView; 56import android.widget.LinearLayout; 57import android.widget.ProgressBar; 58import android.widget.Spinner; 59import android.widget.SpinnerAdapter; 60import android.widget.TextView; 61 62/** 63 * @hide 64 */ 65public class ActionBarView extends AbsActionBarView { 66 private static final String TAG = "ActionBarView"; 67 68 /** 69 * Display options applied by default 70 */ 71 public static final int DISPLAY_DEFAULT = 0; 72 73 /** 74 * Display options that require re-layout as opposed to a simple invalidate 75 */ 76 private static final int DISPLAY_RELAYOUT_MASK = 77 ActionBar.DISPLAY_SHOW_HOME | 78 ActionBar.DISPLAY_USE_LOGO | 79 ActionBar.DISPLAY_HOME_AS_UP | 80 ActionBar.DISPLAY_SHOW_CUSTOM | 81 ActionBar.DISPLAY_SHOW_TITLE; 82 83 private static final int DEFAULT_CUSTOM_GRAVITY = Gravity.LEFT | Gravity.CENTER_VERTICAL; 84 85 private int mContentHeight; 86 87 private int mNavigationMode; 88 private int mDisplayOptions = ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_HOME_AS_UP; 89 private CharSequence mTitle; 90 private CharSequence mSubtitle; 91 private Drawable mIcon; 92 private Drawable mLogo; 93 94 private HomeView mHomeLayout; 95 private HomeView mExpandedHomeLayout; 96 private LinearLayout mTitleLayout; 97 private TextView mTitleView; 98 private TextView mSubtitleView; 99 private Spinner mSpinner; 100 private LinearLayout mListNavLayout; 101 private ScrollingTabContainerView mTabScrollView; 102 private View mCustomNavView; 103 private ProgressBar mProgressView; 104 private ProgressBar mIndeterminateProgressView; 105 106 private int mProgressBarPadding; 107 private int mItemPadding; 108 109 private int mTitleStyleRes; 110 private int mSubtitleStyleRes; 111 private int mProgressStyle; 112 private int mIndeterminateProgressStyle; 113 114 private boolean mSplitActionBar; 115 private boolean mUserTitle; 116 private boolean mIncludeTabs; 117 private boolean mIsCollapsable; 118 119 private MenuBuilder mOptionsMenu; 120 121 private ActionBarContextView mContextView; 122 123 private ActionMenuItem mLogoNavItem; 124 125 private SpinnerAdapter mSpinnerAdapter; 126 private OnNavigationListener mCallback; 127 128 private Runnable mTabSelector; 129 130 private ExpandedActionViewMenuPresenter mExpandedMenuPresenter; 131 View mExpandedActionView; 132 133 private final AdapterView.OnItemSelectedListener mNavItemSelectedListener = 134 new AdapterView.OnItemSelectedListener() { 135 public void onItemSelected(AdapterView parent, View view, int position, long id) { 136 if (mCallback != null) { 137 mCallback.onNavigationItemSelected(position, id); 138 } 139 } 140 public void onNothingSelected(AdapterView parent) { 141 // Do nothing 142 } 143 }; 144 145 private final OnClickListener mExpandedActionViewUpListener = new OnClickListener() { 146 @Override 147 public void onClick(View v) { 148 final MenuItemImpl item = mExpandedMenuPresenter.mCurrentExpandedItem; 149 if (item != null) { 150 item.collapseActionView(); 151 } 152 } 153 }; 154 155 public ActionBarView(Context context, AttributeSet attrs) { 156 super(context, attrs); 157 158 // Background is always provided by the container. 159 setBackgroundResource(0); 160 161 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionBar); 162 163 ApplicationInfo appInfo = context.getApplicationInfo(); 164 PackageManager pm = context.getPackageManager(); 165 mNavigationMode = a.getInt(R.styleable.ActionBar_navigationMode, 166 ActionBar.NAVIGATION_MODE_STANDARD); 167 mTitle = a.getText(R.styleable.ActionBar_title); 168 mSubtitle = a.getText(R.styleable.ActionBar_subtitle); 169 170 mLogo = a.getDrawable(R.styleable.ActionBar_logo); 171 if (mLogo == null) { 172 if (context instanceof Activity) { 173 try { 174 mLogo = pm.getActivityLogo(((Activity) context).getComponentName()); 175 } catch (NameNotFoundException e) { 176 Log.e(TAG, "Activity component name not found!", e); 177 } 178 } 179 if (mLogo == null) { 180 mLogo = appInfo.loadLogo(pm); 181 } 182 } 183 184 mIcon = a.getDrawable(R.styleable.ActionBar_icon); 185 if (mIcon == null) { 186 if (context instanceof Activity) { 187 try { 188 mIcon = pm.getActivityIcon(((Activity) context).getComponentName()); 189 } catch (NameNotFoundException e) { 190 Log.e(TAG, "Activity component name not found!", e); 191 } 192 } 193 if (mIcon == null) { 194 mIcon = appInfo.loadIcon(pm); 195 } 196 } 197 198 final LayoutInflater inflater = LayoutInflater.from(context); 199 200 final int homeResId = a.getResourceId( 201 com.android.internal.R.styleable.ActionBar_homeLayout, 202 com.android.internal.R.layout.action_bar_home); 203 204 mHomeLayout = (HomeView) inflater.inflate(homeResId, this, false); 205 206 mExpandedHomeLayout = (HomeView) inflater.inflate(homeResId, this, false); 207 mExpandedHomeLayout.setUp(true); 208 mExpandedHomeLayout.setOnClickListener(mExpandedActionViewUpListener); 209 210 mTitleStyleRes = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0); 211 mSubtitleStyleRes = a.getResourceId(R.styleable.ActionBar_subtitleTextStyle, 0); 212 mProgressStyle = a.getResourceId(R.styleable.ActionBar_progressBarStyle, 0); 213 mIndeterminateProgressStyle = a.getResourceId( 214 R.styleable.ActionBar_indeterminateProgressStyle, 0); 215 216 mProgressBarPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_progressBarPadding, 0); 217 mItemPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_itemPadding, 0); 218 219 setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, DISPLAY_DEFAULT)); 220 221 final int customNavId = a.getResourceId(R.styleable.ActionBar_customNavigationLayout, 0); 222 if (customNavId != 0) { 223 mCustomNavView = (View) inflater.inflate(customNavId, this, false); 224 mNavigationMode = ActionBar.NAVIGATION_MODE_STANDARD; 225 setDisplayOptions(mDisplayOptions | ActionBar.DISPLAY_SHOW_CUSTOM); 226 } 227 228 mContentHeight = a.getLayoutDimension(R.styleable.ActionBar_height, 0); 229 230 a.recycle(); 231 232 mLogoNavItem = new ActionMenuItem(context, 0, android.R.id.home, 0, 0, mTitle); 233 mHomeLayout.setOnClickListener(new OnClickListener() { 234 public void onClick(View v) { 235 Context context = getContext(); 236 if (context instanceof Activity) { 237 Activity activity = (Activity) context; 238 activity.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mLogoNavItem); 239 } 240 } 241 }); 242 mHomeLayout.setClickable(true); 243 mHomeLayout.setFocusable(true); 244 } 245 246 @Override 247 public void onDetachedFromWindow() { 248 super.onDetachedFromWindow(); 249 removeCallbacks(mTabSelector); 250 } 251 252 @Override 253 public boolean shouldDelayChildPressedState() { 254 return false; 255 } 256 257 public void initProgress() { 258 mProgressView = new ProgressBar(mContext, null, 0, mProgressStyle); 259 mProgressView.setId(R.id.progress_horizontal); 260 mProgressView.setMax(10000); 261 addView(mProgressView); 262 } 263 264 public void initIndeterminateProgress() { 265 mIndeterminateProgressView = new ProgressBar(mContext, null, 0, 266 mIndeterminateProgressStyle); 267 mIndeterminateProgressView.setId(R.id.progress_circular); 268 addView(mIndeterminateProgressView); 269 } 270 271 public void setContentHeight(int height) { 272 mContentHeight = height; 273 requestLayout(); 274 } 275 276 public void setSplitActionBar(boolean splitActionBar) { 277 if (mSplitActionBar != splitActionBar) { 278 if (mMenuView != null) { 279 if (splitActionBar) { 280 removeView(mMenuView); 281 if (mSplitView != null) { 282 mSplitView.addView(mMenuView); 283 } 284 } else { 285 addView(mMenuView); 286 } 287 } 288 mSplitActionBar = splitActionBar; 289 } 290 } 291 292 public boolean isSplitActionBar() { 293 return mSplitActionBar; 294 } 295 296 public boolean hasEmbeddedTabs() { 297 return mIncludeTabs; 298 } 299 300 public void setEmbeddedTabView(ScrollingTabContainerView tabs) { 301 mTabScrollView = tabs; 302 mIncludeTabs = tabs != null; 303 if (mIncludeTabs && mNavigationMode == ActionBar.NAVIGATION_MODE_TABS) { 304 addView(mTabScrollView); 305 } 306 } 307 308 public void setCallback(OnNavigationListener callback) { 309 mCallback = callback; 310 } 311 312 public void setMenu(Menu menu, MenuPresenter.Callback cb) { 313 if (menu == mOptionsMenu) return; 314 315 if (mOptionsMenu != null) { 316 mOptionsMenu.removeMenuPresenter(mActionMenuPresenter); 317 mOptionsMenu.removeMenuPresenter(mExpandedMenuPresenter); 318 } 319 320 MenuBuilder builder = (MenuBuilder) menu; 321 mOptionsMenu = builder; 322 if (mMenuView != null) { 323 removeView(mMenuView); 324 } 325 if (mActionMenuPresenter == null) { 326 mActionMenuPresenter = new ActionMenuPresenter(); 327 mActionMenuPresenter.setCallback(cb); 328 mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter(); 329 } 330 builder.addMenuPresenter(mActionMenuPresenter); 331 builder.addMenuPresenter(mExpandedMenuPresenter); 332 333 final ActionMenuView menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); 334 final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, 335 LayoutParams.MATCH_PARENT); 336 menuView.setLayoutParams(layoutParams); 337 if (!mSplitActionBar) { 338 addView(menuView); 339 } else { 340 // Allow full screen width in split mode. 341 mActionMenuPresenter.setWidthLimit( 342 getContext().getResources().getDisplayMetrics().widthPixels, true); 343 // No limit to the item count; use whatever will fit. 344 mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE); 345 // Span the whole width 346 layoutParams.width = LayoutParams.MATCH_PARENT; 347 if (mSplitView != null) { 348 mSplitView.addView(menuView); 349 } // We'll add this later if we missed it this time. 350 } 351 mMenuView = menuView; 352 } 353 354 public void setCustomNavigationView(View view) { 355 final boolean showCustom = (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0; 356 if (mCustomNavView != null && showCustom) { 357 removeView(mCustomNavView); 358 } 359 mCustomNavView = view; 360 if (mCustomNavView != null && showCustom) { 361 addView(mCustomNavView); 362 } 363 } 364 365 public CharSequence getTitle() { 366 return mTitle; 367 } 368 369 /** 370 * Set the action bar title. This will always replace or override window titles. 371 * @param title Title to set 372 * 373 * @see #setWindowTitle(CharSequence) 374 */ 375 public void setTitle(CharSequence title) { 376 mUserTitle = true; 377 setTitleImpl(title); 378 } 379 380 /** 381 * Set the window title. A window title will always be replaced or overridden by a user title. 382 * @param title Title to set 383 * 384 * @see #setTitle(CharSequence) 385 */ 386 public void setWindowTitle(CharSequence title) { 387 if (!mUserTitle) { 388 setTitleImpl(title); 389 } 390 } 391 392 private void setTitleImpl(CharSequence title) { 393 mTitle = title; 394 if (mTitleView != null) { 395 mTitleView.setText(title); 396 mTitleLayout.setVisibility(TextUtils.isEmpty(mTitle) && TextUtils.isEmpty(mSubtitle) ? 397 GONE : VISIBLE); 398 } 399 if (mLogoNavItem != null) { 400 mLogoNavItem.setTitle(title); 401 } 402 } 403 404 public CharSequence getSubtitle() { 405 return mSubtitle; 406 } 407 408 public void setSubtitle(CharSequence subtitle) { 409 mSubtitle = subtitle; 410 if (mSubtitleView != null) { 411 mSubtitleView.setText(subtitle); 412 mSubtitleView.setVisibility(subtitle != null ? VISIBLE : GONE); 413 mTitleLayout.setVisibility(TextUtils.isEmpty(mTitle) && TextUtils.isEmpty(mSubtitle) ? 414 GONE : VISIBLE); 415 } 416 } 417 418 public void setDisplayOptions(int options) { 419 final int flagsChanged = options ^ mDisplayOptions; 420 mDisplayOptions = options; 421 422 if ((flagsChanged & ActionBar.DISPLAY_DISABLE_HOME) != 0) { 423 final boolean disableHome = (options & ActionBar.DISPLAY_DISABLE_HOME) != 0; 424 mHomeLayout.setEnabled(!disableHome); 425 } 426 427 if ((flagsChanged & DISPLAY_RELAYOUT_MASK) != 0) { 428 final int vis = (options & ActionBar.DISPLAY_SHOW_HOME) != 0 ? VISIBLE : GONE; 429 mHomeLayout.setVisibility(vis); 430 431 if ((flagsChanged & ActionBar.DISPLAY_HOME_AS_UP) != 0) { 432 mHomeLayout.setUp((options & ActionBar.DISPLAY_HOME_AS_UP) != 0); 433 } 434 435 if ((flagsChanged & ActionBar.DISPLAY_USE_LOGO) != 0) { 436 final boolean logoVis = mLogo != null && (options & ActionBar.DISPLAY_USE_LOGO) != 0; 437 mHomeLayout.setIcon(logoVis ? mLogo : mIcon); 438 } 439 440 if ((flagsChanged & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 441 if ((options & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 442 initTitle(); 443 } else { 444 removeView(mTitleLayout); 445 } 446 } 447 448 if ((flagsChanged & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomNavView != null) { 449 if ((options & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { 450 addView(mCustomNavView); 451 } else { 452 removeView(mCustomNavView); 453 } 454 } 455 456 requestLayout(); 457 } else { 458 invalidate(); 459 } 460 461 // Make sure the home button has an accurate content description for accessibility. 462 if ((options & ActionBar.DISPLAY_DISABLE_HOME) != 0) { 463 mHomeLayout.setContentDescription(null); 464 } else if ((options & ActionBar.DISPLAY_HOME_AS_UP) != 0) { 465 mHomeLayout.setContentDescription(mContext.getResources().getText( 466 R.string.action_bar_up_description)); 467 } else { 468 mHomeLayout.setContentDescription(mContext.getResources().getText( 469 R.string.action_bar_home_description)); 470 } 471 } 472 473 public void setIcon(Drawable icon) { 474 mIcon = icon; 475 if (icon != null && 476 ((mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) == 0 || mLogo == null)) { 477 mHomeLayout.setIcon(icon); 478 } 479 } 480 481 public void setIcon(int resId) { 482 setIcon(mContext.getResources().getDrawableForDensity(resId, getPreferredIconDensity())); 483 } 484 485 public void setLogo(Drawable logo) { 486 mLogo = logo; 487 if (logo != null && (mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) != 0) { 488 mHomeLayout.setIcon(logo); 489 } 490 } 491 492 public void setLogo(int resId) { 493 mContext.getResources().getDrawable(resId); 494 } 495 496 /** 497 * @return Drawable density to load that will best fit the available height. 498 */ 499 private int getPreferredIconDensity() { 500 final Resources res = mContext.getResources(); 501 final int availableHeight = getLayoutParams().height - 502 mHomeLayout.getVerticalIconPadding(); 503 int iconSize = res.getDimensionPixelSize(android.R.dimen.app_icon_size); 504 505 if (iconSize * DisplayMetrics.DENSITY_LOW >= availableHeight) { 506 return DisplayMetrics.DENSITY_LOW; 507 } else if (iconSize * DisplayMetrics.DENSITY_MEDIUM >= availableHeight) { 508 return DisplayMetrics.DENSITY_MEDIUM; 509 } else if (iconSize * DisplayMetrics.DENSITY_HIGH >= availableHeight) { 510 return DisplayMetrics.DENSITY_HIGH; 511 } 512 return DisplayMetrics.DENSITY_XHIGH; 513 } 514 515 public void setNavigationMode(int mode) { 516 final int oldMode = mNavigationMode; 517 if (mode != oldMode) { 518 switch (oldMode) { 519 case ActionBar.NAVIGATION_MODE_LIST: 520 if (mListNavLayout != null) { 521 removeView(mListNavLayout); 522 } 523 break; 524 case ActionBar.NAVIGATION_MODE_TABS: 525 if (mTabScrollView != null && mIncludeTabs) { 526 removeView(mTabScrollView); 527 } 528 } 529 530 switch (mode) { 531 case ActionBar.NAVIGATION_MODE_LIST: 532 if (mSpinner == null) { 533 mSpinner = new Spinner(mContext, null, 534 com.android.internal.R.attr.actionDropDownStyle); 535 mListNavLayout = new LinearLayout(mContext, null, 536 com.android.internal.R.attr.actionBarTabBarStyle); 537 LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( 538 LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); 539 params.gravity = Gravity.CENTER; 540 mListNavLayout.addView(mSpinner, params); 541 } 542 if (mSpinner.getAdapter() != mSpinnerAdapter) { 543 mSpinner.setAdapter(mSpinnerAdapter); 544 } 545 mSpinner.setOnItemSelectedListener(mNavItemSelectedListener); 546 addView(mListNavLayout); 547 break; 548 case ActionBar.NAVIGATION_MODE_TABS: 549 if (mTabScrollView != null && mIncludeTabs) { 550 addView(mTabScrollView); 551 } 552 break; 553 } 554 mNavigationMode = mode; 555 requestLayout(); 556 } 557 } 558 559 public ScrollingTabContainerView createTabContainer() { 560 final LinearLayout tabLayout = new LinearLayout(getContext(), null, 561 com.android.internal.R.attr.actionBarTabBarStyle); 562 tabLayout.setMeasureWithLargestChildEnabled(true); 563 tabLayout.setLayoutParams(new LinearLayout.LayoutParams( 564 LinearLayout.LayoutParams.WRAP_CONTENT, mContentHeight)); 565 566 final ScrollingTabContainerView scroller = new ScrollingTabContainerView(mContext); 567 scroller.setTabLayout(tabLayout); 568 return scroller; 569 } 570 571 public void setDropdownAdapter(SpinnerAdapter adapter) { 572 mSpinnerAdapter = adapter; 573 if (mSpinner != null) { 574 mSpinner.setAdapter(adapter); 575 } 576 } 577 578 public SpinnerAdapter getDropdownAdapter() { 579 return mSpinnerAdapter; 580 } 581 582 public void setDropdownSelectedPosition(int position) { 583 mSpinner.setSelection(position); 584 } 585 586 public int getDropdownSelectedPosition() { 587 return mSpinner.getSelectedItemPosition(); 588 } 589 590 public View getCustomNavigationView() { 591 return mCustomNavView; 592 } 593 594 public int getNavigationMode() { 595 return mNavigationMode; 596 } 597 598 public int getDisplayOptions() { 599 return mDisplayOptions; 600 } 601 602 @Override 603 protected LayoutParams generateDefaultLayoutParams() { 604 // Used by custom nav views if they don't supply layout params. Everything else 605 // added to an ActionBarView should have them already. 606 return new ActionBar.LayoutParams(DEFAULT_CUSTOM_GRAVITY); 607 } 608 609 @Override 610 protected void onFinishInflate() { 611 super.onFinishInflate(); 612 613 addView(mHomeLayout); 614 615 if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { 616 final ViewParent parent = mCustomNavView.getParent(); 617 if (parent != this) { 618 if (parent instanceof ViewGroup) { 619 ((ViewGroup) parent).removeView(mCustomNavView); 620 } 621 addView(mCustomNavView); 622 } 623 } 624 } 625 626 private void initTitle() { 627 LayoutInflater inflater = LayoutInflater.from(getContext()); 628 mTitleLayout = (LinearLayout) inflater.inflate(R.layout.action_bar_title_item, null); 629 mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title); 630 mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle); 631 632 if (mTitleStyleRes != 0) { 633 mTitleView.setTextAppearance(mContext, mTitleStyleRes); 634 } 635 if (mTitle != null) { 636 mTitleView.setText(mTitle); 637 } 638 639 if (mSubtitleStyleRes != 0) { 640 mSubtitleView.setTextAppearance(mContext, mSubtitleStyleRes); 641 } 642 if (mSubtitle != null) { 643 mSubtitleView.setText(mSubtitle); 644 mSubtitleView.setVisibility(VISIBLE); 645 } 646 647 addView(mTitleLayout); 648 } 649 650 public void setContextView(ActionBarContextView view) { 651 mContextView = view; 652 } 653 654 public void setCollapsable(boolean collapsable) { 655 mIsCollapsable = collapsable; 656 } 657 658 @Override 659 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 660 final int childCount = getChildCount(); 661 if (mIsCollapsable) { 662 int visibleChildren = 0; 663 for (int i = 0; i < childCount; i++) { 664 final View child = getChildAt(i); 665 if (child.getVisibility() != GONE && 666 !(child == mMenuView && mMenuView.getChildCount() == 0)) { 667 visibleChildren++; 668 } 669 } 670 671 if (visibleChildren == 0) { 672 // No size for an empty action bar when collapsable. 673 setMeasuredDimension(0, 0); 674 return; 675 } 676 } 677 678 int widthMode = MeasureSpec.getMode(widthMeasureSpec); 679 if (widthMode != MeasureSpec.EXACTLY) { 680 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + 681 "with android:layout_width=\"match_parent\" (or fill_parent)"); 682 } 683 684 int heightMode = MeasureSpec.getMode(heightMeasureSpec); 685 if (heightMode != MeasureSpec.AT_MOST) { 686 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + 687 "with android:layout_height=\"wrap_content\""); 688 } 689 690 int contentWidth = MeasureSpec.getSize(widthMeasureSpec); 691 692 int maxHeight = mContentHeight > 0 ? 693 mContentHeight : MeasureSpec.getSize(heightMeasureSpec); 694 695 final int verticalPadding = getPaddingTop() + getPaddingBottom(); 696 final int paddingLeft = getPaddingLeft(); 697 final int paddingRight = getPaddingRight(); 698 final int height = maxHeight - verticalPadding; 699 final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST); 700 701 int availableWidth = contentWidth - paddingLeft - paddingRight; 702 int leftOfCenter = availableWidth / 2; 703 int rightOfCenter = leftOfCenter; 704 705 View homeLayout = mExpandedActionView != null ? mExpandedHomeLayout : mHomeLayout; 706 707 if (homeLayout.getVisibility() != GONE) { 708 homeLayout.measure( 709 MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), 710 MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); 711 final int homeWidth = homeLayout.getMeasuredWidth(); 712 availableWidth = Math.max(0, availableWidth - homeWidth); 713 leftOfCenter = Math.max(0, availableWidth - homeWidth); 714 } 715 716 if (mMenuView != null && mMenuView.getParent() == this) { 717 availableWidth = measureChildView(mMenuView, availableWidth, 718 childSpecHeight, 0); 719 rightOfCenter = Math.max(0, rightOfCenter - mMenuView.getMeasuredWidth()); 720 } 721 722 if (mExpandedActionView == null) { 723 boolean showTitle = mTitleLayout != null && mTitleLayout.getVisibility() != GONE && 724 (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0; 725 if (showTitle) { 726 availableWidth = measureChildView(mTitleLayout, availableWidth, childSpecHeight, 0); 727 leftOfCenter = Math.max(0, leftOfCenter - mTitleLayout.getMeasuredWidth()); 728 } 729 730 switch (mNavigationMode) { 731 case ActionBar.NAVIGATION_MODE_LIST: 732 if (mListNavLayout != null) { 733 final int itemPaddingSize = showTitle ? mItemPadding * 2 : mItemPadding; 734 availableWidth = Math.max(0, availableWidth - itemPaddingSize); 735 leftOfCenter = Math.max(0, leftOfCenter - itemPaddingSize); 736 mListNavLayout.measure( 737 MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), 738 MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); 739 final int listNavWidth = mListNavLayout.getMeasuredWidth(); 740 availableWidth = Math.max(0, availableWidth - listNavWidth); 741 leftOfCenter = Math.max(0, leftOfCenter - listNavWidth); 742 } 743 break; 744 case ActionBar.NAVIGATION_MODE_TABS: 745 if (mTabScrollView != null) { 746 final int itemPaddingSize = showTitle ? mItemPadding * 2 : mItemPadding; 747 availableWidth = Math.max(0, availableWidth - itemPaddingSize); 748 leftOfCenter = Math.max(0, leftOfCenter - itemPaddingSize); 749 mTabScrollView.measure( 750 MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), 751 MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); 752 final int tabWidth = mTabScrollView.getMeasuredWidth(); 753 availableWidth = Math.max(0, availableWidth - tabWidth); 754 leftOfCenter = Math.max(0, leftOfCenter - tabWidth); 755 } 756 break; 757 } 758 } 759 760 if (mIndeterminateProgressView != null && 761 mIndeterminateProgressView.getVisibility() != GONE) { 762 availableWidth = measureChildView(mIndeterminateProgressView, availableWidth, 763 childSpecHeight, 0); 764 rightOfCenter = Math.max(0, 765 rightOfCenter - mIndeterminateProgressView.getMeasuredWidth()); 766 } 767 768 View customView = null; 769 if (mExpandedActionView != null) { 770 customView = mExpandedActionView; 771 } else if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && 772 mCustomNavView != null) { 773 customView = mCustomNavView; 774 } 775 776 if (customView != null) { 777 final LayoutParams lp = generateLayoutParams(customView.getLayoutParams()); 778 final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ? 779 (ActionBar.LayoutParams) lp : null; 780 781 int horizontalMargin = 0; 782 int verticalMargin = 0; 783 if (ablp != null) { 784 horizontalMargin = ablp.leftMargin + ablp.rightMargin; 785 verticalMargin = ablp.topMargin + ablp.bottomMargin; 786 } 787 788 // If the action bar is wrapping to its content height, don't allow a custom 789 // view to MATCH_PARENT. 790 int customNavHeightMode; 791 if (mContentHeight <= 0) { 792 customNavHeightMode = MeasureSpec.AT_MOST; 793 } else { 794 customNavHeightMode = lp.height != LayoutParams.WRAP_CONTENT ? 795 MeasureSpec.EXACTLY : MeasureSpec.AT_MOST; 796 } 797 final int customNavHeight = Math.max(0, 798 (lp.height >= 0 ? Math.min(lp.height, height) : height) - verticalMargin); 799 800 final int customNavWidthMode = lp.width != LayoutParams.WRAP_CONTENT ? 801 MeasureSpec.EXACTLY : MeasureSpec.AT_MOST; 802 int customNavWidth = Math.max(0, 803 (lp.width >= 0 ? Math.min(lp.width, availableWidth) : availableWidth) 804 - horizontalMargin); 805 final int hgrav = (ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY) & 806 Gravity.HORIZONTAL_GRAVITY_MASK; 807 808 // Centering a custom view is treated specially; we try to center within the whole 809 // action bar rather than in the available space. 810 if (hgrav == Gravity.CENTER_HORIZONTAL && lp.width == LayoutParams.MATCH_PARENT) { 811 customNavWidth = Math.min(leftOfCenter, rightOfCenter) * 2; 812 } 813 814 customView.measure( 815 MeasureSpec.makeMeasureSpec(customNavWidth, customNavWidthMode), 816 MeasureSpec.makeMeasureSpec(customNavHeight, customNavHeightMode)); 817 } 818 819 if (mContentHeight <= 0) { 820 int measuredHeight = 0; 821 for (int i = 0; i < childCount; i++) { 822 View v = getChildAt(i); 823 int paddedViewHeight = v.getMeasuredHeight() + verticalPadding; 824 if (paddedViewHeight > measuredHeight) { 825 measuredHeight = paddedViewHeight; 826 } 827 } 828 setMeasuredDimension(contentWidth, measuredHeight); 829 } else { 830 setMeasuredDimension(contentWidth, maxHeight); 831 } 832 833 if (mContextView != null) { 834 mContextView.setHeight(getMeasuredHeight()); 835 } 836 837 if (mProgressView != null && mProgressView.getVisibility() != GONE) { 838 mProgressView.measure(MeasureSpec.makeMeasureSpec( 839 contentWidth - mProgressBarPadding * 2, MeasureSpec.EXACTLY), 840 MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST)); 841 } 842 } 843 844 @Override 845 protected void onLayout(boolean changed, int l, int t, int r, int b) { 846 int x = getPaddingLeft(); 847 final int y = getPaddingTop(); 848 final int contentHeight = b - t - getPaddingTop() - getPaddingBottom(); 849 850 if (contentHeight <= 0) { 851 // Nothing to do if we can't see anything. 852 return; 853 } 854 855 View homeLayout = mExpandedActionView != null ? mExpandedHomeLayout : mHomeLayout; 856 if (homeLayout.getVisibility() != GONE) { 857 x += positionChild(homeLayout, x, y, contentHeight); 858 } 859 860 if (mExpandedActionView == null) { 861 final boolean showTitle = mTitleLayout != null && mTitleLayout.getVisibility() != GONE && 862 (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0; 863 if (showTitle) { 864 x += positionChild(mTitleLayout, x, y, contentHeight); 865 } 866 867 switch (mNavigationMode) { 868 case ActionBar.NAVIGATION_MODE_STANDARD: 869 break; 870 case ActionBar.NAVIGATION_MODE_LIST: 871 if (mListNavLayout != null) { 872 if (showTitle) x += mItemPadding; 873 x += positionChild(mListNavLayout, x, y, contentHeight) + mItemPadding; 874 } 875 break; 876 case ActionBar.NAVIGATION_MODE_TABS: 877 if (mTabScrollView != null) { 878 if (showTitle) x += mItemPadding; 879 x += positionChild(mTabScrollView, x, y, contentHeight) + mItemPadding; 880 } 881 break; 882 } 883 } 884 885 int menuLeft = r - l - getPaddingRight(); 886 if (mMenuView != null && mMenuView.getParent() == this) { 887 positionChildInverse(mMenuView, menuLeft, y, contentHeight); 888 menuLeft -= mMenuView.getMeasuredWidth(); 889 } 890 891 if (mIndeterminateProgressView != null && 892 mIndeterminateProgressView.getVisibility() != GONE) { 893 positionChildInverse(mIndeterminateProgressView, menuLeft, y, contentHeight); 894 menuLeft -= mIndeterminateProgressView.getMeasuredWidth(); 895 } 896 897 View customView = null; 898 if (mExpandedActionView != null) { 899 customView = mExpandedActionView; 900 } else if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && 901 mCustomNavView != null) { 902 customView = mCustomNavView; 903 } 904 if (customView != null) { 905 LayoutParams lp = customView.getLayoutParams(); 906 final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ? 907 (ActionBar.LayoutParams) lp : null; 908 909 final int gravity = ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY; 910 final int navWidth = customView.getMeasuredWidth(); 911 912 int topMargin = 0; 913 int bottomMargin = 0; 914 if (ablp != null) { 915 x += ablp.leftMargin; 916 menuLeft -= ablp.rightMargin; 917 topMargin = ablp.topMargin; 918 bottomMargin = ablp.bottomMargin; 919 } 920 921 int hgravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK; 922 // See if we actually have room to truly center; if not push against left or right. 923 if (hgravity == Gravity.CENTER_HORIZONTAL) { 924 final int centeredLeft = ((mRight - mLeft) - navWidth) / 2; 925 if (centeredLeft < x) { 926 hgravity = Gravity.LEFT; 927 } else if (centeredLeft + navWidth > menuLeft) { 928 hgravity = Gravity.RIGHT; 929 } 930 } 931 932 int xpos = 0; 933 switch (hgravity) { 934 case Gravity.CENTER_HORIZONTAL: 935 xpos = ((mRight - mLeft) - navWidth) / 2; 936 break; 937 case Gravity.LEFT: 938 xpos = x; 939 break; 940 case Gravity.RIGHT: 941 xpos = menuLeft - navWidth; 942 break; 943 } 944 945 int ypos = 0; 946 switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) { 947 case Gravity.CENTER_VERTICAL: 948 final int paddedTop = mTop + getPaddingTop(); 949 final int paddedBottom = mBottom - getPaddingBottom(); 950 ypos = ((paddedBottom - paddedTop) - customView.getMeasuredHeight()) / 2; 951 break; 952 case Gravity.TOP: 953 ypos = getPaddingTop() + topMargin; 954 break; 955 case Gravity.BOTTOM: 956 ypos = getHeight() - getPaddingBottom() - customView.getMeasuredHeight() 957 - bottomMargin; 958 break; 959 } 960 x += positionChild(customView, xpos, ypos, contentHeight); 961 } 962 963 if (mProgressView != null) { 964 mProgressView.bringToFront(); 965 final int halfProgressHeight = mProgressView.getMeasuredHeight() / 2; 966 mProgressView.layout(mProgressBarPadding, -halfProgressHeight, 967 mProgressBarPadding + mProgressView.getMeasuredWidth(), halfProgressHeight); 968 } 969 } 970 971 @Override 972 public LayoutParams generateLayoutParams(LayoutParams lp) { 973 if (lp == null) { 974 lp = generateDefaultLayoutParams(); 975 } 976 return lp; 977 } 978 979 @Override 980 public Parcelable onSaveInstanceState() { 981 Parcelable superState = super.onSaveInstanceState(); 982 SavedState state = new SavedState(superState); 983 984 if (mExpandedMenuPresenter != null && mExpandedMenuPresenter.mCurrentExpandedItem != null) { 985 state.expandedMenuItemId = mExpandedMenuPresenter.mCurrentExpandedItem.getItemId(); 986 } 987 988 state.isOverflowOpen = isOverflowMenuShowing(); 989 990 return state; 991 } 992 993 @Override 994 public void onRestoreInstanceState(Parcelable p) { 995 SavedState state = (SavedState) p; 996 997 super.onRestoreInstanceState(state.getSuperState()); 998 999 if (state.expandedMenuItemId != 0 && 1000 mExpandedMenuPresenter != null && mOptionsMenu != null) { 1001 final MenuItem item = mOptionsMenu.findItem(state.expandedMenuItemId); 1002 if (item != null) { 1003 item.expandActionView(); 1004 } 1005 } 1006 1007 if (state.isOverflowOpen) { 1008 postShowOverflowMenu(); 1009 } 1010 } 1011 1012 static class SavedState extends BaseSavedState { 1013 int expandedMenuItemId; 1014 boolean isOverflowOpen; 1015 1016 SavedState(Parcelable superState) { 1017 super(superState); 1018 } 1019 1020 private SavedState(Parcel in) { 1021 super(in); 1022 expandedMenuItemId = in.readInt(); 1023 isOverflowOpen = in.readInt() != 0; 1024 } 1025 1026 @Override 1027 public void writeToParcel(Parcel out, int flags) { 1028 super.writeToParcel(out, flags); 1029 out.writeInt(expandedMenuItemId); 1030 out.writeInt(isOverflowOpen ? 1 : 0); 1031 } 1032 1033 public static final Parcelable.Creator<SavedState> CREATOR = 1034 new Parcelable.Creator<SavedState>() { 1035 public SavedState createFromParcel(Parcel in) { 1036 return new SavedState(in); 1037 } 1038 1039 public SavedState[] newArray(int size) { 1040 return new SavedState[size]; 1041 } 1042 }; 1043 } 1044 1045 private static class HomeView extends FrameLayout { 1046 private View mUpView; 1047 private ImageView mIconView; 1048 1049 public HomeView(Context context) { 1050 this(context, null); 1051 } 1052 1053 public HomeView(Context context, AttributeSet attrs) { 1054 super(context, attrs); 1055 } 1056 1057 public void setUp(boolean isUp) { 1058 mUpView.setVisibility(isUp ? VISIBLE : GONE); 1059 } 1060 1061 public void setIcon(Drawable icon) { 1062 mIconView.setImageDrawable(icon); 1063 } 1064 1065 @Override 1066 protected void onFinishInflate() { 1067 mUpView = findViewById(com.android.internal.R.id.up); 1068 mIconView = (ImageView) findViewById(com.android.internal.R.id.home); 1069 } 1070 1071 public int getVerticalIconPadding() { 1072 return mIconView.getPaddingTop() + mIconView.getPaddingBottom(); 1073 } 1074 1075 @Override 1076 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 1077 measureChildWithMargins(mUpView, widthMeasureSpec, 0, heightMeasureSpec, 0); 1078 final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams(); 1079 int width = upLp.leftMargin + mUpView.getMeasuredWidth() + upLp.rightMargin; 1080 int height = upLp.topMargin + mUpView.getMeasuredHeight() + upLp.bottomMargin; 1081 measureChildWithMargins(mIconView, widthMeasureSpec, width, heightMeasureSpec, 0); 1082 final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams(); 1083 width += iconLp.leftMargin + mIconView.getMeasuredWidth() + iconLp.rightMargin; 1084 height = Math.max(height, 1085 iconLp.topMargin + mIconView.getMeasuredHeight() + iconLp.bottomMargin); 1086 setMeasuredDimension(width, height); 1087 } 1088 1089 @Override 1090 protected void onLayout(boolean changed, int l, int t, int r, int b) { 1091 final int vCenter = (b - t) / 2; 1092 int width = r - l; 1093 if (mUpView.getVisibility() != GONE) { 1094 final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams(); 1095 final int upHeight = mUpView.getMeasuredHeight(); 1096 final int upWidth = mUpView.getMeasuredWidth(); 1097 final int upTop = t + vCenter - upHeight / 2; 1098 mUpView.layout(l, upTop, l + upWidth, upTop + upHeight); 1099 final int upOffset = upLp.leftMargin + upWidth + upLp.rightMargin; 1100 width -= upOffset; 1101 l += upOffset; 1102 } 1103 final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams(); 1104 final int iconHeight = mIconView.getMeasuredHeight(); 1105 final int iconWidth = mIconView.getMeasuredWidth(); 1106 final int hCenter = (r - l) / 2; 1107 final int iconLeft = l + iconLp.leftMargin + hCenter - iconWidth / 2; 1108 final int iconTop = t + iconLp.topMargin + vCenter - iconHeight / 2; 1109 mIconView.layout(iconLeft, iconTop, iconLeft + iconWidth, iconTop + iconHeight); 1110 } 1111 } 1112 1113 private class ExpandedActionViewMenuPresenter implements MenuPresenter { 1114 MenuBuilder mMenu; 1115 MenuItemImpl mCurrentExpandedItem; 1116 1117 @Override 1118 public void initForMenu(Context context, MenuBuilder menu) { 1119 // Clear the expanded action view when menus change. 1120 mExpandedActionView = null; 1121 if (mCurrentExpandedItem != null) { 1122 mCurrentExpandedItem.collapseActionView(); 1123 } 1124 mMenu = menu; 1125 } 1126 1127 @Override 1128 public MenuView getMenuView(ViewGroup root) { 1129 return null; 1130 } 1131 1132 @Override 1133 public void updateMenuView(boolean cleared) { 1134 // Make sure the expanded item we have is still there. 1135 if (mCurrentExpandedItem != null) { 1136 boolean found = false; 1137 final int count = mMenu.size(); 1138 for (int i = 0; i < count; i++) { 1139 final MenuItem item = mMenu.getItem(i); 1140 if (item == mCurrentExpandedItem) { 1141 found = true; 1142 break; 1143 } 1144 } 1145 1146 if (!found) { 1147 // The item we had expanded disappeared. Collapse. 1148 collapseItemActionView(mMenu, mCurrentExpandedItem); 1149 } 1150 } 1151 } 1152 1153 @Override 1154 public void setCallback(Callback cb) { 1155 } 1156 1157 @Override 1158 public boolean onSubMenuSelected(SubMenuBuilder subMenu) { 1159 return false; 1160 } 1161 1162 @Override 1163 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { 1164 } 1165 1166 @Override 1167 public boolean flagActionItems() { 1168 return false; 1169 } 1170 1171 @Override 1172 public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) { 1173 mExpandedActionView = item.getActionView(); 1174 mExpandedHomeLayout.setIcon(item.getIcon()); 1175 mCurrentExpandedItem = item; 1176 if (mExpandedActionView.getParent() != ActionBarView.this) { 1177 addView(mExpandedActionView); 1178 } 1179 if (mExpandedHomeLayout.getParent() != ActionBarView.this) { 1180 addView(mExpandedHomeLayout); 1181 } 1182 mHomeLayout.setVisibility(GONE); 1183 mTitleLayout.setVisibility(GONE); 1184 if (mTabScrollView != null) mTabScrollView.setVisibility(GONE); 1185 if (mSpinner != null) mSpinner.setVisibility(GONE); 1186 if (mCustomNavView != null) mCustomNavView.setVisibility(GONE); 1187 requestLayout(); 1188 item.setActionViewExpanded(true); 1189 return true; 1190 } 1191 1192 @Override 1193 public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) { 1194 removeView(mExpandedActionView); 1195 removeView(mExpandedHomeLayout); 1196 if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_HOME) != 0) { 1197 mHomeLayout.setVisibility(VISIBLE); 1198 } 1199 if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 1200 mTitleLayout.setVisibility(VISIBLE); 1201 } 1202 if (mTabScrollView != null && mNavigationMode == ActionBar.NAVIGATION_MODE_TABS) { 1203 mTabScrollView.setVisibility(VISIBLE); 1204 } 1205 if (mSpinner != null && mNavigationMode == ActionBar.NAVIGATION_MODE_LIST) { 1206 mSpinner.setVisibility(VISIBLE); 1207 } 1208 if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { 1209 mCustomNavView.setVisibility(VISIBLE); 1210 } 1211 mExpandedActionView = null; 1212 mExpandedHomeLayout.setIcon(null); 1213 mCurrentExpandedItem = null; 1214 requestLayout(); 1215 item.setActionViewExpanded(false); 1216 return true; 1217 } 1218 } 1219} 1220