ActionBarView.java revision 8515ee846bd76aee86ec5ddfcc4dd1e626dd999c
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.ActionMenuView; 22import com.android.internal.view.menu.MenuBuilder; 23 24import android.app.ActionBar; 25import android.app.ActionBar.OnNavigationListener; 26import android.app.Activity; 27import android.content.Context; 28import android.content.pm.ApplicationInfo; 29import android.content.pm.PackageManager; 30import android.content.pm.PackageManager.NameNotFoundException; 31import android.content.res.TypedArray; 32import android.graphics.drawable.Drawable; 33import android.text.TextUtils.TruncateAt; 34import android.util.AttributeSet; 35import android.util.Log; 36import android.view.ActionMode; 37import android.view.Gravity; 38import android.view.LayoutInflater; 39import android.view.Menu; 40import android.view.View; 41import android.view.ViewGroup; 42import android.view.ViewParent; 43import android.view.Window; 44import android.widget.AdapterView; 45import android.widget.HorizontalScrollView; 46import android.widget.ImageView; 47import android.widget.LinearLayout; 48import android.widget.ProgressBar; 49import android.widget.Spinner; 50import android.widget.SpinnerAdapter; 51import android.widget.TextView; 52 53/** 54 * @hide 55 */ 56public class ActionBarView extends ViewGroup { 57 private static final String TAG = "ActionBarView"; 58 59 /** 60 * Display options applied by default 61 */ 62 public static final int DISPLAY_DEFAULT = 0; 63 64 /** 65 * Display options that require re-layout as opposed to a simple invalidate 66 */ 67 private static final int DISPLAY_RELAYOUT_MASK = 68 ActionBar.DISPLAY_SHOW_HOME | 69 ActionBar.DISPLAY_USE_LOGO | 70 ActionBar.DISPLAY_HOME_AS_UP | 71 ActionBar.DISPLAY_SHOW_CUSTOM | 72 ActionBar.DISPLAY_SHOW_TITLE; 73 74 private static final int DEFAULT_CUSTOM_GRAVITY = Gravity.LEFT | Gravity.CENTER_VERTICAL; 75 76 private final int mContentHeight; 77 78 private int mNavigationMode; 79 private int mDisplayOptions = ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_HOME_AS_UP; 80 private CharSequence mTitle; 81 private CharSequence mSubtitle; 82 private Drawable mIcon; 83 private Drawable mLogo; 84 private Drawable mDivider; 85 86 private View mHomeLayout; 87 private View mHomeAsUpView; 88 private ImageView mIconView; 89 private LinearLayout mTitleLayout; 90 private TextView mTitleView; 91 private TextView mSubtitleView; 92 private Spinner mSpinner; 93 private HorizontalScrollView mTabScrollView; 94 private LinearLayout mTabLayout; 95 private View mCustomNavView; 96 private ProgressBar mProgressView; 97 private ProgressBar mIndeterminateProgressView; 98 99 private int mProgressBarPadding; 100 101 private int mTitleStyleRes; 102 private int mSubtitleStyleRes; 103 private int mProgressStyle; 104 private int mIndeterminateProgressStyle; 105 106 private boolean mShowMenu; 107 private boolean mUserTitle; 108 109 private MenuBuilder mOptionsMenu; 110 private ActionMenuView mMenuView; 111 112 private ActionBarContextView mContextView; 113 114 private ActionMenuItem mLogoNavItem; 115 116 private SpinnerAdapter mSpinnerAdapter; 117 private OnNavigationListener mCallback; 118 119 private final AdapterView.OnItemSelectedListener mNavItemSelectedListener = 120 new AdapterView.OnItemSelectedListener() { 121 public void onItemSelected(AdapterView parent, View view, int position, long id) { 122 if (mCallback != null) { 123 mCallback.onNavigationItemSelected(position, id); 124 } 125 } 126 public void onNothingSelected(AdapterView parent) { 127 // Do nothing 128 } 129 }; 130 131 private OnClickListener mTabClickListener = null; 132 133 public ActionBarView(Context context, AttributeSet attrs) { 134 super(context, attrs); 135 136 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionBar); 137 138 ApplicationInfo appInfo = context.getApplicationInfo(); 139 PackageManager pm = context.getPackageManager(); 140 mNavigationMode = a.getInt(R.styleable.ActionBar_navigationMode, 141 ActionBar.NAVIGATION_MODE_STANDARD); 142 mTitle = a.getText(R.styleable.ActionBar_title); 143 mSubtitle = a.getText(R.styleable.ActionBar_subtitle); 144 145 mLogo = a.getDrawable(R.styleable.ActionBar_logo); 146 if (mLogo == null) { 147 if (context instanceof Activity) { 148 try { 149 mLogo = pm.getActivityLogo(((Activity) context).getComponentName()); 150 } catch (NameNotFoundException e) { 151 Log.e(TAG, "Activity component name not found!", e); 152 } 153 } 154 if (mLogo == null) { 155 mLogo = appInfo.loadLogo(pm); 156 } 157 } 158 159 mIcon = a.getDrawable(R.styleable.ActionBar_icon); 160 if (mIcon == null) { 161 if (context instanceof Activity) { 162 try { 163 mIcon = pm.getActivityIcon(((Activity) context).getComponentName()); 164 } catch (NameNotFoundException e) { 165 Log.e(TAG, "Activity component name not found!", e); 166 } 167 } 168 if (mIcon == null) { 169 mIcon = appInfo.loadIcon(pm); 170 } 171 } 172 173 final LayoutInflater inflater = LayoutInflater.from(context); 174 175 final int homeResId = a.getResourceId( 176 com.android.internal.R.styleable.ActionBar_homeLayout, 177 com.android.internal.R.layout.action_bar_home); 178 179 mHomeLayout = inflater.inflate(homeResId, this, false); 180 181 mHomeAsUpView = mHomeLayout.findViewById(com.android.internal.R.id.up); 182 mIconView = (ImageView) mHomeLayout.findViewById(com.android.internal.R.id.home); 183 184 Drawable background = a.getDrawable(R.styleable.ActionBar_background); 185 if (background != null) { 186 setBackgroundDrawable(background); 187 } 188 189 mTitleStyleRes = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0); 190 mSubtitleStyleRes = a.getResourceId(R.styleable.ActionBar_subtitleTextStyle, 0); 191 mProgressStyle = a.getResourceId(R.styleable.ActionBar_progressBarStyle, 0); 192 mIndeterminateProgressStyle = a.getResourceId( 193 R.styleable.ActionBar_indeterminateProgressStyle, 0); 194 195 mProgressBarPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_progressBarPadding, 0); 196 197 setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, DISPLAY_DEFAULT)); 198 199 final int customNavId = a.getResourceId(R.styleable.ActionBar_customNavigationLayout, 0); 200 if (customNavId != 0) { 201 mCustomNavView = (View) inflater.inflate(customNavId, this, false); 202 mNavigationMode = ActionBar.NAVIGATION_MODE_STANDARD; 203 setDisplayOptions(mDisplayOptions | ActionBar.DISPLAY_SHOW_CUSTOM); 204 } 205 206 mContentHeight = a.getLayoutDimension(R.styleable.ActionBar_height, 0); 207 208 mDivider = a.getDrawable(R.styleable.ActionBar_divider); 209 210 a.recycle(); 211 212 mLogoNavItem = new ActionMenuItem(context, 0, android.R.id.home, 0, 0, mTitle); 213 mHomeLayout.setOnClickListener(new OnClickListener() { 214 public void onClick(View v) { 215 Context context = getContext(); 216 if (context instanceof Activity) { 217 Activity activity = (Activity) context; 218 activity.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mLogoNavItem); 219 } 220 } 221 }); 222 mHomeLayout.setClickable(true); 223 mHomeLayout.setFocusable(true); 224 } 225 226 public void initProgress() { 227 mProgressView = new ProgressBar(mContext, null, 0, mProgressStyle); 228 mProgressView.setId(R.id.progress_horizontal); 229 mProgressView.setMax(10000); 230 addView(mProgressView); 231 } 232 233 public void initIndeterminateProgress() { 234 mIndeterminateProgressView = new ProgressBar(mContext, null, 0, 235 mIndeterminateProgressStyle); 236 mIndeterminateProgressView.setId(R.id.progress_circular); 237 addView(mIndeterminateProgressView); 238 } 239 240 @Override 241 public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) { 242 // No starting an action mode for an action bar child! (Where would it go?) 243 return null; 244 } 245 246 public void setCallback(OnNavigationListener callback) { 247 mCallback = callback; 248 } 249 250 public void setMenu(Menu menu) { 251 MenuBuilder builder = (MenuBuilder) menu; 252 mOptionsMenu = builder; 253 if (mMenuView != null) { 254 removeView(mMenuView); 255 } 256 final ActionMenuView menuView = (ActionMenuView) builder.getMenuView( 257 MenuBuilder.TYPE_ACTION_BUTTON, null); 258 final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, 259 LayoutParams.MATCH_PARENT); 260 menuView.setLayoutParams(layoutParams); 261 addView(menuView); 262 mMenuView = menuView; 263 } 264 265 public boolean showOverflowMenu() { 266 if (mMenuView != null) { 267 return mMenuView.showOverflowMenu(); 268 } 269 return false; 270 } 271 272 public void openOverflowMenu() { 273 if (mMenuView != null) { 274 mMenuView.openOverflowMenu(); 275 } 276 } 277 278 public void postShowOverflowMenu() { 279 post(new Runnable() { 280 public void run() { 281 showOverflowMenu(); 282 } 283 }); 284 } 285 286 public boolean hideOverflowMenu() { 287 if (mMenuView != null) { 288 return mMenuView.hideOverflowMenu(); 289 } 290 return false; 291 } 292 293 public boolean isOverflowMenuShowing() { 294 if (mMenuView != null) { 295 return mMenuView.isOverflowMenuShowing(); 296 } 297 return false; 298 } 299 300 public boolean isOverflowMenuOpen() { 301 if (mMenuView != null) { 302 return mMenuView.isOverflowMenuOpen(); 303 } 304 return false; 305 } 306 307 public boolean isOverflowReserved() { 308 return mMenuView != null && mMenuView.isOverflowReserved(); 309 } 310 311 public void setCustomNavigationView(View view) { 312 final boolean showCustom = (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0; 313 if (mCustomNavView != null && showCustom) { 314 removeView(mCustomNavView); 315 } 316 mCustomNavView = view; 317 if (mCustomNavView != null && showCustom) { 318 addView(mCustomNavView); 319 } 320 } 321 322 public CharSequence getTitle() { 323 return mTitle; 324 } 325 326 /** 327 * Set the action bar title. This will always replace or override window titles. 328 * @param title Title to set 329 * 330 * @see #setWindowTitle(CharSequence) 331 */ 332 public void setTitle(CharSequence title) { 333 mUserTitle = true; 334 setTitleImpl(title); 335 } 336 337 /** 338 * Set the window title. A window title will always be replaced or overridden by a user title. 339 * @param title Title to set 340 * 341 * @see #setTitle(CharSequence) 342 */ 343 public void setWindowTitle(CharSequence title) { 344 if (!mUserTitle) { 345 setTitleImpl(title); 346 } 347 } 348 349 private void setTitleImpl(CharSequence title) { 350 mTitle = title; 351 if (mTitleView != null) { 352 mTitleView.setText(title); 353 } 354 if (mLogoNavItem != null) { 355 mLogoNavItem.setTitle(title); 356 } 357 } 358 359 public CharSequence getSubtitle() { 360 return mSubtitle; 361 } 362 363 public void setSubtitle(CharSequence subtitle) { 364 mSubtitle = subtitle; 365 if (mSubtitleView != null) { 366 mSubtitleView.setText(subtitle); 367 mSubtitleView.setVisibility(subtitle != null ? VISIBLE : GONE); 368 } 369 } 370 371 public void setDisplayOptions(int options) { 372 // TODO Remove this once DISPLAY_HIDE_HOME is removed 373 if ((options & ActionBar.DISPLAY_HIDE_HOME) != 0) { 374 options &= ~(ActionBar.DISPLAY_HIDE_HOME | ActionBar.DISPLAY_SHOW_HOME); 375 } 376 // End TODO 377 378 final int flagsChanged = options ^ mDisplayOptions; 379 mDisplayOptions = options; 380 if ((flagsChanged & DISPLAY_RELAYOUT_MASK) != 0) { 381 final int vis = (options & ActionBar.DISPLAY_SHOW_HOME) != 0 ? VISIBLE : GONE; 382 mHomeLayout.setVisibility(vis); 383 384 if ((flagsChanged & ActionBar.DISPLAY_HOME_AS_UP) != 0) { 385 mHomeAsUpView.setVisibility((options & ActionBar.DISPLAY_HOME_AS_UP) != 0 386 ? VISIBLE : INVISIBLE); 387 } 388 389 if ((flagsChanged & ActionBar.DISPLAY_USE_LOGO) != 0) { 390 final boolean logoVis = mLogo != null && (options & ActionBar.DISPLAY_USE_LOGO) != 0; 391 mIconView.setImageDrawable(logoVis ? mLogo : mIcon); 392 } 393 394 if ((flagsChanged & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 395 if ((options & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 396 initTitle(); 397 } else { 398 removeView(mTitleLayout); 399 } 400 } 401 402 if ((flagsChanged & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomNavView != null) { 403 if ((options & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { 404 addView(mCustomNavView); 405 } else { 406 removeView(mCustomNavView); 407 } 408 } 409 410 requestLayout(); 411 } else { 412 invalidate(); 413 } 414 } 415 416 public void setNavigationMode(int mode) { 417 final int oldMode = mNavigationMode; 418 if (mode != oldMode) { 419 switch (oldMode) { 420 case ActionBar.NAVIGATION_MODE_LIST: 421 if (mSpinner != null) { 422 removeView(mSpinner); 423 } 424 break; 425 case ActionBar.NAVIGATION_MODE_TABS: 426 if (mTabLayout != null) { 427 removeView(mTabScrollView); 428 } 429 } 430 431 switch (mode) { 432 case ActionBar.NAVIGATION_MODE_LIST: 433 mSpinner = new Spinner(mContext, null, 434 com.android.internal.R.attr.actionDropDownStyle); 435 mSpinner.setAdapter(mSpinnerAdapter); 436 mSpinner.setOnItemSelectedListener(mNavItemSelectedListener); 437 addView(mSpinner); 438 break; 439 case ActionBar.NAVIGATION_MODE_TABS: 440 ensureTabsExist(); 441 addView(mTabScrollView); 442 break; 443 } 444 mNavigationMode = mode; 445 requestLayout(); 446 } 447 } 448 449 private void ensureTabsExist() { 450 if (mTabScrollView == null) { 451 mTabScrollView = new HorizontalScrollView(getContext()); 452 mTabLayout = new LinearLayout(getContext(), null, 453 com.android.internal.R.attr.actionBarTabBarStyle); 454 mTabScrollView.addView(mTabLayout); 455 } 456 } 457 458 public void setDropdownAdapter(SpinnerAdapter adapter) { 459 mSpinnerAdapter = adapter; 460 if (mSpinner != null) { 461 mSpinner.setAdapter(adapter); 462 } 463 } 464 465 public SpinnerAdapter getDropdownAdapter() { 466 return mSpinnerAdapter; 467 } 468 469 public void setDropdownSelectedPosition(int position) { 470 mSpinner.setSelection(position); 471 } 472 473 public int getDropdownSelectedPosition() { 474 return mSpinner.getSelectedItemPosition(); 475 } 476 477 public View getCustomNavigationView() { 478 return mCustomNavView; 479 } 480 481 public int getNavigationMode() { 482 return mNavigationMode; 483 } 484 485 public int getDisplayOptions() { 486 return mDisplayOptions; 487 } 488 489 private TabView createTabView(ActionBar.Tab tab) { 490 final TabView tabView = new TabView(getContext(), tab); 491 tabView.setFocusable(true); 492 493 if (mTabClickListener == null) { 494 mTabClickListener = new TabClickListener(); 495 } 496 tabView.setOnClickListener(mTabClickListener); 497 return tabView; 498 } 499 500 public void addTab(ActionBar.Tab tab, boolean setSelected) { 501 ensureTabsExist(); 502 View tabView = createTabView(tab); 503 mTabLayout.addView(tabView); 504 if (setSelected) { 505 tabView.setSelected(true); 506 } 507 } 508 509 public void addTab(ActionBar.Tab tab, int position, boolean setSelected) { 510 ensureTabsExist(); 511 final TabView tabView = createTabView(tab); 512 mTabLayout.addView(tabView, position); 513 if (setSelected) { 514 tabView.setSelected(true); 515 } 516 } 517 518 public void removeTabAt(int position) { 519 if (mTabLayout != null) { 520 mTabLayout.removeViewAt(position); 521 } 522 } 523 524 public void removeAllTabs() { 525 if (mTabLayout != null) { 526 mTabLayout.removeAllViews(); 527 } 528 } 529 530 @Override 531 protected LayoutParams generateDefaultLayoutParams() { 532 // Used by custom nav views if they don't supply layout params. Everything else 533 // added to an ActionBarView should have them already. 534 return new ActionBar.LayoutParams(DEFAULT_CUSTOM_GRAVITY); 535 } 536 537 @Override 538 protected void onFinishInflate() { 539 super.onFinishInflate(); 540 541 addView(mHomeLayout); 542 543 if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { 544 final ViewParent parent = mCustomNavView.getParent(); 545 if (parent != this) { 546 if (parent instanceof ViewGroup) { 547 ((ViewGroup) parent).removeView(mCustomNavView); 548 } 549 addView(mCustomNavView); 550 } 551 } 552 } 553 554 private void initTitle() { 555 LayoutInflater inflater = LayoutInflater.from(getContext()); 556 mTitleLayout = (LinearLayout) inflater.inflate(R.layout.action_bar_title_item, null); 557 mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title); 558 mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle); 559 560 if (mTitleStyleRes != 0) { 561 mTitleView.setTextAppearance(mContext, mTitleStyleRes); 562 } 563 if (mTitle != null) { 564 mTitleView.setText(mTitle); 565 } 566 567 if (mSubtitleStyleRes != 0) { 568 mSubtitleView.setTextAppearance(mContext, mSubtitleStyleRes); 569 } 570 if (mSubtitle != null) { 571 mSubtitleView.setText(mSubtitle); 572 mSubtitleView.setVisibility(VISIBLE); 573 } 574 575 addView(mTitleLayout); 576 } 577 578 public void setTabSelected(int position) { 579 ensureTabsExist(); 580 final int tabCount = mTabLayout.getChildCount(); 581 for (int i = 0; i < tabCount; i++) { 582 final View child = mTabLayout.getChildAt(i); 583 child.setSelected(i == position); 584 } 585 } 586 587 public void setContextView(ActionBarContextView view) { 588 mContextView = view; 589 } 590 591 @Override 592 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 593 int widthMode = MeasureSpec.getMode(widthMeasureSpec); 594 if (widthMode != MeasureSpec.EXACTLY) { 595 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + 596 "with android:layout_width=\"match_parent\" (or fill_parent)"); 597 } 598 599 int heightMode = MeasureSpec.getMode(heightMeasureSpec); 600 if (heightMode != MeasureSpec.AT_MOST) { 601 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + 602 "with android:layout_height=\"wrap_content\""); 603 } 604 605 int contentWidth = MeasureSpec.getSize(widthMeasureSpec); 606 607 int maxHeight = mContentHeight > 0 ? 608 mContentHeight : MeasureSpec.getSize(heightMeasureSpec); 609 610 final int verticalPadding = getPaddingTop() + getPaddingBottom(); 611 final int paddingLeft = getPaddingLeft(); 612 final int paddingRight = getPaddingRight(); 613 final int height = maxHeight - verticalPadding; 614 final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST); 615 616 int availableWidth = contentWidth - paddingLeft - paddingRight; 617 int leftOfCenter = availableWidth / 2; 618 int rightOfCenter = leftOfCenter; 619 620 if (mHomeLayout.getVisibility() != GONE) { 621 mHomeLayout.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), 622 MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); 623 final int homeWidth = mHomeLayout.getMeasuredWidth(); 624 availableWidth -= homeWidth; 625 leftOfCenter -= homeWidth; 626 } 627 628 if (mMenuView != null) { 629 availableWidth = measureChildView(mMenuView, availableWidth, 630 childSpecHeight, 0); 631 rightOfCenter -= mMenuView.getMeasuredWidth(); 632 } 633 634 if (mTitleLayout != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 635 availableWidth = measureChildView(mTitleLayout, availableWidth, childSpecHeight, 0); 636 leftOfCenter -= mTitleLayout.getMeasuredWidth(); 637 } 638 639 switch (mNavigationMode) { 640 case ActionBar.NAVIGATION_MODE_LIST: 641 if (mSpinner != null) { 642 mSpinner.measure( 643 MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), 644 MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST)); 645 final int spinnerWidth = mSpinner.getMeasuredWidth(); 646 availableWidth -= spinnerWidth; 647 leftOfCenter -= spinnerWidth; 648 } 649 break; 650 case ActionBar.NAVIGATION_MODE_TABS: 651 if (mTabScrollView != null) { 652 mTabScrollView.measure( 653 MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), 654 MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); 655 final int tabWidth = mTabScrollView.getMeasuredWidth(); 656 availableWidth -= tabWidth; 657 leftOfCenter -= tabWidth; 658 } 659 break; 660 } 661 662 if (mIndeterminateProgressView != null && 663 mIndeterminateProgressView.getVisibility() != GONE) { 664 availableWidth = measureChildView(mIndeterminateProgressView, availableWidth, 665 childSpecHeight, 0); 666 rightOfCenter -= mIndeterminateProgressView.getMeasuredWidth(); 667 } 668 669 if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomNavView != null) { 670 final LayoutParams lp = generateLayoutParams(mCustomNavView.getLayoutParams()); 671 final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ? 672 (ActionBar.LayoutParams) lp : null; 673 674 int horizontalMargin = 0; 675 int verticalMargin = 0; 676 if (ablp != null) { 677 horizontalMargin = ablp.leftMargin + ablp.rightMargin; 678 verticalMargin = ablp.topMargin + ablp.bottomMargin; 679 } 680 681 // If the action bar is wrapping to its content height, don't allow a custom 682 // view to MATCH_PARENT. 683 int customNavHeightMode; 684 if (mContentHeight <= 0) { 685 customNavHeightMode = MeasureSpec.AT_MOST; 686 } else { 687 customNavHeightMode = lp.height != LayoutParams.WRAP_CONTENT ? 688 MeasureSpec.EXACTLY : MeasureSpec.AT_MOST; 689 } 690 final int customNavHeight = Math.max(0, 691 (lp.height >= 0 ? Math.min(lp.height, height) : height) - verticalMargin); 692 693 final int customNavWidthMode = lp.width != LayoutParams.WRAP_CONTENT ? 694 MeasureSpec.EXACTLY : MeasureSpec.AT_MOST; 695 int customNavWidth = Math.max(0, 696 (lp.width >= 0 ? Math.min(lp.width, availableWidth) : availableWidth) 697 - horizontalMargin); 698 final int hgrav = (ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY) & 699 Gravity.HORIZONTAL_GRAVITY_MASK; 700 701 // Centering a custom view is treated specially; we try to center within the whole 702 // action bar rather than in the available space. 703 if (hgrav == Gravity.CENTER_HORIZONTAL && lp.width == LayoutParams.MATCH_PARENT) { 704 customNavWidth = Math.min(leftOfCenter, rightOfCenter) * 2; 705 } 706 707 mCustomNavView.measure( 708 MeasureSpec.makeMeasureSpec(customNavWidth, customNavWidthMode), 709 MeasureSpec.makeMeasureSpec(customNavHeight, customNavHeightMode)); 710 } 711 712 if (mContentHeight <= 0) { 713 int measuredHeight = 0; 714 final int count = getChildCount(); 715 for (int i = 0; i < count; i++) { 716 View v = getChildAt(i); 717 int paddedViewHeight = v.getMeasuredHeight() + verticalPadding; 718 if (paddedViewHeight > measuredHeight) { 719 measuredHeight = paddedViewHeight; 720 } 721 } 722 setMeasuredDimension(contentWidth, measuredHeight); 723 } else { 724 setMeasuredDimension(contentWidth, maxHeight); 725 } 726 727 if (mContextView != null) { 728 mContextView.setHeight(getMeasuredHeight()); 729 } 730 731 if (mProgressView != null && mProgressView.getVisibility() != GONE) { 732 mProgressView.measure(MeasureSpec.makeMeasureSpec( 733 contentWidth - mProgressBarPadding * 2, MeasureSpec.EXACTLY), 734 MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST)); 735 } 736 } 737 738 private int measureChildView(View child, int availableWidth, int childSpecHeight, int spacing) { 739 child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), 740 childSpecHeight); 741 742 availableWidth -= child.getMeasuredWidth(); 743 availableWidth -= spacing; 744 745 return availableWidth; 746 } 747 748 @Override 749 protected void onLayout(boolean changed, int l, int t, int r, int b) { 750 int x = getPaddingLeft(); 751 final int y = getPaddingTop(); 752 final int contentHeight = b - t - getPaddingTop() - getPaddingBottom(); 753 754 if (mHomeLayout.getVisibility() != GONE) { 755 x += positionChild(mHomeLayout, x, y, contentHeight); 756 } 757 758 if (mTitleLayout != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 759 x += positionChild(mTitleLayout, x, y, contentHeight); 760 } 761 762 switch (mNavigationMode) { 763 case ActionBar.NAVIGATION_MODE_STANDARD: 764 break; 765 case ActionBar.NAVIGATION_MODE_LIST: 766 if (mSpinner != null) { 767 x += positionChild(mSpinner, x, y, contentHeight); 768 } 769 break; 770 case ActionBar.NAVIGATION_MODE_TABS: 771 if (mTabScrollView != null) { 772 x += positionChild(mTabScrollView, x, y, contentHeight); 773 } 774 break; 775 } 776 777 int menuLeft = r - l - getPaddingRight(); 778 if (mMenuView != null) { 779 positionChildInverse(mMenuView, menuLeft, y, contentHeight); 780 menuLeft -= mMenuView.getMeasuredWidth(); 781 } 782 783 if (mIndeterminateProgressView != null && 784 mIndeterminateProgressView.getVisibility() != GONE) { 785 positionChildInverse(mIndeterminateProgressView, menuLeft, y, contentHeight); 786 menuLeft -= mIndeterminateProgressView.getMeasuredWidth(); 787 } 788 789 if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { 790 LayoutParams lp = mCustomNavView.getLayoutParams(); 791 final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ? 792 (ActionBar.LayoutParams) lp : null; 793 794 final int gravity = ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY; 795 final int navWidth = mCustomNavView.getMeasuredWidth(); 796 797 int topMargin = 0; 798 int bottomMargin = 0; 799 if (ablp != null) { 800 x += ablp.leftMargin; 801 menuLeft -= ablp.rightMargin; 802 topMargin = ablp.topMargin; 803 bottomMargin = ablp.bottomMargin; 804 } 805 806 int hgravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK; 807 // See if we actually have room to truly center; if not push against left or right. 808 if (hgravity == Gravity.CENTER_HORIZONTAL) { 809 final int centeredLeft = ((mRight - mLeft) - navWidth) / 2; 810 if (centeredLeft < x) { 811 hgravity = Gravity.LEFT; 812 } else if (centeredLeft + navWidth > menuLeft) { 813 hgravity = Gravity.RIGHT; 814 } 815 } 816 817 int xpos = 0; 818 switch (hgravity) { 819 case Gravity.CENTER_HORIZONTAL: 820 xpos = ((mRight - mLeft) - navWidth) / 2; 821 break; 822 case Gravity.LEFT: 823 xpos = x; 824 break; 825 case Gravity.RIGHT: 826 xpos = menuLeft - navWidth; 827 break; 828 } 829 830 int ypos = 0; 831 switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) { 832 case Gravity.CENTER_VERTICAL: 833 final int paddedTop = mTop + getPaddingTop(); 834 final int paddedBottom = mBottom - getPaddingBottom(); 835 ypos = ((paddedBottom - paddedTop) - mCustomNavView.getMeasuredHeight()) / 2; 836 break; 837 case Gravity.TOP: 838 ypos = getPaddingTop() + topMargin; 839 break; 840 case Gravity.BOTTOM: 841 ypos = getHeight() - getPaddingBottom() - mCustomNavView.getMeasuredHeight() 842 - bottomMargin; 843 break; 844 } 845 x += positionChild(mCustomNavView, xpos, ypos, contentHeight); 846 } 847 848 if (mProgressView != null) { 849 mProgressView.bringToFront(); 850 final int halfProgressHeight = mProgressView.getMeasuredHeight() / 2; 851 mProgressView.layout(mProgressBarPadding, -halfProgressHeight, 852 mProgressBarPadding + mProgressView.getMeasuredWidth(), halfProgressHeight); 853 } 854 } 855 856 private int positionChild(View child, int x, int y, int contentHeight) { 857 int childWidth = child.getMeasuredWidth(); 858 int childHeight = child.getMeasuredHeight(); 859 int childTop = y + (contentHeight - childHeight) / 2; 860 861 child.layout(x, childTop, x + childWidth, childTop + childHeight); 862 863 return childWidth; 864 } 865 866 private int positionChildInverse(View child, int x, int y, int contentHeight) { 867 int childWidth = child.getMeasuredWidth(); 868 int childHeight = child.getMeasuredHeight(); 869 int childTop = y + (contentHeight - childHeight) / 2; 870 871 child.layout(x - childWidth, childTop, x, childTop + childHeight); 872 873 return childWidth; 874 } 875 876 private static class TabView extends LinearLayout { 877 private ActionBar.Tab mTab; 878 879 public TabView(Context context, ActionBar.Tab tab) { 880 super(context, null, com.android.internal.R.attr.actionBarTabStyle); 881 mTab = tab; 882 883 final View custom = tab.getCustomView(); 884 if (custom != null) { 885 addView(custom); 886 } else { 887 // TODO Style tabs based on the theme 888 889 final Drawable icon = tab.getIcon(); 890 final CharSequence text = tab.getText(); 891 892 if (icon != null) { 893 ImageView iconView = new ImageView(context); 894 iconView.setImageDrawable(icon); 895 LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, 896 LayoutParams.WRAP_CONTENT); 897 lp.gravity = Gravity.CENTER_VERTICAL; 898 iconView.setLayoutParams(lp); 899 addView(iconView); 900 } 901 902 if (text != null) { 903 TextView textView = new TextView(context, null, 904 com.android.internal.R.attr.actionBarTabTextStyle); 905 textView.setText(text); 906 textView.setSingleLine(); 907 textView.setEllipsize(TruncateAt.END); 908 LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, 909 LayoutParams.WRAP_CONTENT); 910 lp.gravity = Gravity.CENTER_VERTICAL; 911 textView.setLayoutParams(lp); 912 addView(textView); 913 } 914 } 915 916 setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, 917 LayoutParams.MATCH_PARENT, 1)); 918 } 919 920 public ActionBar.Tab getTab() { 921 return mTab; 922 } 923 } 924 925 private class TabClickListener implements OnClickListener { 926 public void onClick(View view) { 927 TabView tabView = (TabView) view; 928 tabView.getTab().select(); 929 final int tabCount = mTabLayout.getChildCount(); 930 for (int i = 0; i < tabCount; i++) { 931 final View child = mTabLayout.getChildAt(i); 932 child.setSelected(child == view); 933 } 934 } 935 } 936} 937