ActionBarOverlayLayout.java revision ade8ff8ba125b7d370de9ef5033ccbe89b2a30cc
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 // The top and bottom action bars are always within the content area. 322 boolean changed = applyInsets(mActionBarTop, systemInsets, true, true, false, true); 323 if (mActionBarBottom != null) { 324 changed |= applyInsets(mActionBarBottom, systemInsets, true, false, true, true); 325 } 326 327 mBaseInnerInsets.set(systemInsets); 328 ViewUtils.computeFitSystemWindows(this, mBaseInnerInsets, mBaseContentInsets); 329 if (!mLastBaseContentInsets.equals(mBaseContentInsets)) { 330 changed = true; 331 mLastBaseContentInsets.set(mBaseContentInsets); 332 } 333 334 if (changed) { 335 requestLayout(); 336 } 337 338 // We don't do any more at this point. To correctly compute the content/inner 339 // insets in all cases, we need to know the measured size of the various action 340 // bar elements. fitSystemWindows() happens before the measure pass, so we can't 341 // do that here. Instead we will take this up in onMeasure(). 342 return true; 343 } 344 345 @Override 346 protected LayoutParams generateDefaultLayoutParams() { 347 return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 348 } 349 350 @Override 351 public LayoutParams generateLayoutParams(AttributeSet attrs) { 352 return new LayoutParams(getContext(), attrs); 353 } 354 355 @Override 356 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 357 return new LayoutParams(p); 358 } 359 360 @Override 361 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 362 return p instanceof LayoutParams; 363 } 364 365 @Override 366 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 367 pullChildren(); 368 369 int maxHeight = 0; 370 int maxWidth = 0; 371 int childState = 0; 372 373 int topInset = 0; 374 int bottomInset = 0; 375 376 measureChildWithMargins(mActionBarTop, widthMeasureSpec, 0, heightMeasureSpec, 0); 377 LayoutParams lp = (LayoutParams) mActionBarTop.getLayoutParams(); 378 maxWidth = Math.max(maxWidth, 379 mActionBarTop.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); 380 maxHeight = Math.max(maxHeight, 381 mActionBarTop.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); 382 childState = ViewUtils.combineMeasuredStates(childState, 383 ViewCompat.getMeasuredState(mActionBarTop)); 384 385 // xlarge screen layout doesn't have bottom action bar. 386 if (mActionBarBottom != null) { 387 measureChildWithMargins(mActionBarBottom, widthMeasureSpec, 0, heightMeasureSpec, 0); 388 lp = (LayoutParams) mActionBarBottom.getLayoutParams(); 389 maxWidth = Math.max(maxWidth, 390 mActionBarBottom.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); 391 maxHeight = Math.max(maxHeight, 392 mActionBarBottom.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); 393 childState = ViewUtils.combineMeasuredStates(childState, 394 ViewCompat.getMeasuredState(mActionBarBottom)); 395 } 396 397 final int vis = ViewCompat.getWindowSystemUiVisibility(this); 398 final boolean stable = (vis & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0; 399 400 if (stable) { 401 // This is the standard space needed for the action bar. For stable measurement, 402 // we can't depend on the size currently reported by it -- this must remain constant. 403 topInset = mActionBarHeight; 404 if (mHasNonEmbeddedTabs) { 405 final View tabs = mActionBarTop.getTabContainer(); 406 if (tabs != null) { 407 // If tabs are not embedded, increase space on top to account for them. 408 topInset += mActionBarHeight; 409 } 410 } 411 } else if (mActionBarTop.getVisibility() != GONE) { 412 // This is the space needed on top of the window for all of the action bar 413 // and tabs. 414 topInset = mActionBarTop.getMeasuredHeight(); 415 } 416 417 if (mDecorToolbar.isSplit()) { 418 // If action bar is split, adjust bottom insets for it. 419 if (mActionBarBottom != null) { 420 if (stable) { 421 bottomInset = mActionBarHeight; 422 } else { 423 bottomInset = mActionBarBottom.getMeasuredHeight(); 424 } 425 } 426 } 427 428 // If the window has not requested system UI layout flags, we need to 429 // make sure its content is not being covered by system UI... though it 430 // will still be covered by the action bar if they have requested it to 431 // overlay. 432 mContentInsets.set(mBaseContentInsets); 433 mInnerInsets.set(mBaseInnerInsets); 434 if (!mOverlayMode && !stable) { 435 mContentInsets.top += topInset; 436 mContentInsets.bottom += bottomInset; 437 } else { 438 mInnerInsets.top += topInset; 439 mInnerInsets.bottom += bottomInset; 440 } 441 applyInsets(mContent, mContentInsets, true, true, true, true); 442 443 if (!mLastInnerInsets.equals(mInnerInsets)) { 444 // If the inner insets have changed, we need to dispatch this down to 445 // the app's fitSystemWindows(). We do this before measuring the content 446 // view to keep the same semantics as the normal fitSystemWindows() call. 447 mLastInnerInsets.set(mInnerInsets); 448 449 mContent.dispatchFitSystemWindows(mInnerInsets); 450 } 451 452 measureChildWithMargins(mContent, widthMeasureSpec, 0, heightMeasureSpec, 0); 453 lp = (LayoutParams) mContent.getLayoutParams(); 454 maxWidth = Math.max(maxWidth, 455 mContent.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); 456 maxHeight = Math.max(maxHeight, 457 mContent.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); 458 childState = ViewUtils.combineMeasuredStates(childState, 459 ViewCompat.getMeasuredState(mContent)); 460 461 // Account for padding too 462 maxWidth += getPaddingLeft() + getPaddingRight(); 463 maxHeight += getPaddingTop() + getPaddingBottom(); 464 465 // Check against our minimum height and width 466 maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); 467 maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); 468 469 setMeasuredDimension( 470 ViewCompat.resolveSizeAndState(maxWidth, widthMeasureSpec, childState), 471 ViewCompat.resolveSizeAndState(maxHeight, heightMeasureSpec, 472 childState << MEASURED_HEIGHT_STATE_SHIFT)); 473 } 474 475 @Override 476 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 477 final int count = getChildCount(); 478 479 final int parentLeft = getPaddingLeft(); 480 final int parentRight = right - left - getPaddingRight(); 481 482 final int parentTop = getPaddingTop(); 483 final int parentBottom = bottom - top - getPaddingBottom(); 484 485 for (int i = 0; i < count; i++) { 486 final View child = getChildAt(i); 487 if (child.getVisibility() != GONE) { 488 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 489 490 final int width = child.getMeasuredWidth(); 491 final int height = child.getMeasuredHeight(); 492 493 int childLeft = parentLeft + lp.leftMargin; 494 int childTop; 495 if (child == mActionBarBottom) { 496 childTop = parentBottom - height - lp.bottomMargin; 497 } else { 498 childTop = parentTop + lp.topMargin; 499 } 500 501 child.layout(childLeft, childTop, childLeft + width, childTop + height); 502 } 503 } 504 } 505 506 @Override 507 public void draw(Canvas c) { 508 super.draw(c); 509 if (mWindowContentOverlay != null && !mIgnoreWindowContentOverlay) { 510 final int top = mActionBarTop.getVisibility() == VISIBLE ? 511 (int) (mActionBarTop.getBottom() + ViewCompat.getTranslationY(mActionBarTop) + 0.5f) 512 : 0; 513 mWindowContentOverlay.setBounds(0, top, getWidth(), 514 top + mWindowContentOverlay.getIntrinsicHeight()); 515 mWindowContentOverlay.draw(c); 516 } 517 } 518 519 @Override 520 public boolean shouldDelayChildPressedState() { 521 return false; 522 } 523 524 @Override 525 public boolean onStartNestedScroll(View child, View target, int axes) { 526 if ((axes & SCROLL_AXIS_VERTICAL) == 0 || mActionBarTop.getVisibility() != VISIBLE) { 527 return false; 528 } 529 return mHideOnContentScroll; 530 } 531 532 @Override 533 public void onNestedScrollAccepted(View child, View target, int axes) { 534 super.onNestedScrollAccepted(child, target, axes); 535 mHideOnContentScrollReference = getActionBarHideOffset(); 536 haltActionBarHideOffsetAnimations(); 537 if (mActionBarVisibilityCallback != null) { 538 mActionBarVisibilityCallback.onContentScrollStarted(); 539 } 540 } 541 542 @Override 543 public void onNestedScroll(View target, int dxConsumed, int dyConsumed, 544 int dxUnconsumed, int dyUnconsumed) { 545 mHideOnContentScrollReference += dyConsumed; 546 setActionBarHideOffset(mHideOnContentScrollReference); 547 } 548 549 @Override 550 public void onStopNestedScroll(View target) { 551 super.onStopNestedScroll(target); 552 if (mHideOnContentScroll && !mAnimatingForFling) { 553 if (mHideOnContentScrollReference <= mActionBarTop.getHeight()) { 554 postRemoveActionBarHideOffset(); 555 } else { 556 postAddActionBarHideOffset(); 557 } 558 } 559 if (mActionBarVisibilityCallback != null) { 560 mActionBarVisibilityCallback.onContentScrollStopped(); 561 } 562 } 563 564 @Override 565 public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) { 566 if (!mHideOnContentScroll || !consumed) { 567 return false; 568 } 569 if (shouldHideActionBarOnFling(velocityX, velocityY)) { 570 addActionBarHideOffset(); 571 } else { 572 removeActionBarHideOffset(); 573 } 574 mAnimatingForFling = true; 575 return true; 576 } 577 578 void pullChildren() { 579 if (mContent == null) { 580 mContent = (ContentFrameLayout) findViewById(R.id.action_bar_activity_content); 581 mActionBarTop = (ActionBarContainer) findViewById(R.id.action_bar_container); 582 mDecorToolbar = getDecorToolbar(findViewById(R.id.action_bar)); 583 mActionBarBottom = (ActionBarContainer) findViewById(R.id.split_action_bar); 584 } 585 } 586 587 private DecorToolbar getDecorToolbar(View view) { 588 if (view instanceof DecorToolbar) { 589 return (DecorToolbar) view; 590 } else if (view instanceof Toolbar) { 591 return ((Toolbar) view).getWrapper(); 592 } else { 593 throw new IllegalStateException("Can't make a decor toolbar out of " + 594 view.getClass().getSimpleName()); 595 } 596 } 597 598 public void setHideOnContentScrollEnabled(boolean hideOnContentScroll) { 599 if (hideOnContentScroll != mHideOnContentScroll) { 600 mHideOnContentScroll = hideOnContentScroll; 601 if (!hideOnContentScroll) { 602 if (VersionUtils.isAtLeastL()) { 603 stopNestedScroll(); 604 } 605 haltActionBarHideOffsetAnimations(); 606 setActionBarHideOffset(0); 607 } 608 } 609 } 610 611 public boolean isHideOnContentScrollEnabled() { 612 return mHideOnContentScroll; 613 } 614 615 public int getActionBarHideOffset() { 616 return mActionBarTop != null ? -((int) ViewCompat.getTranslationY(mActionBarTop)) : 0; 617 } 618 619 public void setActionBarHideOffset(int offset) { 620 haltActionBarHideOffsetAnimations(); 621 final int topHeight = mActionBarTop.getHeight(); 622 offset = Math.max(0, Math.min(offset, topHeight)); 623 ViewCompat.setTranslationY(mActionBarTop, -offset); 624 if (mActionBarBottom != null && mActionBarBottom.getVisibility() != GONE) { 625 // Match the hide offset proportionally for a split bar 626 final float fOffset = (float) offset / topHeight; 627 final int bOffset = (int) (mActionBarBottom.getHeight() * fOffset); 628 ViewCompat.setTranslationY(mActionBarBottom, bOffset); 629 } 630 } 631 632 private void haltActionBarHideOffsetAnimations() { 633 removeCallbacks(mRemoveActionBarHideOffset); 634 removeCallbacks(mAddActionBarHideOffset); 635 if (mCurrentActionBarTopAnimator != null) { 636 mCurrentActionBarTopAnimator.cancel(); 637 } 638 if (mCurrentActionBarBottomAnimator != null) { 639 mCurrentActionBarBottomAnimator.cancel(); 640 } 641 } 642 643 private void postRemoveActionBarHideOffset() { 644 haltActionBarHideOffsetAnimations(); 645 postDelayed(mRemoveActionBarHideOffset, ACTION_BAR_ANIMATE_DELAY); 646 } 647 648 private void postAddActionBarHideOffset() { 649 haltActionBarHideOffsetAnimations(); 650 postDelayed(mAddActionBarHideOffset, ACTION_BAR_ANIMATE_DELAY); 651 } 652 653 private void removeActionBarHideOffset() { 654 haltActionBarHideOffsetAnimations(); 655 mRemoveActionBarHideOffset.run(); 656 } 657 658 private void addActionBarHideOffset() { 659 haltActionBarHideOffsetAnimations(); 660 mAddActionBarHideOffset.run(); 661 } 662 663 private boolean shouldHideActionBarOnFling(float velocityX, float velocityY) { 664 mFlingEstimator.fling(0, 0, 0, (int) velocityY, 0, 0, Integer.MIN_VALUE, Integer.MAX_VALUE); 665 final int finalY = mFlingEstimator.getFinalY(); 666 return finalY > mActionBarTop.getHeight(); 667 } 668 669 @Override 670 public void setWindowCallback(Window.Callback cb) { 671 pullChildren(); 672 mDecorToolbar.setWindowCallback(cb); 673 } 674 675 @Override 676 public void setWindowTitle(CharSequence title) { 677 pullChildren(); 678 mDecorToolbar.setWindowTitle(title); 679 } 680 681 @Override 682 public CharSequence getTitle() { 683 pullChildren(); 684 return mDecorToolbar.getTitle(); 685 } 686 687 @Override 688 public void initFeature(int windowFeature) { 689 pullChildren(); 690 switch (windowFeature) { 691 case Window.FEATURE_PROGRESS: 692 mDecorToolbar.initProgress(); 693 break; 694 case Window.FEATURE_INDETERMINATE_PROGRESS: 695 mDecorToolbar.initIndeterminateProgress(); 696 break; 697 case Window.FEATURE_ACTION_BAR_OVERLAY: 698 setOverlayMode(true); 699 break; 700 } 701 } 702 703 @Override 704 public void setUiOptions(int uiOptions) { 705 // Split Action Bar not included. 706 } 707 708 @Override 709 public boolean hasIcon() { 710 pullChildren(); 711 return mDecorToolbar.hasIcon(); 712 } 713 714 @Override 715 public boolean hasLogo() { 716 pullChildren(); 717 return mDecorToolbar.hasLogo(); 718 } 719 720 @Override 721 public void setIcon(int resId) { 722 pullChildren(); 723 mDecorToolbar.setIcon(resId); 724 } 725 726 @Override 727 public void setIcon(Drawable d) { 728 pullChildren(); 729 mDecorToolbar.setIcon(d); 730 } 731 732 @Override 733 public void setLogo(int resId) { 734 pullChildren(); 735 mDecorToolbar.setLogo(resId); 736 } 737 738 @Override 739 public boolean canShowOverflowMenu() { 740 pullChildren(); 741 return mDecorToolbar.canShowOverflowMenu(); 742 } 743 744 @Override 745 public boolean isOverflowMenuShowing() { 746 pullChildren(); 747 return mDecorToolbar.isOverflowMenuShowing(); 748 } 749 750 @Override 751 public boolean isOverflowMenuShowPending() { 752 pullChildren(); 753 return mDecorToolbar.isOverflowMenuShowPending(); 754 } 755 756 @Override 757 public boolean showOverflowMenu() { 758 pullChildren(); 759 return mDecorToolbar.showOverflowMenu(); 760 } 761 762 @Override 763 public boolean hideOverflowMenu() { 764 pullChildren(); 765 return mDecorToolbar.hideOverflowMenu(); 766 } 767 768 @Override 769 public void setMenuPrepared() { 770 pullChildren(); 771 mDecorToolbar.setMenuPrepared(); 772 } 773 774 @Override 775 public void setMenu(Menu menu, MenuPresenter.Callback cb) { 776 pullChildren(); 777 mDecorToolbar.setMenu(menu, cb); 778 } 779 780 @Override 781 public void saveToolbarHierarchyState(SparseArray<Parcelable> toolbarStates) { 782 pullChildren(); 783 mDecorToolbar.saveHierarchyState(toolbarStates); 784 } 785 786 @Override 787 public void restoreToolbarHierarchyState(SparseArray<Parcelable> toolbarStates) { 788 pullChildren(); 789 mDecorToolbar.restoreHierarchyState(toolbarStates); 790 } 791 792 @Override 793 public void dismissPopups() { 794 pullChildren(); 795 mDecorToolbar.dismissPopupMenus(); 796 } 797 798 public static class LayoutParams extends MarginLayoutParams { 799 public LayoutParams(Context c, AttributeSet attrs) { 800 super(c, attrs); 801 } 802 803 public LayoutParams(int width, int height) { 804 super(width, height); 805 } 806 807 public LayoutParams(ViewGroup.LayoutParams source) { 808 super(source); 809 } 810 811 public LayoutParams(ViewGroup.MarginLayoutParams source) { 812 super(source); 813 } 814 } 815 816 public interface ActionBarVisibilityCallback { 817 void onWindowVisibilityChanged(int visibility); 818 void showForSystem(); 819 void hideForSystem(); 820 void enableContentAnimations(boolean enable); 821 void onContentScrollStarted(); 822 void onContentScrollStopped(); 823 } 824} 825