コード例 #1
0
 private void updateStateForChildFullyInBottomStack(
     StackScrollAlgorithmState algorithmState,
     float transitioningPositionStart,
     StackViewState childViewState,
     int childHeight,
     AmbientState ambientState) {
   Log.d(TAG, "updateStateForChildFullyInBottomStack: ");
   float currentYPosition;
   algorithmState.itemsInBottomStack += 1.0f;
   if (algorithmState.itemsInBottomStack < MAX_ITEMS_IN_BOTTOM_STACK) {
     // We are visually entering the bottom stack
     currentYPosition =
         transitioningPositionStart
             + mBottomStackIndentationFunctor.getValue(algorithmState.itemsInBottomStack)
             - mPaddingBetweenElements;
     childViewState.location = StackViewState.LOCATION_BOTTOM_STACK_PEEKING;
   } else {
     // we are fully inside the stack
     if (algorithmState.itemsInBottomStack > MAX_ITEMS_IN_BOTTOM_STACK + 2) {
       childViewState.alpha = 0.0f;
     } else if (algorithmState.itemsInBottomStack > MAX_ITEMS_IN_BOTTOM_STACK + 1) {
       childViewState.alpha = 1.0f - algorithmState.partialInBottom;
     }
     childViewState.location = StackViewState.LOCATION_BOTTOM_STACK_HIDDEN;
     currentYPosition = ambientState.getInnerHeight();
   }
   childViewState.yTranslation = currentYPosition - childHeight;
   clampPositionToTopStackEnd(childViewState, childHeight);
 }
コード例 #2
0
 /**
  * Clamp the yTranslation of the child down such that its end is at most on the beginning of the
  * bottom stack.
  *
  * @param childViewState the view state of the child
  * @param childHeight the height of this child
  */
 private void clampPositionToBottomStackStart(
     StackViewState childViewState, int childHeight, AmbientState ambientState) {
   Log.d(TAG, "clampPositionToBottomStackStart: ");
   childViewState.yTranslation =
       Math.min(
           childViewState.yTranslation,
           ambientState.getInnerHeight()
               - mBottomStackPeekSize
               - mCollapseSecondCardPadding
               - childHeight);
 }
コード例 #3
0
  /**
   * Find the number of items in the top stack and update the result state if needed.
   *
   * @param resultState The result state to update if a height change of an child occurs
   * @param algorithmState The state in which the current pass of the algorithm is currently in
   */
  private void findNumberOfItemsInTopStackAndUpdateState(
      StackScrollState resultState,
      StackScrollAlgorithmState algorithmState,
      AmbientState ambientState) {
    Log.d(TAG, "findNumberOfItemsInTopStackAndUpdateState: ");
    // The y Position if the element would be in a regular scrollView
    float yPositionInScrollView = 0.0f;
    int childCount = algorithmState.visibleChildren.size();

    // find the number of elements in the top stack.
    for (int i = 0; i < childCount; i++) {
      ExpandableView child = algorithmState.visibleChildren.get(i);
      StackViewState childViewState = resultState.getViewStateForView(child);
      int childHeight = getMaxAllowedChildHeight(child, ambientState);
      float yPositionInScrollViewAfterElement =
          yPositionInScrollView + childHeight + mPaddingBetweenElements;
      if (yPositionInScrollView < algorithmState.scrollY) {
        if (i == 0 && algorithmState.scrollY <= mCollapsedSize) {

          // The starting position of the bottom stack peek
          int bottomPeekStart =
              ambientState.getInnerHeight() - mBottomStackPeekSize - mCollapseSecondCardPadding;
          // Collapse and expand the first child while the shade is being expanded
          float maxHeight =
              mIsExpansionChanging && child == mFirstChildWhileExpanding
                  ? mFirstChildMaxHeight
                  : childHeight;
          childViewState.height =
              (int) Math.max(Math.min(bottomPeekStart, maxHeight), mCollapsedSize);
          algorithmState.itemsInTopStack = 1.0f;

        } else if (yPositionInScrollViewAfterElement < algorithmState.scrollY) {
          // According to the regular scroll view we are fully off screen
          algorithmState.itemsInTopStack += 1.0f;
          if (i == 0) {
            childViewState.height = mCollapsedSize;
          }
        } else {
          // According to the regular scroll view we are partially off screen

          // How much did we scroll into this child
          algorithmState.scrolledPixelsTop = algorithmState.scrollY - yPositionInScrollView;
          algorithmState.partialInTop =
              (algorithmState.scrolledPixelsTop) / (childHeight + mPaddingBetweenElements);

          // Our element can be expanded, so this can get negative
          algorithmState.partialInTop = Math.max(0.0f, algorithmState.partialInTop);
          algorithmState.itemsInTopStack += algorithmState.partialInTop;

          if (i == 0) {
            // If it is expanded we have to collapse it to a new size
            float newSize =
                yPositionInScrollViewAfterElement
                    - mPaddingBetweenElements
                    - algorithmState.scrollY
                    + mCollapsedSize;
            newSize = Math.max(mCollapsedSize, newSize);
            algorithmState.itemsInTopStack = 1.0f;
            childViewState.height = (int) newSize;
          }
          algorithmState.lastTopStackIndex = i;
          break;
        }
      } else {
        algorithmState.lastTopStackIndex = i - 1;
        // We are already past the stack so we can end the loop
        break;
      }
      yPositionInScrollView = yPositionInScrollViewAfterElement;
    }
  }
コード例 #4
0
  /**
   * Determine the positions for the views. This is the main part of the algorithm.
   *
   * @param resultState The result state to update if a change to the properties of a child occurs
   * @param algorithmState The state in which the current pass of the algorithm is currently in
   * @param ambientState The current ambient state
   */
  private void updatePositionsForState(
      StackScrollState resultState,
      StackScrollAlgorithmState algorithmState,
      AmbientState ambientState) {
    Log.d(TAG, "updatePositionsForState: ");
    // The starting position of the bottom stack peek
    float bottomPeekStart = ambientState.getInnerHeight() - mBottomStackPeekSize;

    // The position where the bottom stack starts.
    float bottomStackStart = bottomPeekStart - mBottomStackSlowDownLength;

    // The y coordinate of the current child.
    float currentYPosition = 0.0f;

    // How far in is the element currently transitioning into the bottom stack.
    float yPositionInScrollView = 0.0f;

    // If we have a heads-up higher than the collapsed height we need to add the difference to
    // the padding of all other elements, i.e push in the top stack slightly.
    ExpandableNotificationRow topHeadsUpEntry = ambientState.getTopHeadsUpEntry();

    int childCount = algorithmState.visibleChildren.size();
    int numberOfElementsCompletelyIn =
        algorithmState.partialInTop == 1.0f
            ? algorithmState.lastTopStackIndex
            : (int) algorithmState.itemsInTopStack;
    for (int i = 0; i < childCount; i++) {
      ExpandableView child = algorithmState.visibleChildren.get(i);
      StackViewState childViewState = resultState.getViewStateForView(child);
      childViewState.location = StackViewState.LOCATION_UNKNOWN;
      int childHeight = getMaxAllowedChildHeight(child, ambientState);
      float yPositionInScrollViewAfterElement =
          yPositionInScrollView + childHeight + mPaddingBetweenElements;
      float scrollOffset = yPositionInScrollView - algorithmState.scrollY + mCollapsedSize;

      if (i == algorithmState.lastTopStackIndex + 1) {
        // Normally the position of this child is the position in the regular scrollview,
        // but if the two stacks are very close to each other,
        // then have have to push it even more upwards to the position of the bottom
        // stack start.
        currentYPosition = Math.min(scrollOffset, bottomStackStart);
      }
      childViewState.yTranslation = currentYPosition;

      // The y position after this element
      float nextYPosition = currentYPosition + childHeight + mPaddingBetweenElements;

      if (i <= algorithmState.lastTopStackIndex) {
        // Case 1:
        // We are in the top Stack
        updateStateForTopStackChild(
            algorithmState,
            numberOfElementsCompletelyIn,
            i,
            childHeight,
            childViewState,
            scrollOffset);
        clampPositionToTopStackEnd(childViewState, childHeight);

        // check if we are overlapping with the bottom stack
        if (childViewState.yTranslation + childHeight + mPaddingBetweenElements >= bottomStackStart
            && !mIsExpansionChanging
            && i != 0
            && mIsSmallScreen) {
          // we just collapse this element slightly
          int newSize =
              (int)
                  Math.max(
                      bottomStackStart - mPaddingBetweenElements - childViewState.yTranslation,
                      mCollapsedSize);
          childViewState.height = newSize;
          updateStateForChildTransitioningInBottom(
              algorithmState,
              bottomStackStart,
              bottomPeekStart,
              childViewState.yTranslation,
              childViewState,
              childHeight);
        }
        clampPositionToBottomStackStart(childViewState, childViewState.height, ambientState);
      } else if (nextYPosition >= bottomStackStart) {
        // Case 2:
        // We are in the bottom stack.
        if (currentYPosition >= bottomStackStart) {
          // According to the regular scroll view we are fully translated out of the
          // bottom of the screen so we are fully in the bottom stack
          updateStateForChildFullyInBottomStack(
              algorithmState, bottomStackStart, childViewState, childHeight, ambientState);
        } else {
          // According to the regular scroll view we are currently translating out of /
          // into the bottom of the screen
          updateStateForChildTransitioningInBottom(
              algorithmState,
              bottomStackStart,
              bottomPeekStart,
              currentYPosition,
              childViewState,
              childHeight);
        }
      } else {
        // Case 3:
        // We are in the regular scroll area.
        childViewState.location = StackViewState.LOCATION_MAIN_AREA;
        clampYTranslation(childViewState, childHeight, ambientState);
      }

      // The first card is always rendered.
      if (i == 0) {
        childViewState.alpha = 1.0f;
        childViewState.yTranslation = Math.max(mCollapsedSize - algorithmState.scrollY, 0);
        if (childViewState.yTranslation + childViewState.height
            > bottomPeekStart - mCollapseSecondCardPadding) {
          childViewState.height =
              (int)
                  Math.max(
                      bottomPeekStart - mCollapseSecondCardPadding - childViewState.yTranslation,
                      mCollapsedSize);
        }
        childViewState.location = StackViewState.LOCATION_FIRST_CARD;
      }
      if (childViewState.location == StackViewState.LOCATION_UNKNOWN) {
        Log.wtf(LOG_TAG, "Failed to assign location for child " + i);
      }
      currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements;
      yPositionInScrollView = yPositionInScrollViewAfterElement;

      if (ambientState.isShadeExpanded() && topHeadsUpEntry != null && child != topHeadsUpEntry) {
        childViewState.yTranslation += topHeadsUpEntry.getHeadsUpHeight() - mCollapsedSize;
      }
      childViewState.yTranslation +=
          ambientState.getTopPadding() + ambientState.getStackTranslation();
    }
    updateHeadsUpStates(resultState, algorithmState, ambientState);
  }