ActionBarOverlayLayout.java revision f3ad1351d8b40ec5defe35e79d3430ad3c384b6d
1/* 2 * Copyright (C) 2012 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 android.support.v7.internal.widget; 18 19import android.content.Context; 20import android.content.res.Configuration; 21import android.content.res.TypedArray; 22import android.graphics.Canvas; 23import android.graphics.Rect; 24import android.graphics.drawable.Drawable; 25import android.os.Build; 26import android.os.Parcelable; 27import android.support.v4.view.ViewCompat; 28import android.support.v4.view.ViewPropertyAnimatorCompat; 29import android.support.v4.view.ViewPropertyAnimatorListener; 30import android.support.v4.view.ViewPropertyAnimatorListenerAdapter; 31import android.support.v4.widget.ScrollerCompat; 32import android.support.v7.appcompat.R; 33import android.support.v7.internal.VersionUtils; 34import android.support.v7.internal.view.menu.MenuPresenter; 35import android.support.v7.widget.Toolbar; 36import android.util.AttributeSet; 37import android.util.SparseArray; 38import android.view.Menu; 39import android.view.View; 40import android.view.ViewGroup; 41import android.view.Window; 42 43/** 44 * Special layout for the containing of an overlay action bar (and its content) to correctly handle 45 * fitting system windows when the content has request that its layout ignore them. 46 * 47 * @hide 48 */ 49public class ActionBarOverlayLayout extends ViewGroup implements DecorContentParent { 50 private static final String TAG = "ActionBarOverlayLayout"; 51 52 private int mActionBarHeight; 53 //private WindowDecorActionBar mActionBar; 54 private int mWindowVisibility = View.VISIBLE; 55 56 // The main UI elements that we handle the layout of. 57 private ContentFrameLayout mContent; 58 private ActionBarContainer mActionBarBottom; 59 private ActionBarContainer mActionBarTop; 60 61 // Some interior UI elements. 62 private DecorToolbar mDecorToolbar; 63 64 // Content overlay drawable - generally the action bar's shadow 65 private Drawable mWindowContentOverlay; 66 private boolean mIgnoreWindowContentOverlay; 67 68 private boolean mOverlayMode; 69 private boolean mHasNonEmbeddedTabs; 70 private boolean mHideOnContentScroll; 71 private boolean mAnimatingForFling; 72 private int mHideOnContentScrollReference; 73 private int mLastSystemUiVisibility; 74 private final Rect mBaseContentInsets = new Rect(); 75 private final Rect mLastBaseContentInsets = new Rect(); 76 private final Rect mContentInsets = new Rect(); 77 private final Rect mBaseInnerInsets = new Rect(); 78 private final Rect mInnerInsets = new Rect(); 79 private final Rect mLastInnerInsets = new Rect(); 80 81 private ActionBarVisibilityCallback mActionBarVisibilityCallback; 82 83 private final int ACTION_BAR_ANIMATE_DELAY = 600; // ms 84 85 private ScrollerCompat mFlingEstimator; 86 87 private ViewPropertyAnimatorCompat mCurrentActionBarTopAnimator; 88 private ViewPropertyAnimatorCompat mCurrentActionBarBottomAnimator; 89 90 private final ViewPropertyAnimatorListener mTopAnimatorListener 91 = new ViewPropertyAnimatorListenerAdapter() { 92 @Override 93 public void onAnimationEnd(View view) { 94 mCurrentActionBarTopAnimator = null; 95 mAnimatingForFling = false; 96 } 97 98 @Override 99 public void onAnimationCancel(View view) { 100 mCurrentActionBarTopAnimator = null; 101 mAnimatingForFling = false; 102 } 103 }; 104 105 private final ViewPropertyAnimatorListener mBottomAnimatorListener = 106 new ViewPropertyAnimatorListenerAdapter() { 107 @Override 108 public void onAnimationEnd(View view) { 109 mCurrentActionBarBottomAnimator = null; 110 mAnimatingForFling = false; 111 } 112 113 @Override 114 public void onAnimationCancel(View view) { 115 mCurrentActionBarBottomAnimator = null; 116 mAnimatingForFling = false; 117 } 118 }; 119 120 private final Runnable mRemoveActionBarHideOffset = new Runnable() { 121 public void run() { 122 haltActionBarHideOffsetAnimations(); 123 mCurrentActionBarTopAnimator = ViewCompat.animate(mActionBarTop).translationY(0) 124 .setListener(mTopAnimatorListener); 125 if (mActionBarBottom != null && mActionBarBottom.getVisibility() != GONE) { 126 mCurrentActionBarBottomAnimator = ViewCompat.animate(mActionBarBottom).translationY(0) 127 .setListener(mBottomAnimatorListener); 128 } 129 } 130 }; 131 132 private final Runnable mAddActionBarHideOffset = new Runnable() { 133 public void run() { 134 haltActionBarHideOffsetAnimations(); 135 mCurrentActionBarTopAnimator = ViewCompat.animate(mActionBarTop) 136 .translationY(-mActionBarTop.getHeight()) 137 .setListener(mTopAnimatorListener); 138 if (mActionBarBottom != null && mActionBarBottom.getVisibility() != GONE) { 139 mCurrentActionBarBottomAnimator = ViewCompat.animate(mActionBarBottom) 140 .translationY(mActionBarBottom.getHeight()) 141 .setListener(mBottomAnimatorListener); 142 } 143 } 144 }; 145 146// public static final Property<ActionBarOverlayLayout, Integer> ACTION_BAR_HIDE_OFFSET = 147// new IntProperty<ActionBarOverlayLayout>("actionBarHideOffset") { 148// 149// @Override 150// public void setValue(ActionBarOverlayLayout object, int value) { 151// object.setActionBarHideOffset(value); 152// } 153// 154// @Override 155// public Integer get(ActionBarOverlayLayout object) { 156// return object.getActionBarHideOffset(); 157// } 158// }; 159 160 static final int[] ATTRS = new int [] { 161 R.attr.actionBarSize, 162 android.R.attr.windowContentOverlay 163 }; 164 165 public ActionBarOverlayLayout(Context context) { 166 super(context); 167 init(context); 168 } 169 170 public ActionBarOverlayLayout(Context context, AttributeSet attrs) { 171 super(context, attrs); 172 init(context); 173 } 174 175 private void init(Context context) { 176 TypedArray ta = getContext().getTheme().obtainStyledAttributes(ATTRS); 177 mActionBarHeight = ta.getDimensionPixelSize(0, 0); 178 mWindowContentOverlay = ta.getDrawable(1); 179 setWillNotDraw(mWindowContentOverlay == null); 180 ta.recycle(); 181 182 mIgnoreWindowContentOverlay = context.getApplicationInfo().targetSdkVersion < 183 Build.VERSION_CODES.KITKAT; 184 185 mFlingEstimator = ScrollerCompat.create(context); 186 } 187 188 @Override 189 protected void onDetachedFromWindow() { 190 super.onDetachedFromWindow(); 191 haltActionBarHideOffsetAnimations(); 192 } 193 194 public void setActionBarVisibilityCallback(ActionBarVisibilityCallback cb) { 195 mActionBarVisibilityCallback = cb; 196 if (getWindowToken() != null) { 197 // This is being initialized after being added to a window; 198 // make sure to update all state now. 199 mActionBarVisibilityCallback.onWindowVisibilityChanged(mWindowVisibility); 200 if (mLastSystemUiVisibility != 0) { 201 int newVis = mLastSystemUiVisibility; 202 onWindowSystemUiVisibilityChanged(newVis); 203 ViewCompat.requestApplyInsets(this); 204 } 205 } 206 } 207 208 public void setOverlayMode(boolean overlayMode) { 209 mOverlayMode = overlayMode; 210 211 /* 212 * Drawing the window content overlay was broken before K so starting to draw it 213 * again unexpectedly will cause artifacts in some apps. They should fix it. 214 */ 215 mIgnoreWindowContentOverlay = overlayMode && 216 getContext().getApplicationInfo().targetSdkVersion < 217 Build.VERSION_CODES.KITKAT; 218 } 219 220 public boolean isInOverlayMode() { 221 return mOverlayMode; 222 } 223 224 public void setHasNonEmbeddedTabs(boolean hasNonEmbeddedTabs) { 225 mHasNonEmbeddedTabs = hasNonEmbeddedTabs; 226 } 227 228 public void setShowingForActionMode(boolean showing) { 229 // TODO: Add workaround for this 230// if (showing) { 231// // Here's a fun hack: if the status bar is currently being hidden, 232// // and the application has asked for stable content insets, then 233// // we will end up with the action mode action bar being shown 234// // without the status bar, but moved below where the status bar 235// // would be. Not nice. Trying to have this be positioned 236// // correctly is not easy (basically we need yet *another* content 237// // inset from the window manager to know where to put it), so 238// // instead we will just temporarily force the status bar to be shown. 239// if ((getWindowSystemUiVisibility() & (SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 240// | SYSTEM_UI_FLAG_LAYOUT_STABLE)) 241// == (SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | SYSTEM_UI_FLAG_LAYOUT_STABLE)) { 242// setDisabledSystemUiVisibility(SYSTEM_UI_FLAG_FULLSCREEN); 243// } 244// } else { 245// setDisabledSystemUiVisibility(0); 246// } 247 } 248 249 protected void onConfigurationChanged(Configuration newConfig) { 250 if (Build.VERSION.SDK_INT >= 8) { 251 super.onConfigurationChanged(newConfig); 252 } 253 init(getContext()); 254 ViewCompat.requestApplyInsets(this); 255 } 256 257 public void onWindowSystemUiVisibilityChanged(int visible) { 258 if (Build.VERSION.SDK_INT >= 16) { 259 super.onWindowSystemUiVisibilityChanged(visible); 260 } 261 pullChildren(); 262 final int diff = mLastSystemUiVisibility ^ visible; 263 mLastSystemUiVisibility = visible; 264 final boolean barVisible = (visible & SYSTEM_UI_FLAG_FULLSCREEN) == 0; 265 final boolean stable = (visible & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0; 266 if (mActionBarVisibilityCallback != null) { 267 // We want the bar to be visible if it is not being hidden, 268 // or the app has not turned on a stable UI mode (meaning they 269 // are performing explicit layout around the action bar). 270 mActionBarVisibilityCallback.enableContentAnimations(!stable); 271 if (barVisible || !stable) mActionBarVisibilityCallback.showForSystem(); 272 else mActionBarVisibilityCallback.hideForSystem(); 273 } 274 if ((diff & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) { 275 if (mActionBarVisibilityCallback != null) { 276 ViewCompat.requestApplyInsets(this); 277 } 278 } 279 } 280 281 @Override 282 protected void onWindowVisibilityChanged(int visibility) { 283 super.onWindowVisibilityChanged(visibility); 284 mWindowVisibility = visibility; 285 if (mActionBarVisibilityCallback != null) { 286 mActionBarVisibilityCallback.onWindowVisibilityChanged(visibility); 287 } 288 } 289 290 private boolean applyInsets(View view, Rect insets, boolean left, boolean top, 291 boolean bottom, boolean right) { 292 boolean changed = false; 293 LayoutParams lp = (LayoutParams)view.getLayoutParams(); 294 if (left && lp.leftMargin != insets.left) { 295 changed = true; 296 lp.leftMargin = insets.left; 297 } 298 if (top && lp.topMargin != insets.top) { 299 changed = true; 300 lp.topMargin = insets.top; 301 } 302 if (right && lp.rightMargin != insets.right) { 303 changed = true; 304 lp.rightMargin = insets.right; 305 } 306 if (bottom && lp.bottomMargin != insets.bottom) { 307 changed = true; 308 lp.bottomMargin = insets.bottom; 309 } 310 return changed; 311 } 312 313 @Override 314 protected boolean fitSystemWindows(Rect insets) { 315 pullChildren(); 316 317 final int vis = ViewCompat.getWindowSystemUiVisibility(this); 318 final boolean stable = (vis & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0; 319 final Rect systemInsets = insets; 320 321 // Since we're not the top level view in the window decor, we do not need to 322 // inset the Action Bars 323 324 boolean changed = false; 325 mBaseInnerInsets.set(systemInsets); 326 ViewUtils.computeFitSystemWindows(this, mBaseInnerInsets, mBaseContentInsets); 327 if (!mLastBaseContentInsets.equals(mBaseContentInsets)) { 328 changed = true; 329 mLastBaseContentInsets.set(mBaseContentInsets); 330 } 331 332 if (changed) { 333 requestLayout(); 334 } 335 336 // We don't do any more at this point. To correctly compute the content/inner 337 // insets in all cases, we need to know the measured size of the various action 338 // bar elements. fitSystemWindows() happens before the measure pass, so we can't 339 // do that here. Instead we will take this up in onMeasure(). 340 return true; 341 } 342 343 @Override 344 protected LayoutParams generateDefaultLayoutParams() { 345 return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 346 } 347 348 @Override 349 public LayoutParams generateLayoutParams(AttributeSet attrs) { 350 return new LayoutParams(getContext(), attrs); 351 } 352 353 @Override 354 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 355 return new LayoutParams(p); 356 } 357 358 @Override 359 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 360 return p instanceof LayoutParams; 361 } 362 363 @Override 364 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 365 pullChildren(); 366 367 int maxHeight = 0; 368 int maxWidth = 0; 369 int childState = 0; 370 371 int topInset = 0; 372 int bottomInset = 0; 373 374 measureChildWithMargins(mActionBarTop, widthMeasureSpec, 0, heightMeasureSpec, 0); 375 LayoutParams lp = (LayoutParams) mActionBarTop.getLayoutParams(); 376 maxWidth = Math.max(maxWidth, 377 mActionBarTop.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); 378 maxHeight = Math.max(maxHeight, 379 mActionBarTop.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); 380 childState = ViewUtils.combineMeasuredStates(childState, 381 ViewCompat.getMeasuredState(mActionBarTop)); 382 383 // xlarge screen layout doesn't have bottom action bar. 384 if (mActionBarBottom != null) { 385 measureChildWithMargins(mActionBarBottom, widthMeasureSpec, 0, heightMeasureSpec, 0); 386 lp = (LayoutParams) mActionBarBottom.getLayoutParams(); 387 maxWidth = Math.max(maxWidth, 388 mActionBarBottom.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); 389 maxHeight = Math.max(maxHeight, 390 mActionBarBottom.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); 391 childState = ViewUtils.combineMeasuredStates(childState, 392 ViewCompat.getMeasuredState(mActionBarBottom)); 393 } 394 395 final int vis = ViewCompat.getWindowSystemUiVisibility(this); 396 final boolean stable = (vis & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0; 397 398 if (stable) { 399 // This is the standard space needed for the action bar. For stable measurement, 400 // we can't depend on the size currently reported by it -- this must remain constant. 401 topInset = mActionBarHeight; 402 if (mHasNonEmbeddedTabs) { 403 final View tabs = mActionBarTop.getTabContainer(); 404 if (tabs != null) { 405 // If tabs are not embedded, increase space on top to account for them. 406 topInset += mActionBarHeight; 407 } 408 } 409 } else if (mActionBarTop.getVisibility() != GONE) { 410 // This is the space needed on top of the window for all of the action bar 411 // and tabs. 412 topInset = mActionBarTop.getMeasuredHeight(); 413 } 414 415 if (mDecorToolbar.isSplit()) { 416 // If action bar is split, adjust bottom insets for it. 417 if (mActionBarBottom != null) { 418 if (stable) { 419 bottomInset = mActionBarHeight; 420 } else { 421 bottomInset = mActionBarBottom.getMeasuredHeight(); 422 } 423 } 424 } 425 426 // If the window has not requested system UI layout flags, we need to 427 // make sure its content is not being covered by system UI... though it 428 // will still be covered by the action bar if they have requested it to 429 // overlay. 430 mContentInsets.set(mBaseContentInsets); 431 mInnerInsets.set(mBaseInnerInsets); 432 if (!mOverlayMode && !stable) { 433 mContentInsets.top += topInset; 434 mContentInsets.bottom += bottomInset; 435 } else { 436 mInnerInsets.top += topInset; 437 mInnerInsets.bottom += bottomInset; 438 } 439 applyInsets(mContent, mContentInsets, true, true, true, true); 440 441 if (!mLastInnerInsets.equals(mInnerInsets)) { 442 // If the inner insets have changed, we need to dispatch this down to 443 // the app's fitSystemWindows(). We do this before measuring the content 444 // view to keep the same semantics as the normal fitSystemWindows() call. 445 mLastInnerInsets.set(mInnerInsets); 446 447 mContent.dispatchFitSystemWindows(mInnerInsets); 448 } 449 450 measureChildWithMargins(mContent, widthMeasureSpec, 0, heightMeasureSpec, 0); 451 lp = (LayoutParams) mContent.getLayoutParams(); 452 maxWidth = Math.max(maxWidth, 453 mContent.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); 454 maxHeight = Math.max(maxHeight, 455 mContent.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); 456 childState = ViewUtils.combineMeasuredStates(childState, 457 ViewCompat.getMeasuredState(mContent)); 458 459 // Account for padding too 460 maxWidth += getPaddingLeft() + getPaddingRight(); 461 maxHeight += getPaddingTop() + getPaddingBottom(); 462 463 // Check against our minimum height and width 464 maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); 465 maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); 466 467 setMeasuredDimension( 468 ViewCompat.resolveSizeAndState(maxWidth, widthMeasureSpec, childState), 469 ViewCompat.resolveSizeAndState(maxHeight, heightMeasureSpec, 470 childState << MEASURED_HEIGHT_STATE_SHIFT)); 471 } 472 473 @Override 474 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 475 final int count = getChildCount(); 476 477 final int parentLeft = getPaddingLeft(); 478 final int parentRight = right - left - getPaddingRight(); 479 480 final int parentTop = getPaddingTop(); 481 final int parentBottom = bottom - top - getPaddingBottom(); 482 483 for (int i = 0; i < count; i++) { 484 final View child = getChildAt(i); 485 if (child.getVisibility() != GONE) { 486 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 487 488 final int width = child.getMeasuredWidth(); 489 final int height = child.getMeasuredHeight(); 490 491 int childLeft = parentLeft + lp.leftMargin; 492 int childTop; 493 if (child == mActionBarBottom) { 494 childTop = parentBottom - height - lp.bottomMargin; 495 } else { 496 childTop = parentTop + lp.topMargin; 497 } 498 499 child.layout(childLeft, childTop, childLeft + width, childTop + height); 500 } 501 } 502 } 503 504 @Override 505 public void draw(Canvas c) { 506 super.draw(c); 507 if (mWindowContentOverlay != null && !mIgnoreWindowContentOverlay) { 508 final int top = mActionBarTop.getVisibility() == VISIBLE ? 509 (int) (mActionBarTop.getBottom() + ViewCompat.getTranslationY(mActionBarTop) + 0.5f) 510 : 0; 511 mWindowContentOverlay.setBounds(0, top, getWidth(), 512 top + mWindowContentOverlay.getIntrinsicHeight()); 513 mWindowContentOverlay.draw(c); 514 } 515 } 516 517 @Override 518 public boolean shouldDelayChildPressedState() { 519 return false; 520 } 521 522 @Override 523 public boolean onStartNestedScroll(View child, View target, int axes) { 524 if ((axes & SCROLL_AXIS_VERTICAL) == 0 || mActionBarTop.getVisibility() != VISIBLE) { 525 return false; 526 } 527 return mHideOnContentScroll; 528 } 529 530 @Override 531 public void onNestedScrollAccepted(View child, View target, int axes) { 532 super.onNestedScrollAccepted(child, target, axes); 533 mHideOnContentScrollReference = getActionBarHideOffset(); 534 haltActionBarHideOffsetAnimations(); 535 if (mActionBarVisibilityCallback != null) { 536 mActionBarVisibilityCallback.onContentScrollStarted(); 537 } 538 } 539 540 @Override 541 public void onNestedScroll(View target, int dxConsumed, int dyConsumed, 542 int dxUnconsumed, int dyUnconsumed) { 543 mHideOnContentScrollReference += dyConsumed; 544 setActionBarHideOffset(mHideOnContentScrollReference); 545 } 546 547 @Override 548 public void onStopNestedScroll(View target) { 549 super.onStopNestedScroll(target); 550 if (mHideOnContentScroll && !mAnimatingForFling) { 551 if (mHideOnContentScrollReference <= mActionBarTop.getHeight()) { 552 postRemoveActionBarHideOffset(); 553 } else { 554 postAddActionBarHideOffset(); 555 } 556 } 557 if (mActionBarVisibilityCallback != null) { 558 mActionBarVisibilityCallback.onContentScrollStopped(); 559 } 560 } 561 562 @Override 563 public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) { 564 if (!mHideOnContentScroll || !consumed) { 565 return false; 566 } 567 if (shouldHideActionBarOnFling(velocityX, velocityY)) { 568 addActionBarHideOffset(); 569 } else { 570 removeActionBarHideOffset(); 571 } 572 mAnimatingForFling = true; 573 return true; 574 } 575 576 void pullChildren() { 577 if (mContent == null) { 578 mContent = (ContentFrameLayout) findViewById(R.id.action_bar_activity_content); 579 mActionBarTop = (ActionBarContainer) findViewById(R.id.action_bar_container); 580 mDecorToolbar = getDecorToolbar(findViewById(R.id.action_bar)); 581 mActionBarBottom = (ActionBarContainer) findViewById(R.id.split_action_bar); 582 } 583 } 584 585 private DecorToolbar getDecorToolbar(View view) { 586 if (view instanceof DecorToolbar) { 587 return (DecorToolbar) view; 588 } else if (view instanceof Toolbar) { 589 return ((Toolbar) view).getWrapper(); 590 } else { 591 throw new IllegalStateException("Can't make a decor toolbar out of " + 592 view.getClass().getSimpleName()); 593 } 594 } 595 596 public void setHideOnContentScrollEnabled(boolean hideOnContentScroll) { 597 if (hideOnContentScroll != mHideOnContentScroll) { 598 mHideOnContentScroll = hideOnContentScroll; 599 if (!hideOnContentScroll) { 600 if (VersionUtils.isAtLeastL()) { 601 stopNestedScroll(); 602 } 603 haltActionBarHideOffsetAnimations(); 604 setActionBarHideOffset(0); 605 } 606 } 607 } 608 609 public boolean isHideOnContentScrollEnabled() { 610 return mHideOnContentScroll; 611 } 612 613 public int getActionBarHideOffset() { 614 return mActionBarTop != null ? -((int) ViewCompat.getTranslationY(mActionBarTop)) : 0; 615 } 616 617 public void setActionBarHideOffset(int offset) { 618 haltActionBarHideOffsetAnimations(); 619 final int topHeight = mActionBarTop.getHeight(); 620 offset = Math.max(0, Math.min(offset, topHeight)); 621 ViewCompat.setTranslationY(mActionBarTop, -offset); 622 if (mActionBarBottom != null && mActionBarBottom.getVisibility() != GONE) { 623 // Match the hide offset proportionally for a split bar 624 final float fOffset = (float) offset / topHeight; 625 final int bOffset = (int) (mActionBarBottom.getHeight() * fOffset); 626 ViewCompat.setTranslationY(mActionBarBottom, bOffset); 627 } 628 } 629 630 private void haltActionBarHideOffsetAnimations() { 631 removeCallbacks(mRemoveActionBarHideOffset); 632 removeCallbacks(mAddActionBarHideOffset); 633 if (mCurrentActionBarTopAnimator != null) { 634 mCurrentActionBarTopAnimator.cancel(); 635 } 636 if (mCurrentActionBarBottomAnimator != null) { 637 mCurrentActionBarBottomAnimator.cancel(); 638 } 639 } 640 641 private void postRemoveActionBarHideOffset() { 642 haltActionBarHideOffsetAnimations(); 643 postDelayed(mRemoveActionBarHideOffset, ACTION_BAR_ANIMATE_DELAY); 644 } 645 646 private void postAddActionBarHideOffset() { 647 haltActionBarHideOffsetAnimations(); 648 postDelayed(mAddActionBarHideOffset, ACTION_BAR_ANIMATE_DELAY); 649 } 650 651 private void removeActionBarHideOffset() { 652 haltActionBarHideOffsetAnimations(); 653 mRemoveActionBarHideOffset.run(); 654 } 655 656 private void addActionBarHideOffset() { 657 haltActionBarHideOffsetAnimations(); 658 mAddActionBarHideOffset.run(); 659 } 660 661 private boolean shouldHideActionBarOnFling(float velocityX, float velocityY) { 662 mFlingEstimator.fling(0, 0, 0, (int) velocityY, 0, 0, Integer.MIN_VALUE, Integer.MAX_VALUE); 663 final int finalY = mFlingEstimator.getFinalY(); 664 return finalY > mActionBarTop.getHeight(); 665 } 666 667 @Override 668 public void setWindowCallback(Window.Callback cb) { 669 pullChildren(); 670 mDecorToolbar.setWindowCallback(cb); 671 } 672 673 @Override 674 public void setWindowTitle(CharSequence title) { 675 pullChildren(); 676 mDecorToolbar.setWindowTitle(title); 677 } 678 679 @Override 680 public CharSequence getTitle() { 681 pullChildren(); 682 return mDecorToolbar.getTitle(); 683 } 684 685 @Override 686 public void initFeature(int windowFeature) { 687 pullChildren(); 688 switch (windowFeature) { 689 case Window.FEATURE_PROGRESS: 690 mDecorToolbar.initProgress(); 691 break; 692 case Window.FEATURE_INDETERMINATE_PROGRESS: 693 mDecorToolbar.initIndeterminateProgress(); 694 break; 695 case Window.FEATURE_ACTION_BAR_OVERLAY: 696 setOverlayMode(true); 697 break; 698 } 699 } 700 701 @Override 702 public void setUiOptions(int uiOptions) { 703 // Split Action Bar not included. 704 } 705 706 @Override 707 public boolean hasIcon() { 708 pullChildren(); 709 return mDecorToolbar.hasIcon(); 710 } 711 712 @Override 713 public boolean hasLogo() { 714 pullChildren(); 715 return mDecorToolbar.hasLogo(); 716 } 717 718 @Override 719 public void setIcon(int resId) { 720 pullChildren(); 721 mDecorToolbar.setIcon(resId); 722 } 723 724 @Override 725 public void setIcon(Drawable d) { 726 pullChildren(); 727 mDecorToolbar.setIcon(d); 728 } 729 730 @Override 731 public void setLogo(int resId) { 732 pullChildren(); 733 mDecorToolbar.setLogo(resId); 734 } 735 736 @Override 737 public boolean canShowOverflowMenu() { 738 pullChildren(); 739 return mDecorToolbar.canShowOverflowMenu(); 740 } 741 742 @Override 743 public boolean isOverflowMenuShowing() { 744 pullChildren(); 745 return mDecorToolbar.isOverflowMenuShowing(); 746 } 747 748 @Override 749 public boolean isOverflowMenuShowPending() { 750 pullChildren(); 751 return mDecorToolbar.isOverflowMenuShowPending(); 752 } 753 754 @Override 755 public boolean showOverflowMenu() { 756 pullChildren(); 757 return mDecorToolbar.showOverflowMenu(); 758 } 759 760 @Override 761 public boolean hideOverflowMenu() { 762 pullChildren(); 763 return mDecorToolbar.hideOverflowMenu(); 764 } 765 766 @Override 767 public void setMenuPrepared() { 768 pullChildren(); 769 mDecorToolbar.setMenuPrepared(); 770 } 771 772 @Override 773 public void setMenu(Menu menu, MenuPresenter.Callback cb) { 774 pullChildren(); 775 mDecorToolbar.setMenu(menu, cb); 776 } 777 778 @Override 779 public void saveToolbarHierarchyState(SparseArray<Parcelable> toolbarStates) { 780 pullChildren(); 781 mDecorToolbar.saveHierarchyState(toolbarStates); 782 } 783 784 @Override 785 public void restoreToolbarHierarchyState(SparseArray<Parcelable> toolbarStates) { 786 pullChildren(); 787 mDecorToolbar.restoreHierarchyState(toolbarStates); 788 } 789 790 @Override 791 public void dismissPopups() { 792 pullChildren(); 793 mDecorToolbar.dismissPopupMenus(); 794 } 795 796 public static class LayoutParams extends MarginLayoutParams { 797 public LayoutParams(Context c, AttributeSet attrs) { 798 super(c, attrs); 799 } 800 801 public LayoutParams(int width, int height) { 802 super(width, height); 803 } 804 805 public LayoutParams(ViewGroup.LayoutParams source) { 806 super(source); 807 } 808 809 public LayoutParams(ViewGroup.MarginLayoutParams source) { 810 super(source); 811 } 812 } 813 814 public interface ActionBarVisibilityCallback { 815 void onWindowVisibilityChanged(int visibility); 816 void showForSystem(); 817 void hideForSystem(); 818 void enableContentAnimations(boolean enable); 819 void onContentScrollStarted(); 820 void onContentScrollStopped(); 821 } 822} 823