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