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