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