StackScrollAlgorithm.java revision b6e0e1228bfd38f5ba971194afc5c31d99980fa1
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 17package com.android.systemui.statusbar.stack; 18 19import android.content.Context; 20import android.util.Log; 21import android.view.View; 22import android.view.ViewGroup; 23 24import com.android.systemui.R; 25import com.android.systemui.statusbar.ExpandableNotificationRow; 26 27import java.util.ArrayList; 28 29/** 30 * The Algorithm of the {@link com.android.systemui.statusbar.stack 31 * .NotificationStackScrollLayout} which can be queried for {@link com.android.systemui.statusbar 32 * .stack.StackScrollState} 33 */ 34public class StackScrollAlgorithm { 35 36 private static final String LOG_TAG = "StackScrollAlgorithm"; 37 38 private static final int MAX_ITEMS_IN_BOTTOM_STACK = 3; 39 private static final int MAX_ITEMS_IN_TOP_STACK = 3; 40 41 private int mPaddingBetweenElements; 42 private int mCollapsedSize; 43 private int mTopStackPeekSize; 44 private int mBottomStackPeekSize; 45 private int mZDistanceBetweenElements; 46 private int mZBasicHeight; 47 48 private StackIndentationFunctor mTopStackIndentationFunctor; 49 private StackIndentationFunctor mBottomStackIndentationFunctor; 50 51 private int mLayoutHeight; 52 private StackScrollAlgorithmState mTempAlgorithmState = new StackScrollAlgorithmState(); 53 private boolean mIsExpansionChanging; 54 private int mFirstChildMaxHeight; 55 private boolean mIsExpanded; 56 private View mFirstChildWhileExpanding; 57 private boolean mExpandedOnStart; 58 private int mTopStackTotalSize; 59 60 public StackScrollAlgorithm(Context context) { 61 initConstants(context); 62 } 63 64 private void initConstants(Context context) { 65 66 // currently the padding is in the elements themself 67 mPaddingBetweenElements = 0; 68 mCollapsedSize = context.getResources() 69 .getDimensionPixelSize(R.dimen.notification_row_min_height); 70 mTopStackPeekSize = context.getResources() 71 .getDimensionPixelSize(R.dimen.top_stack_peek_amount); 72 mBottomStackPeekSize = context.getResources() 73 .getDimensionPixelSize(R.dimen.bottom_stack_peek_amount); 74 mZDistanceBetweenElements = context.getResources() 75 .getDimensionPixelSize(R.dimen.z_distance_between_notifications); 76 mZBasicHeight = (MAX_ITEMS_IN_BOTTOM_STACK + 1) * mZDistanceBetweenElements; 77 mTopStackTotalSize = mCollapsedSize + mPaddingBetweenElements; 78 mTopStackIndentationFunctor = new PiecewiseLinearIndentationFunctor( 79 MAX_ITEMS_IN_TOP_STACK, 80 mTopStackPeekSize, 81 mTopStackTotalSize, 82 0.5f); 83 mBottomStackIndentationFunctor = new PiecewiseLinearIndentationFunctor( 84 MAX_ITEMS_IN_BOTTOM_STACK, 85 mBottomStackPeekSize, 86 mCollapsedSize + mPaddingBetweenElements + mBottomStackPeekSize, 87 0.5f); 88 } 89 90 91 public void getStackScrollState(StackScrollState resultState) { 92 // The state of the local variables are saved in an algorithmState to easily subdivide it 93 // into multiple phases. 94 StackScrollAlgorithmState algorithmState = mTempAlgorithmState; 95 96 // First we reset the view states to their default values. 97 resultState.resetViewStates(); 98 99 algorithmState.itemsInTopStack = 0.0f; 100 algorithmState.partialInTop = 0.0f; 101 algorithmState.lastTopStackIndex = 0; 102 algorithmState.scrolledPixelsTop = 0; 103 algorithmState.itemsInBottomStack = 0.0f; 104 algorithmState.partialInBottom = 0.0f; 105 106 updateVisibleChildren(resultState, algorithmState); 107 108 algorithmState.scrollY = getAlgorithmScrollPosition(resultState, algorithmState); 109 110 // Phase 1: 111 findNumberOfItemsInTopStackAndUpdateState(resultState, algorithmState); 112 113 // Phase 2: 114 updatePositionsForState(resultState, algorithmState); 115 116 // Phase 3: 117 updateZValuesForState(resultState, algorithmState); 118 } 119 120 /** 121 * Calculates the scroll offset of the algorithm, based on the resultState. 122 * 123 * @param resultState the state to base the calculation on 124 * @param algorithmState The state in which the current pass of the algorithm is currently in 125 * @return the scroll offset used for the algorithm 126 */ 127 private int getAlgorithmScrollPosition(StackScrollState resultState, 128 StackScrollAlgorithmState algorithmState) { 129 130 int resultScroll = resultState.getScrollY() + mCollapsedSize; 131 132 // If the first child was collapsed in an earlier pass, we have to decrease the scroll 133 // position to get into the same state again. 134 if (algorithmState.visibleChildren.size() > 0) { 135 View firstView = algorithmState.visibleChildren.get(0); 136 if (firstView instanceof ExpandableNotificationRow) { 137 ExpandableNotificationRow firstRow = (ExpandableNotificationRow) firstView; 138 if (firstRow.isUserLocked()) { 139 // User is currently modifying this height. 140 return resultScroll; 141 } 142 int scrolledInAmount = 0; 143 // If the child size was not decreased due to scrolling, we don't substract it, 144 if (!mIsExpansionChanging) { 145 scrolledInAmount = firstRow.getExpandPotential(); 146 } else if (mExpandedOnStart && mFirstChildWhileExpanding == firstView) { 147 scrolledInAmount = firstRow.getMaximumAllowedExpandHeight() - 148 mFirstChildMaxHeight; 149 } 150 resultScroll -= scrolledInAmount; 151 } 152 } 153 return resultScroll; 154 } 155 156 /** 157 * Update the visible children on the state. 158 */ 159 private void updateVisibleChildren(StackScrollState resultState, 160 StackScrollAlgorithmState state) { 161 ViewGroup hostView = resultState.getHostView(); 162 int childCount = hostView.getChildCount(); 163 state.visibleChildren.clear(); 164 state.visibleChildren.ensureCapacity(childCount); 165 for (int i = 0; i < childCount; i++) { 166 View v = hostView.getChildAt(i); 167 if (v.getVisibility() != View.GONE) { 168 state.visibleChildren.add(v); 169 } 170 } 171 } 172 173 /** 174 * Determine the positions for the views. This is the main part of the algorithm. 175 * 176 * @param resultState The result state to update if a change to the properties of a child occurs 177 * @param algorithmState The state in which the current pass of the algorithm is currently in 178 * and which will be updated 179 */ 180 private void updatePositionsForState(StackScrollState resultState, 181 StackScrollAlgorithmState algorithmState) { 182 183 // The starting position of the bottom stack peek 184 float bottomPeekStart = mLayoutHeight - mBottomStackPeekSize; 185 186 // The position where the bottom stack starts. 187 float bottomStackStart = bottomPeekStart - mCollapsedSize; 188 189 // The y coordinate of the current child. 190 float currentYPosition = 0.0f; 191 192 // How far in is the element currently transitioning into the bottom stack. 193 float yPositionInScrollView = 0.0f; 194 195 int childCount = algorithmState.visibleChildren.size(); 196 int numberOfElementsCompletelyIn = (int) algorithmState.itemsInTopStack; 197 for (int i = 0; i < childCount; i++) { 198 View child = algorithmState.visibleChildren.get(i); 199 StackScrollState.ViewState childViewState = resultState.getViewStateForView(child); 200 childViewState.location = StackScrollState.ViewState.LOCATION_UNKNOWN; 201 int childHeight = child.getHeight(); 202 float yPositionInScrollViewAfterElement = yPositionInScrollView 203 + childHeight 204 + mPaddingBetweenElements; 205 float scrollOffset = yPositionInScrollView - algorithmState.scrollY + mCollapsedSize; 206 207 if (i == algorithmState.lastTopStackIndex + 1) { 208 // Normally the position of this child is the position in the regular scrollview, 209 // but if the two stacks are very close to each other, 210 // then have have to push it even more upwards to the position of the bottom 211 // stack start. 212 currentYPosition = Math.min(scrollOffset, bottomStackStart); 213 } 214 childViewState.yTranslation = currentYPosition; 215 216 // The y position after this element 217 float nextYPosition = currentYPosition + childHeight + 218 mPaddingBetweenElements; 219 220 if (i <= algorithmState.lastTopStackIndex) { 221 // Case 1: 222 // We are in the top Stack 223 updateStateForTopStackChild(algorithmState, 224 numberOfElementsCompletelyIn, i, childHeight, childViewState, scrollOffset); 225 clampYTranslation(childViewState, childHeight); 226 // check if we are overlapping with the bottom stack 227 if (childViewState.yTranslation + childHeight + mPaddingBetweenElements 228 >= bottomStackStart && !mIsExpansionChanging) { 229 // TODO: handle overlapping sizes with end stack better 230 // we just collapse this element 231 childViewState.height = mCollapsedSize; 232 } 233 } else if (nextYPosition >= bottomStackStart) { 234 // Case 2: 235 // We are in the bottom stack. 236 if (currentYPosition >= bottomStackStart) { 237 // According to the regular scroll view we are fully translated out of the 238 // bottom of the screen so we are fully in the bottom stack 239 updateStateForChildFullyInBottomStack(algorithmState, 240 bottomStackStart, childViewState, childHeight); 241 } else { 242 // According to the regular scroll view we are currently translating out of / 243 // into the bottom of the screen 244 updateStateForChildTransitioningInBottom(algorithmState, 245 bottomStackStart, bottomPeekStart, currentYPosition, 246 childViewState, childHeight); 247 } 248 } else { 249 // Case 3: 250 // We are in the regular scroll area. 251 childViewState.location = StackScrollState.ViewState.LOCATION_MAIN_AREA; 252 clampYTranslation(childViewState, childHeight); 253 } 254 255 // The first card is always rendered. 256 if (i == 0) { 257 childViewState.alpha = 1.0f; 258 childViewState.yTranslation = 0; 259 childViewState.location = StackScrollState.ViewState.LOCATION_FIRST_CARD; 260 } 261 if (childViewState.location == StackScrollState.ViewState.LOCATION_UNKNOWN) { 262 Log.wtf(LOG_TAG, "Failed to assign location for child " + i); 263 } 264 currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements; 265 yPositionInScrollView = yPositionInScrollViewAfterElement; 266 } 267 } 268 269 /** 270 * Clamp the yTranslation both up and down to valid positions. 271 * 272 * @param childViewState the view state of the child 273 * @param childHeight the height of this child 274 */ 275 private void clampYTranslation(StackScrollState.ViewState childViewState, int childHeight) { 276 clampPositionToBottomStackStart(childViewState, childHeight); 277 clampPositionToTopStackEnd(childViewState, childHeight); 278 } 279 280 /** 281 * Clamp the yTranslation of the child down such that its end is at most on the beginning of 282 * the bottom stack. 283 * 284 * @param childViewState the view state of the child 285 * @param childHeight the height of this child 286 */ 287 private void clampPositionToBottomStackStart(StackScrollState.ViewState childViewState, 288 int childHeight) { 289 childViewState.yTranslation = Math.min(childViewState.yTranslation, 290 mLayoutHeight - mBottomStackPeekSize - childHeight); 291 } 292 293 /** 294 * Clamp the yTranslation of the child up such that its end is at lest on the end of the top 295 * stack. 296 * 297 * @param childViewState the view state of the child 298 * @param childHeight the height of this child 299 */ 300 private void clampPositionToTopStackEnd(StackScrollState.ViewState childViewState, 301 int childHeight) { 302 childViewState.yTranslation = Math.max(childViewState.yTranslation, 303 mCollapsedSize - childHeight); 304 } 305 306 private int getMaxAllowedChildHeight(View child) { 307 if (child instanceof ExpandableNotificationRow) { 308 ExpandableNotificationRow row = (ExpandableNotificationRow) child; 309 return row.getMaximumAllowedExpandHeight(); 310 } 311 return child.getHeight(); 312 } 313 314 private void updateStateForChildTransitioningInBottom(StackScrollAlgorithmState algorithmState, 315 float transitioningPositionStart, float bottomPeakStart, float currentYPosition, 316 StackScrollState.ViewState childViewState, int childHeight) { 317 318 // This is the transitioning element on top of bottom stack, calculate how far we are in. 319 algorithmState.partialInBottom = 1.0f - ( 320 (transitioningPositionStart - currentYPosition) / (childHeight + 321 mPaddingBetweenElements)); 322 323 // the offset starting at the transitionPosition of the bottom stack 324 float offset = mBottomStackIndentationFunctor.getValue(algorithmState.partialInBottom); 325 algorithmState.itemsInBottomStack += algorithmState.partialInBottom; 326 childViewState.yTranslation = transitioningPositionStart + offset - childHeight; 327 328 // We want at least to be at the end of the top stack when collapsing 329 clampPositionToTopStackEnd(childViewState, childHeight); 330 childViewState.location = StackScrollState.ViewState.LOCATION_MAIN_AREA; 331 } 332 333 private void updateStateForChildFullyInBottomStack(StackScrollAlgorithmState algorithmState, 334 float transitioningPositionStart, StackScrollState.ViewState childViewState, 335 int childHeight) { 336 337 float currentYPosition; 338 algorithmState.itemsInBottomStack += 1.0f; 339 if (algorithmState.itemsInBottomStack < MAX_ITEMS_IN_BOTTOM_STACK) { 340 // We are visually entering the bottom stack 341 currentYPosition = transitioningPositionStart 342 + mBottomStackIndentationFunctor.getValue(algorithmState.itemsInBottomStack); 343 childViewState.location = StackScrollState.ViewState.LOCATION_BOTTOM_STACK_PEEKING; 344 } else { 345 // we are fully inside the stack 346 if (algorithmState.itemsInBottomStack > MAX_ITEMS_IN_BOTTOM_STACK + 2) { 347 childViewState.alpha = 0.0f; 348 } else if (algorithmState.itemsInBottomStack 349 > MAX_ITEMS_IN_BOTTOM_STACK + 1) { 350 childViewState.alpha = 1.0f - algorithmState.partialInBottom; 351 } 352 childViewState.location = StackScrollState.ViewState.LOCATION_BOTTOM_STACK_HIDDEN; 353 currentYPosition = mLayoutHeight; 354 } 355 childViewState.yTranslation = currentYPosition - childHeight; 356 clampPositionToTopStackEnd(childViewState, childHeight); 357 } 358 359 private void updateStateForTopStackChild(StackScrollAlgorithmState algorithmState, 360 int numberOfElementsCompletelyIn, int i, int childHeight, 361 StackScrollState.ViewState childViewState, float scrollOffset) { 362 363 364 // First we calculate the index relative to the current stack window of size at most 365 // {@link #MAX_ITEMS_IN_TOP_STACK} 366 int paddedIndex = i - 1 367 - Math.max(numberOfElementsCompletelyIn - MAX_ITEMS_IN_TOP_STACK, 0); 368 if (paddedIndex >= 0) { 369 370 // We are currently visually entering the top stack 371 float distanceToStack = childHeight - algorithmState.scrolledPixelsTop; 372 if (i == algorithmState.lastTopStackIndex && distanceToStack > mTopStackTotalSize) { 373 374 // Child is currently translating into stack but not yet inside slow down zone. 375 // Handle it like the regular scrollview. 376 childViewState.yTranslation = scrollOffset; 377 } else { 378 // Apply stacking logic. 379 float numItemsBefore; 380 if (i == algorithmState.lastTopStackIndex) { 381 numItemsBefore = 1.0f - (distanceToStack / mTopStackTotalSize); 382 } else { 383 numItemsBefore = algorithmState.itemsInTopStack - i; 384 } 385 // The end position of the current child 386 float currentChildEndY = mCollapsedSize + mTopStackTotalSize - 387 mTopStackIndentationFunctor.getValue(numItemsBefore); 388 childViewState.yTranslation = currentChildEndY - childHeight; 389 } 390 childViewState.location = StackScrollState.ViewState.LOCATION_TOP_STACK_PEEKING; 391 } else { 392 if (paddedIndex == -1) { 393 childViewState.alpha = 1.0f - algorithmState.partialInTop; 394 } else { 395 // We are hidden behind the top card and faded out, so we can hide ourselves. 396 childViewState.alpha = 0.0f; 397 } 398 childViewState.yTranslation = mCollapsedSize - childHeight; 399 childViewState.location = StackScrollState.ViewState.LOCATION_TOP_STACK_HIDDEN; 400 } 401 402 403 } 404 405 /** 406 * Find the number of items in the top stack and update the result state if needed. 407 * 408 * @param resultState The result state to update if a height change of an child occurs 409 * @param algorithmState The state in which the current pass of the algorithm is currently in 410 * and which will be updated 411 */ 412 private void findNumberOfItemsInTopStackAndUpdateState(StackScrollState resultState, 413 StackScrollAlgorithmState algorithmState) { 414 415 // The y Position if the element would be in a regular scrollView 416 float yPositionInScrollView = 0.0f; 417 int childCount = algorithmState.visibleChildren.size(); 418 419 // find the number of elements in the top stack. 420 for (int i = 0; i < childCount; i++) { 421 View child = algorithmState.visibleChildren.get(i); 422 StackScrollState.ViewState childViewState = resultState.getViewStateForView(child); 423 int childHeight = child.getHeight(); 424 float yPositionInScrollViewAfterElement = yPositionInScrollView 425 + childHeight 426 + mPaddingBetweenElements; 427 if (yPositionInScrollView < algorithmState.scrollY) { 428 if (i == 0 && algorithmState.scrollY == mCollapsedSize) { 429 430 // The starting position of the bottom stack peek 431 int bottomPeekStart = mLayoutHeight - mBottomStackPeekSize; 432 // Collapse and expand the first child while the shade is being expanded 433 float maxHeight = mIsExpansionChanging && child == mFirstChildWhileExpanding 434 ? mFirstChildMaxHeight 435 : childHeight; 436 childViewState.height = (int) Math.max(Math.min(bottomPeekStart, maxHeight), 437 mCollapsedSize); 438 algorithmState.itemsInTopStack = 1.0f; 439 440 } else if (yPositionInScrollViewAfterElement < algorithmState.scrollY) { 441 // According to the regular scroll view we are fully off screen 442 algorithmState.itemsInTopStack += 1.0f; 443 if (i == 0) { 444 childViewState.height = mCollapsedSize; 445 } 446 } else { 447 // According to the regular scroll view we are partially off screen 448 // If it is expanded we have to collapse it to a new size 449 float newSize = yPositionInScrollViewAfterElement 450 - mPaddingBetweenElements 451 - algorithmState.scrollY; 452 453 if (i == 0) { 454 newSize += mCollapsedSize; 455 } 456 457 // How much did we scroll into this child 458 algorithmState.scrolledPixelsTop = childHeight - newSize; 459 algorithmState.partialInTop = (algorithmState.scrolledPixelsTop) / (childHeight 460 + mPaddingBetweenElements); 461 462 // Our element can be expanded, so this can get negative 463 algorithmState.partialInTop = Math.max(0.0f, algorithmState.partialInTop); 464 algorithmState.itemsInTopStack += algorithmState.partialInTop; 465 newSize = Math.max(mCollapsedSize, newSize); 466 if (i == 0) { 467 childViewState.height = (int) newSize; 468 } 469 algorithmState.lastTopStackIndex = i; 470 break; 471 } 472 } else { 473 algorithmState.lastTopStackIndex = i - 1; 474 // We are already past the stack so we can end the loop 475 break; 476 } 477 yPositionInScrollView = yPositionInScrollViewAfterElement; 478 } 479 } 480 481 /** 482 * Calculate the Z positions for all children based on the number of items in both stacks and 483 * save it in the resultState 484 * 485 * @param resultState The result state to update the zTranslation values 486 * @param algorithmState The state in which the current pass of the algorithm is currently in 487 */ 488 private void updateZValuesForState(StackScrollState resultState, 489 StackScrollAlgorithmState algorithmState) { 490 int childCount = algorithmState.visibleChildren.size(); 491 for (int i = 0; i < childCount; i++) { 492 View child = algorithmState.visibleChildren.get(i); 493 StackScrollState.ViewState childViewState = resultState.getViewStateForView(child); 494 if (i < algorithmState.itemsInTopStack) { 495 float stackIndex = algorithmState.itemsInTopStack - i; 496 stackIndex = Math.min(stackIndex, MAX_ITEMS_IN_TOP_STACK + 2); 497 childViewState.zTranslation = mZBasicHeight 498 + stackIndex * mZDistanceBetweenElements; 499 } else if (i > (childCount - 1 - algorithmState.itemsInBottomStack)) { 500 float numItemsAbove = i - (childCount - 1 - algorithmState.itemsInBottomStack); 501 float translationZ = mZBasicHeight 502 - numItemsAbove * mZDistanceBetweenElements; 503 childViewState.zTranslation = translationZ; 504 } else { 505 childViewState.zTranslation = mZBasicHeight; 506 } 507 } 508 } 509 510 public int getLayoutHeight() { 511 return mLayoutHeight; 512 } 513 514 public void setLayoutHeight(int layoutHeight) { 515 this.mLayoutHeight = layoutHeight; 516 } 517 518 public void onExpansionStarted(StackScrollState currentState) { 519 mIsExpansionChanging = true; 520 mExpandedOnStart = mIsExpanded; 521 ViewGroup hostView = currentState.getHostView(); 522 updateFirstChildHeightWhileExpanding(hostView); 523 } 524 525 private void updateFirstChildHeightWhileExpanding(ViewGroup hostView) { 526 mFirstChildWhileExpanding = findFirstVisibleChild(hostView); 527 if (mFirstChildWhileExpanding != null) { 528 if (mExpandedOnStart) { 529 530 // We are collapsing the shade, so the first child can get as most as high as the 531 // current height. 532 mFirstChildMaxHeight = mFirstChildWhileExpanding.getHeight(); 533 } else { 534 535 // We are expanding the shade, expand it to its full height. 536 if (mFirstChildWhileExpanding.getWidth() == 0) { 537 538 // This child was not layouted yet, wait for a layout pass 539 mFirstChildWhileExpanding 540 .addOnLayoutChangeListener(new View.OnLayoutChangeListener() { 541 @Override 542 public void onLayoutChange(View v, int left, int top, int right, 543 int bottom, int oldLeft, int oldTop, int oldRight, 544 int oldBottom) { 545 mFirstChildMaxHeight = getMaxAllowedChildHeight( 546 mFirstChildWhileExpanding); 547 mFirstChildWhileExpanding.removeOnLayoutChangeListener(this); 548 } 549 }); 550 } else { 551 mFirstChildMaxHeight = getMaxAllowedChildHeight(mFirstChildWhileExpanding); 552 } 553 } 554 } else { 555 mFirstChildMaxHeight = 0; 556 } 557 } 558 559 private View findFirstVisibleChild(ViewGroup container) { 560 int childCount = container.getChildCount(); 561 for (int i = 0; i < childCount; i++) { 562 View child = container.getChildAt(i); 563 if (child.getVisibility() != View.GONE) { 564 return child; 565 } 566 } 567 return null; 568 } 569 570 public void onExpansionStopped() { 571 mIsExpansionChanging = false; 572 mFirstChildWhileExpanding = null; 573 } 574 575 public void setIsExpanded(boolean isExpanded) { 576 this.mIsExpanded = isExpanded; 577 } 578 579 public void notifyChildrenChanged(ViewGroup hostView) { 580 if (mIsExpansionChanging) { 581 updateFirstChildHeightWhileExpanding(hostView); 582 } 583 } 584 585 class StackScrollAlgorithmState { 586 587 /** 588 * The scroll position of the algorithm 589 */ 590 public int scrollY; 591 592 /** 593 * The quantity of items which are in the top stack. 594 */ 595 public float itemsInTopStack; 596 597 /** 598 * how far in is the element currently transitioning into the top stack 599 */ 600 public float partialInTop; 601 602 /** 603 * The number of pixels the last child in the top stack has scrolled in to the stack 604 */ 605 public float scrolledPixelsTop; 606 607 /** 608 * The last item index which is in the top stack. 609 */ 610 public int lastTopStackIndex; 611 612 /** 613 * The quantity of items which are in the bottom stack. 614 */ 615 public float itemsInBottomStack; 616 617 /** 618 * how far in is the element currently transitioning into the bottom stack 619 */ 620 public float partialInBottom; 621 622 /** 623 * The children from the host view which are not gone. 624 */ 625 public final ArrayList<View> visibleChildren = new ArrayList<View>(); 626 } 627 628} 629