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