ToolbarWidgetWrapper.java revision a2c72b87007d10202c25e78e904938f770c6337d
1/* 2 * Copyright (C) 2014 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 17 18package android.support.v7.internal.widget; 19 20import android.app.ActionBar; 21import android.content.Context; 22import android.content.res.TypedArray; 23import android.graphics.drawable.Drawable; 24import android.os.Parcelable; 25import android.support.v4.content.ContextCompat; 26import android.support.v4.view.ViewCompat; 27import android.support.v4.view.ViewPropertyAnimatorListenerAdapter; 28import android.support.v7.appcompat.R; 29import android.support.v7.internal.app.WindowCallback; 30import android.support.v7.internal.view.menu.ActionMenuItem; 31import android.support.v7.widget.ActionMenuPresenter; 32import android.support.v7.internal.view.menu.MenuBuilder; 33import android.support.v7.internal.view.menu.MenuPresenter; 34import android.support.v7.widget.Toolbar; 35import android.text.TextUtils; 36import android.util.Log; 37import android.util.SparseArray; 38import android.view.Gravity; 39import android.view.LayoutInflater; 40import android.view.Menu; 41import android.view.View; 42import android.view.ViewGroup; 43import android.view.Window; 44import android.widget.SpinnerAdapter; 45 46/** 47 * Internal class used to interact with the Toolbar widget without 48 * exposing interface methods to the public API. 49 * 50 * <p>ToolbarWidgetWrapper manages the differences between Toolbar and ActionBarView 51 * so that either variant acting as a 52 * {@link android.support.v7.internal.app.WindowDecorActionBar WindowDecorActionBar} can behave 53 * in the same way.</p> 54 * 55 * @hide 56 */ 57public class ToolbarWidgetWrapper implements DecorToolbar { 58 private static final String TAG = "ToolbarWidgetWrapper"; 59 60 private static final int AFFECTS_LOGO_MASK = 61 ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_USE_LOGO; 62 63 private Toolbar mToolbar; 64 65 private int mDisplayOpts; 66 private View mTabView; 67 private SpinnerCompat mSpinner; 68 private View mCustomView; 69 70 private Drawable mIcon; 71 private Drawable mLogo; 72 private Drawable mNavIcon; 73 74 private boolean mTitleSet; 75 private CharSequence mTitle; 76 private CharSequence mSubtitle; 77 78 private WindowCallback mWindowCallback; 79 private boolean mMenuPrepared; 80 private ActionMenuPresenter mActionMenuPresenter; 81 82 private int mNavigationMode = ActionBar.NAVIGATION_MODE_STANDARD; 83 84 public ToolbarWidgetWrapper(Toolbar toolbar, boolean style) { 85 mToolbar = toolbar; 86 87 mTitle = toolbar.getTitle(); 88 mSubtitle = toolbar.getSubtitle(); 89 mTitleSet = !TextUtils.isEmpty(mTitle); 90 91 if (style) { 92 final TypedArray a = toolbar.getContext().obtainStyledAttributes(null, 93 R.styleable.ActionBar, R.attr.actionBarStyle, 0); 94 95 final CharSequence title = a.getText(R.styleable.ActionBar_title); 96 if (!TextUtils.isEmpty(title)) { 97 setTitle(title); 98 } 99 100 final CharSequence subtitle = a.getText(R.styleable.ActionBar_subtitle); 101 if (!TextUtils.isEmpty(subtitle)) { 102 setSubtitle(subtitle); 103 } 104 105 final Drawable logo = a.getDrawable(R.styleable.ActionBar_logo); 106 if (logo != null) { 107 setLogo(logo); 108 } 109 110 final Drawable icon = a.getDrawable(R.styleable.ActionBar_icon); 111 if (icon != null) { 112 setIcon(icon); 113 } 114 115 final Drawable navIcon = a.getDrawable(R.styleable.ActionBar_homeAsUpIndicator); 116 if (navIcon != null) { 117 setNavigationIcon(navIcon); 118 } 119 120 setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, 0)); 121 122 final int customNavId = a.getResourceId( 123 R.styleable.ActionBar_customNavigationLayout, 0); 124 if (customNavId != 0) { 125 setCustomView(LayoutInflater.from(mToolbar.getContext()).inflate(customNavId, 126 mToolbar, false)); 127 setDisplayOptions(mDisplayOpts | ActionBar.DISPLAY_SHOW_CUSTOM); 128 } 129 130 final int height = a.getLayoutDimension(R.styleable.ActionBar_height, 0); 131 if (height > 0) { 132 final ViewGroup.LayoutParams lp = mToolbar.getLayoutParams(); 133 lp.height = height; 134 mToolbar.setLayoutParams(lp); 135 } 136 137 final int contentInsetStart = a.getDimensionPixelOffset( 138 R.styleable.ActionBar_contentInsetStart, -1); 139 final int contentInsetEnd = a.getDimensionPixelOffset( 140 R.styleable.ActionBar_contentInsetEnd, -1); 141 if (contentInsetStart >= 0 || contentInsetEnd >= 0) { 142 mToolbar.setContentInsetsRelative(Math.max(contentInsetStart, 0), 143 Math.max(contentInsetEnd, 0)); 144 } 145 146 final int titleTextStyle = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0); 147 if (titleTextStyle != 0) { 148 mToolbar.setTitleTextAppearance(mToolbar.getContext(), titleTextStyle); 149 } 150 151 final int subtitleTextStyle = a.getResourceId( 152 R.styleable.ActionBar_subtitleTextStyle, 0); 153 if (subtitleTextStyle != 0) { 154 mToolbar.setSubtitleTextAppearance(mToolbar.getContext(), subtitleTextStyle); 155 } 156 157 final int popupTheme = a.getResourceId(R.styleable.ActionBar_popupTheme, 0); 158 if (popupTheme != 0) { 159 mToolbar.setPopupTheme(popupTheme); 160 } 161 162 a.recycle(); 163 } 164 165 if (TextUtils.isEmpty(mToolbar.getNavigationContentDescription())) { 166 mToolbar.setNavigationContentDescription( 167 getContext().getResources().getText(R.string.abc_action_bar_up_description)); 168 } 169 170 mToolbar.setNavigationOnClickListener(new View.OnClickListener() { 171 final ActionMenuItem mNavItem = new ActionMenuItem(mToolbar.getContext(), 172 0, android.R.id.home, 0, 0, mTitle); 173 @Override 174 public void onClick(View v) { 175 if (mWindowCallback != null && mMenuPrepared) { 176 mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mNavItem); 177 } 178 } 179 }); 180 } 181 182 @Override 183 public ViewGroup getViewGroup() { 184 return mToolbar; 185 } 186 187 @Override 188 public Context getContext() { 189 return mToolbar.getContext(); 190 } 191 192 @Override 193 public boolean isSplit() { 194 return false; 195 } 196 197 @Override 198 public boolean hasExpandedActionView() { 199 return mToolbar.hasExpandedActionView(); 200 } 201 202 @Override 203 public void collapseActionView() { 204 mToolbar.collapseActionView(); 205 } 206 207 @Override 208 public void setWindowCallback(WindowCallback cb) { 209 mWindowCallback = cb; 210 } 211 212 @Override 213 public void setWindowTitle(CharSequence title) { 214 // "Real" title always trumps window title. 215 if (!mTitleSet) { 216 setTitleInt(title); 217 } 218 } 219 220 @Override 221 public CharSequence getTitle() { 222 return mToolbar.getTitle(); 223 } 224 225 @Override 226 public void setTitle(CharSequence title) { 227 mTitleSet = true; 228 setTitleInt(title); 229 } 230 231 private void setTitleInt(CharSequence title) { 232 mTitle = title; 233 if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 234 mToolbar.setTitle(title); 235 } 236 } 237 238 @Override 239 public CharSequence getSubtitle() { 240 return mToolbar.getSubtitle(); 241 } 242 243 @Override 244 public void setSubtitle(CharSequence subtitle) { 245 mSubtitle = subtitle; 246 if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 247 mToolbar.setSubtitle(subtitle); 248 } 249 } 250 251 @Override 252 public void initProgress() { 253 Log.i(TAG, "Progress display unsupported"); 254 } 255 256 @Override 257 public void initIndeterminateProgress() { 258 Log.i(TAG, "Progress display unsupported"); 259 } 260 261 @Override 262 public boolean canSplit() { 263 return false; 264 } 265 266 @Override 267 public void setSplitView(ViewGroup splitView) { 268 } 269 270 @Override 271 public void setSplitToolbar(boolean split) { 272 if (split) { 273 throw new UnsupportedOperationException("Cannot split an android.widget.Toolbar"); 274 } 275 } 276 277 @Override 278 public void setSplitWhenNarrow(boolean splitWhenNarrow) { 279 // Ignore. 280 } 281 282 @Override 283 public boolean hasIcon() { 284 return mIcon != null; 285 } 286 287 @Override 288 public boolean hasLogo() { 289 return mLogo != null; 290 } 291 292 @Override 293 public void setIcon(int resId) { 294 setIcon(resId != 0 ? ContextCompat.getDrawable(getContext(), resId) : null); 295 } 296 297 @Override 298 public void setIcon(Drawable d) { 299 mIcon = d; 300 updateToolbarLogo(); 301 } 302 303 @Override 304 public void setLogo(int resId) { 305 setLogo(resId != 0 ? ContextCompat.getDrawable(getContext(), resId) : null); 306 } 307 308 @Override 309 public void setLogo(Drawable d) { 310 mLogo = d; 311 updateToolbarLogo(); 312 } 313 314 private void updateToolbarLogo() { 315 Drawable logo = null; 316 if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_HOME) != 0) { 317 if ((mDisplayOpts & ActionBar.DISPLAY_USE_LOGO) != 0) { 318 logo = mLogo != null ? mLogo : mIcon; 319 } else { 320 logo = mIcon; 321 } 322 } 323 mToolbar.setLogo(logo); 324 } 325 326 @Override 327 public boolean canShowOverflowMenu() { 328 return mToolbar.canShowOverflowMenu(); 329 } 330 331 @Override 332 public boolean isOverflowMenuShowing() { 333 return mToolbar.isOverflowMenuShowing(); 334 } 335 336 @Override 337 public boolean isOverflowMenuShowPending() { 338 return mToolbar.isOverflowMenuShowPending(); 339 } 340 341 @Override 342 public boolean showOverflowMenu() { 343 return mToolbar.showOverflowMenu(); 344 } 345 346 @Override 347 public boolean hideOverflowMenu() { 348 return mToolbar.hideOverflowMenu(); 349 } 350 351 @Override 352 public void setMenuPrepared() { 353 mMenuPrepared = true; 354 } 355 356 @Override 357 public void setMenu(Menu menu, MenuPresenter.Callback cb) { 358 if (mActionMenuPresenter == null) { 359 mActionMenuPresenter = new ActionMenuPresenter(mToolbar.getContext()); 360 mActionMenuPresenter.setId(R.id.action_menu_presenter); 361 } 362 mActionMenuPresenter.setCallback(cb); 363 mToolbar.setMenu((MenuBuilder) menu, mActionMenuPresenter); 364 } 365 366 @Override 367 public void dismissPopupMenus() { 368 mToolbar.dismissPopupMenus(); 369 } 370 371 @Override 372 public int getDisplayOptions() { 373 return mDisplayOpts; 374 } 375 376 @Override 377 public void setDisplayOptions(int newOpts) { 378 final int oldOpts = mDisplayOpts; 379 final int changed = oldOpts ^ newOpts; 380 mDisplayOpts = newOpts; 381 if (changed != 0) { 382 if ((changed & ActionBar.DISPLAY_HOME_AS_UP) != 0) { 383 if ((newOpts & ActionBar.DISPLAY_HOME_AS_UP) != 0) { 384 mToolbar.setNavigationIcon(mNavIcon); 385 } else { 386 mToolbar.setNavigationIcon(null); 387 } 388 } 389 390 if ((changed & AFFECTS_LOGO_MASK) != 0) { 391 updateToolbarLogo(); 392 } 393 394 if ((changed & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 395 if ((newOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 396 mToolbar.setTitle(mTitle); 397 mToolbar.setSubtitle(mSubtitle); 398 } else { 399 mToolbar.setTitle(null); 400 mToolbar.setSubtitle(null); 401 } 402 } 403 404 if ((changed & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomView != null) { 405 if ((newOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { 406 mToolbar.addView(mCustomView); 407 } else { 408 mToolbar.removeView(mCustomView); 409 } 410 } 411 } 412 } 413 414 @Override 415 public void setEmbeddedTabView(ScrollingTabContainerView tabView) { 416 if (mTabView != null && mTabView.getParent() == mToolbar) { 417 mToolbar.removeView(mTabView); 418 } 419 mTabView = tabView; 420 if (tabView != null && mNavigationMode == ActionBar.NAVIGATION_MODE_TABS) { 421 mToolbar.addView(mTabView, 0); 422 Toolbar.LayoutParams lp = (Toolbar.LayoutParams) mTabView.getLayoutParams(); 423 lp.width = ViewGroup.LayoutParams.WRAP_CONTENT; 424 lp.height = ViewGroup.LayoutParams.WRAP_CONTENT; 425 lp.gravity = Gravity.START | Gravity.BOTTOM; 426 tabView.setAllowCollapse(true); 427 } 428 } 429 430 @Override 431 public boolean hasEmbeddedTabs() { 432 return mTabView != null; 433 } 434 435 @Override 436 public boolean isTitleTruncated() { 437 return mToolbar.isTitleTruncated(); 438 } 439 440 @Override 441 public void setCollapsible(boolean collapsible) { 442 mToolbar.setCollapsible(collapsible); 443 } 444 445 @Override 446 public void setHomeButtonEnabled(boolean enable) { 447 // Ignore 448 } 449 450 @Override 451 public int getNavigationMode() { 452 return mNavigationMode; 453 } 454 455 @Override 456 public void setNavigationMode(int mode) { 457 final int oldMode = mNavigationMode; 458 if (mode != oldMode) { 459 switch (oldMode) { 460 case ActionBar.NAVIGATION_MODE_LIST: 461 if (mSpinner != null && mSpinner.getParent() == mToolbar) { 462 mToolbar.removeView(mSpinner); 463 } 464 break; 465 case ActionBar.NAVIGATION_MODE_TABS: 466 if (mTabView != null && mTabView.getParent() == mToolbar) { 467 mToolbar.removeView(mTabView); 468 } 469 break; 470 } 471 472 mNavigationMode = mode; 473 474 switch (mode) { 475 case ActionBar.NAVIGATION_MODE_STANDARD: 476 break; 477 case ActionBar.NAVIGATION_MODE_LIST: 478 ensureSpinner(); 479 mToolbar.addView(mSpinner, 0); 480 break; 481 case ActionBar.NAVIGATION_MODE_TABS: 482 if (mTabView != null) { 483 mToolbar.addView(mTabView, 0); 484 Toolbar.LayoutParams lp = (Toolbar.LayoutParams) mTabView.getLayoutParams(); 485 lp.width = ViewGroup.LayoutParams.WRAP_CONTENT; 486 lp.height = ViewGroup.LayoutParams.WRAP_CONTENT; 487 lp.gravity = Gravity.START | Gravity.BOTTOM; 488 } 489 break; 490 default: 491 throw new IllegalArgumentException("Invalid navigation mode " + mode); 492 } 493 } 494 } 495 496 private void ensureSpinner() { 497 if (mSpinner == null) { 498 mSpinner = new SpinnerCompat(getContext(), null, R.attr.actionDropDownStyle); 499 Toolbar.LayoutParams lp = new Toolbar.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 500 ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.CENTER_VERTICAL); 501 mSpinner.setLayoutParams(lp); 502 } 503 } 504 505 @Override 506 public void setDropdownParams(SpinnerAdapter adapter, 507 AdapterViewCompat.OnItemSelectedListener listener) { 508 ensureSpinner(); 509 mSpinner.setAdapter(adapter); 510 mSpinner.setOnItemSelectedListener(listener); 511 } 512 513 @Override 514 public void setDropdownSelectedPosition(int position) { 515 if (mSpinner == null) { 516 throw new IllegalStateException( 517 "Can't set dropdown selected position without an adapter"); 518 } 519 mSpinner.setSelection(position); 520 } 521 522 @Override 523 public int getDropdownSelectedPosition() { 524 return mSpinner != null ? mSpinner.getSelectedItemPosition() : 0; 525 } 526 527 @Override 528 public int getDropdownItemCount() { 529 return mSpinner != null ? mSpinner.getCount() : 0; 530 } 531 532 @Override 533 public void setCustomView(View view) { 534 if (mCustomView != null && (mDisplayOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { 535 mToolbar.removeView(mCustomView); 536 } 537 mCustomView = view; 538 if (view != null && (mDisplayOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { 539 mToolbar.addView(mCustomView); 540 } 541 } 542 543 @Override 544 public View getCustomView() { 545 return mCustomView; 546 } 547 548 @Override 549 public void animateToVisibility(int visibility) { 550 if (visibility == View.GONE) { 551 ViewCompat.animate(mToolbar).translationY(mToolbar.getHeight()).alpha(0) 552 .setListener(new ViewPropertyAnimatorListenerAdapter() { 553 private boolean mCanceled = false; 554 @Override 555 public void onAnimationEnd(View view) { 556 if (!mCanceled) { 557 mToolbar.setVisibility(View.GONE); 558 } 559 } 560 561 @Override 562 public void onAnimationCancel(View view) { 563 mCanceled = true; 564 } 565 }); 566 } else if (visibility == View.VISIBLE) { 567 ViewCompat.animate(mToolbar).translationY(0).alpha(1) 568 .setListener(new ViewPropertyAnimatorListenerAdapter() { 569 @Override 570 public void onAnimationStart(View view) { 571 mToolbar.setVisibility(View.VISIBLE); 572 } 573 }); 574 } 575 } 576 577 @Override 578 public void setNavigationIcon(Drawable icon) { 579 mNavIcon = icon; 580 if ((mDisplayOpts & ActionBar.DISPLAY_HOME_AS_UP) != 0) { 581 mToolbar.setNavigationIcon(icon); 582 } 583 } 584 585 @Override 586 public void setNavigationIcon(int resId) { 587 setNavigationIcon(resId != 0 588 ? ContextCompat.getDrawable(mToolbar.getContext(), resId) 589 : null); 590 } 591 592 @Override 593 public void setNavigationContentDescription(CharSequence description) { 594 mToolbar.setNavigationContentDescription(description); 595 } 596 597 @Override 598 public void setNavigationContentDescription(int resId) { 599 mToolbar.setNavigationContentDescription(resId); 600 } 601 602 @Override 603 public void saveHierarchyState(SparseArray<Parcelable> toolbarStates) { 604 mToolbar.saveHierarchyState(toolbarStates); 605 } 606 607 @Override 608 public void restoreHierarchyState(SparseArray<Parcelable> toolbarStates) { 609 mToolbar.restoreHierarchyState(toolbarStates); 610 } 611 612}