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