ToolbarWidgetWrapper.java revision ca58f2ce7cd9f0e0f2c4a46c3bcc98013209f6b3
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 } else { 164 mDisplayOpts = detectDisplayOptions(); 165 } 166 167 if (TextUtils.isEmpty(mToolbar.getNavigationContentDescription())) { 168 mToolbar.setNavigationContentDescription( 169 getContext().getResources().getText(R.string.abc_action_bar_up_description)); 170 } 171 172 mToolbar.setNavigationOnClickListener(new View.OnClickListener() { 173 final ActionMenuItem mNavItem = new ActionMenuItem(mToolbar.getContext(), 174 0, android.R.id.home, 0, 0, mTitle); 175 @Override 176 public void onClick(View v) { 177 if (mWindowCallback != null && mMenuPrepared) { 178 mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mNavItem); 179 } 180 } 181 }); 182 } 183 184 private int detectDisplayOptions() { 185 int opts = ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_HOME | 186 ActionBar.DISPLAY_USE_LOGO; 187 if (mToolbar.getNavigationIcon() != null) { 188 opts |= ActionBar.DISPLAY_HOME_AS_UP; 189 } 190 return opts; 191 } 192 193 @Override 194 public ViewGroup getViewGroup() { 195 return mToolbar; 196 } 197 198 @Override 199 public Context getContext() { 200 return mToolbar.getContext(); 201 } 202 203 @Override 204 public boolean isSplit() { 205 return false; 206 } 207 208 @Override 209 public boolean hasExpandedActionView() { 210 return mToolbar.hasExpandedActionView(); 211 } 212 213 @Override 214 public void collapseActionView() { 215 mToolbar.collapseActionView(); 216 } 217 218 @Override 219 public void setWindowCallback(WindowCallback cb) { 220 mWindowCallback = cb; 221 } 222 223 @Override 224 public void setWindowTitle(CharSequence title) { 225 // "Real" title always trumps window title. 226 if (!mTitleSet) { 227 setTitleInt(title); 228 } 229 } 230 231 @Override 232 public CharSequence getTitle() { 233 return mToolbar.getTitle(); 234 } 235 236 @Override 237 public void setTitle(CharSequence title) { 238 mTitleSet = true; 239 setTitleInt(title); 240 } 241 242 private void setTitleInt(CharSequence title) { 243 mTitle = title; 244 if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 245 mToolbar.setTitle(title); 246 } 247 } 248 249 @Override 250 public CharSequence getSubtitle() { 251 return mToolbar.getSubtitle(); 252 } 253 254 @Override 255 public void setSubtitle(CharSequence subtitle) { 256 mSubtitle = subtitle; 257 if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 258 mToolbar.setSubtitle(subtitle); 259 } 260 } 261 262 @Override 263 public void initProgress() { 264 Log.i(TAG, "Progress display unsupported"); 265 } 266 267 @Override 268 public void initIndeterminateProgress() { 269 Log.i(TAG, "Progress display unsupported"); 270 } 271 272 @Override 273 public boolean canSplit() { 274 return false; 275 } 276 277 @Override 278 public void setSplitView(ViewGroup splitView) { 279 } 280 281 @Override 282 public void setSplitToolbar(boolean split) { 283 if (split) { 284 throw new UnsupportedOperationException("Cannot split an android.widget.Toolbar"); 285 } 286 } 287 288 @Override 289 public void setSplitWhenNarrow(boolean splitWhenNarrow) { 290 // Ignore. 291 } 292 293 @Override 294 public boolean hasIcon() { 295 return mIcon != null; 296 } 297 298 @Override 299 public boolean hasLogo() { 300 return mLogo != null; 301 } 302 303 @Override 304 public void setIcon(int resId) { 305 setIcon(resId != 0 ? ContextCompat.getDrawable(getContext(), resId) : null); 306 } 307 308 @Override 309 public void setIcon(Drawable d) { 310 mIcon = d; 311 updateToolbarLogo(); 312 } 313 314 @Override 315 public void setLogo(int resId) { 316 setLogo(resId != 0 ? ContextCompat.getDrawable(getContext(), resId) : null); 317 } 318 319 @Override 320 public void setLogo(Drawable d) { 321 mLogo = d; 322 updateToolbarLogo(); 323 } 324 325 private void updateToolbarLogo() { 326 Drawable logo = null; 327 if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_HOME) != 0) { 328 if ((mDisplayOpts & ActionBar.DISPLAY_USE_LOGO) != 0) { 329 logo = mLogo != null ? mLogo : mIcon; 330 } else { 331 logo = mIcon; 332 } 333 } 334 mToolbar.setLogo(logo); 335 } 336 337 @Override 338 public boolean canShowOverflowMenu() { 339 return mToolbar.canShowOverflowMenu(); 340 } 341 342 @Override 343 public boolean isOverflowMenuShowing() { 344 return mToolbar.isOverflowMenuShowing(); 345 } 346 347 @Override 348 public boolean isOverflowMenuShowPending() { 349 return mToolbar.isOverflowMenuShowPending(); 350 } 351 352 @Override 353 public boolean showOverflowMenu() { 354 return mToolbar.showOverflowMenu(); 355 } 356 357 @Override 358 public boolean hideOverflowMenu() { 359 return mToolbar.hideOverflowMenu(); 360 } 361 362 @Override 363 public void setMenuPrepared() { 364 mMenuPrepared = true; 365 } 366 367 @Override 368 public void setMenu(Menu menu, MenuPresenter.Callback cb) { 369 if (mActionMenuPresenter == null) { 370 mActionMenuPresenter = new ActionMenuPresenter(mToolbar.getContext()); 371 mActionMenuPresenter.setId(R.id.action_menu_presenter); 372 } 373 mActionMenuPresenter.setCallback(cb); 374 mToolbar.setMenu((MenuBuilder) menu, mActionMenuPresenter); 375 } 376 377 @Override 378 public void dismissPopupMenus() { 379 mToolbar.dismissPopupMenus(); 380 } 381 382 @Override 383 public int getDisplayOptions() { 384 return mDisplayOpts; 385 } 386 387 @Override 388 public void setDisplayOptions(int newOpts) { 389 final int oldOpts = mDisplayOpts; 390 final int changed = oldOpts ^ newOpts; 391 mDisplayOpts = newOpts; 392 if (changed != 0) { 393 if ((changed & ActionBar.DISPLAY_HOME_AS_UP) != 0) { 394 if ((newOpts & ActionBar.DISPLAY_HOME_AS_UP) != 0) { 395 mToolbar.setNavigationIcon(mNavIcon); 396 } else { 397 mToolbar.setNavigationIcon(null); 398 } 399 } 400 401 if ((changed & AFFECTS_LOGO_MASK) != 0) { 402 updateToolbarLogo(); 403 } 404 405 if ((changed & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 406 if ((newOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 407 mToolbar.setTitle(mTitle); 408 mToolbar.setSubtitle(mSubtitle); 409 } else { 410 mToolbar.setTitle(null); 411 mToolbar.setSubtitle(null); 412 } 413 } 414 415 if ((changed & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomView != null) { 416 if ((newOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { 417 mToolbar.addView(mCustomView); 418 } else { 419 mToolbar.removeView(mCustomView); 420 } 421 } 422 } 423 } 424 425 @Override 426 public void setEmbeddedTabView(ScrollingTabContainerView tabView) { 427 if (mTabView != null && mTabView.getParent() == mToolbar) { 428 mToolbar.removeView(mTabView); 429 } 430 mTabView = tabView; 431 if (tabView != null && mNavigationMode == ActionBar.NAVIGATION_MODE_TABS) { 432 mToolbar.addView(mTabView, 0); 433 Toolbar.LayoutParams lp = (Toolbar.LayoutParams) mTabView.getLayoutParams(); 434 lp.width = ViewGroup.LayoutParams.WRAP_CONTENT; 435 lp.height = ViewGroup.LayoutParams.WRAP_CONTENT; 436 lp.gravity = Gravity.START | Gravity.BOTTOM; 437 tabView.setAllowCollapse(true); 438 } 439 } 440 441 @Override 442 public boolean hasEmbeddedTabs() { 443 return mTabView != null; 444 } 445 446 @Override 447 public boolean isTitleTruncated() { 448 return mToolbar.isTitleTruncated(); 449 } 450 451 @Override 452 public void setCollapsible(boolean collapsible) { 453 mToolbar.setCollapsible(collapsible); 454 } 455 456 @Override 457 public void setHomeButtonEnabled(boolean enable) { 458 // Ignore 459 } 460 461 @Override 462 public int getNavigationMode() { 463 return mNavigationMode; 464 } 465 466 @Override 467 public void setNavigationMode(int mode) { 468 final int oldMode = mNavigationMode; 469 if (mode != oldMode) { 470 switch (oldMode) { 471 case ActionBar.NAVIGATION_MODE_LIST: 472 if (mSpinner != null && mSpinner.getParent() == mToolbar) { 473 mToolbar.removeView(mSpinner); 474 } 475 break; 476 case ActionBar.NAVIGATION_MODE_TABS: 477 if (mTabView != null && mTabView.getParent() == mToolbar) { 478 mToolbar.removeView(mTabView); 479 } 480 break; 481 } 482 483 mNavigationMode = mode; 484 485 switch (mode) { 486 case ActionBar.NAVIGATION_MODE_STANDARD: 487 break; 488 case ActionBar.NAVIGATION_MODE_LIST: 489 ensureSpinner(); 490 mToolbar.addView(mSpinner, 0); 491 break; 492 case ActionBar.NAVIGATION_MODE_TABS: 493 if (mTabView != null) { 494 mToolbar.addView(mTabView, 0); 495 Toolbar.LayoutParams lp = (Toolbar.LayoutParams) mTabView.getLayoutParams(); 496 lp.width = ViewGroup.LayoutParams.WRAP_CONTENT; 497 lp.height = ViewGroup.LayoutParams.WRAP_CONTENT; 498 lp.gravity = Gravity.START | Gravity.BOTTOM; 499 } 500 break; 501 default: 502 throw new IllegalArgumentException("Invalid navigation mode " + mode); 503 } 504 } 505 } 506 507 private void ensureSpinner() { 508 if (mSpinner == null) { 509 mSpinner = new SpinnerCompat(getContext(), null, R.attr.actionDropDownStyle); 510 Toolbar.LayoutParams lp = new Toolbar.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 511 ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.CENTER_VERTICAL); 512 mSpinner.setLayoutParams(lp); 513 } 514 } 515 516 @Override 517 public void setDropdownParams(SpinnerAdapter adapter, 518 AdapterViewCompat.OnItemSelectedListener listener) { 519 ensureSpinner(); 520 mSpinner.setAdapter(adapter); 521 mSpinner.setOnItemSelectedListener(listener); 522 } 523 524 @Override 525 public void setDropdownSelectedPosition(int position) { 526 if (mSpinner == null) { 527 throw new IllegalStateException( 528 "Can't set dropdown selected position without an adapter"); 529 } 530 mSpinner.setSelection(position); 531 } 532 533 @Override 534 public int getDropdownSelectedPosition() { 535 return mSpinner != null ? mSpinner.getSelectedItemPosition() : 0; 536 } 537 538 @Override 539 public int getDropdownItemCount() { 540 return mSpinner != null ? mSpinner.getCount() : 0; 541 } 542 543 @Override 544 public void setCustomView(View view) { 545 if (mCustomView != null && (mDisplayOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { 546 mToolbar.removeView(mCustomView); 547 } 548 mCustomView = view; 549 if (view != null && (mDisplayOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { 550 mToolbar.addView(mCustomView); 551 } 552 } 553 554 @Override 555 public View getCustomView() { 556 return mCustomView; 557 } 558 559 @Override 560 public void animateToVisibility(int visibility) { 561 if (visibility == View.GONE) { 562 ViewCompat.animate(mToolbar).translationY(mToolbar.getHeight()).alpha(0) 563 .setListener(new ViewPropertyAnimatorListenerAdapter() { 564 private boolean mCanceled = false; 565 @Override 566 public void onAnimationEnd(View view) { 567 if (!mCanceled) { 568 mToolbar.setVisibility(View.GONE); 569 } 570 } 571 572 @Override 573 public void onAnimationCancel(View view) { 574 mCanceled = true; 575 } 576 }); 577 } else if (visibility == View.VISIBLE) { 578 ViewCompat.animate(mToolbar).translationY(0).alpha(1) 579 .setListener(new ViewPropertyAnimatorListenerAdapter() { 580 @Override 581 public void onAnimationStart(View view) { 582 mToolbar.setVisibility(View.VISIBLE); 583 } 584 }); 585 } 586 } 587 588 @Override 589 public void setNavigationIcon(Drawable icon) { 590 mNavIcon = icon; 591 if ((mDisplayOpts & ActionBar.DISPLAY_HOME_AS_UP) != 0) { 592 mToolbar.setNavigationIcon(icon); 593 } 594 } 595 596 @Override 597 public void setNavigationIcon(int resId) { 598 setNavigationIcon(resId != 0 599 ? ContextCompat.getDrawable(mToolbar.getContext(), resId) 600 : null); 601 } 602 603 @Override 604 public void setNavigationContentDescription(CharSequence description) { 605 mToolbar.setNavigationContentDescription(description); 606 } 607 608 @Override 609 public void setNavigationContentDescription(int resId) { 610 mToolbar.setNavigationContentDescription(resId); 611 } 612 613 @Override 614 public void saveHierarchyState(SparseArray<Parcelable> toolbarStates) { 615 mToolbar.saveHierarchyState(toolbarStates); 616 } 617 618 @Override 619 public void restoreHierarchyState(SparseArray<Parcelable> toolbarStates) { 620 mToolbar.restoreHierarchyState(toolbarStates); 621 } 622 623}