Android 9 SystemUI中下拉展开时,NotificationShelf、FooterView会发生重叠。
下拉展开时,NotificationStackScrollLayout中会监听view变化,
private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
= new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
updateForcedScroll();
updateChildren(); //更新所有子view
mChildrenUpdateRequested = false;
getViewTreeObserver().removeOnPreDrawListener(this);
return true;
}
};
/**
* Updates the children views according to the stack scroll algorithm. Call this whenever
* modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
*/
private void updateChildren() {
updateScrollStateForAddedChildren();
mAmbientState.setCurrentScrollVelocity(mScroller.isFinished()
? 0
: mScroller.getCurrVelocity()); //获取滚动速度、设置到 mAmbientState中
mAmbientState.setScrollY(mOwnScrollY);
mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState); //循环更新子view位置、这里每次更新所有子view位置
if (!isCurrentlyAnimating() && !mNeedsAnimation) {
applyCurrentState();
} else {
startAnimationToState();
}
}
public void getStackScrollState(AmbientState ambientState, StackScrollState resultState) {
// The state of the local variables are saved in an algorithmState to easily subdivide it
// into multiple phases.
StackScrollAlgorithmState algorithmState = mTempAlgorithmState;
// First we reset the view states to their default values.
resultState.resetViewStates(); //初始化子view 信息,如何view的state是null, 则创建。数据包含height、gone、alpha等
initAlgorithmState(resultState, algorithmState, ambientState);//Initialize the algorithm state like updating the visible children.
updatePositionsForState(resultState, algorithmState, ambientState);
updateZValuesForState(resultState, algorithmState, ambientState);
updateHeadsUpStates(resultState, algorithmState, ambientState);
handleDraggedViews(ambientState, resultState, algorithmState);
updateDimmedActivatedHideSensitive(ambientState, resultState, algorithmState);
updateClipping(resultState, algorithmState, ambientState);
updateSpeedBumpState(resultState, algorithmState, ambientState);
updateShelfState(resultState, ambientState);
getNotificationChildrenStates(resultState, algorithmState, ambientState);
}
下面是更新具体的view
/**
* 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) {
// The y coordinate of the current child.
float currentYPosition = -algorithmState.scrollY;
int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) {
currentYPosition = updateChild(i, resultState, algorithmState, ambientState,
currentYPosition);
}
}
protected float updateChild(int i, StackScrollState resultState,
StackScrollAlgorithmState algorithmState, AmbientState ambientState,
float currentYPosition) {
ExpandableView child = algorithmState.visibleChildren.get(i);
ExpandableViewState childViewState = resultState.getViewStateForView(child);
childViewState.location = ExpandableViewState.LOCATION_UNKNOWN;
int paddingAfterChild = getPaddingAfterChild(algorithmState, child);
int childHeight = getMaxAllowedChildHeight(child);
childViewState.yTranslation = currentYPosition;
boolean isFooterView = child instanceof FooterView;
boolean isEmptyShadeView = child instanceof EmptyShadeView;
childViewState.location = ExpandableViewState.LOCATION_MAIN_AREA;
float inset = ambientState.getTopPadding() + ambientState.getStackTranslation();
if (i <= algorithmState.getIndexOfExpandingNotification()) {
inset += ambientState.getExpandAnimationTopChange();
}
if (child.mustStayOnScreen() && childViewState.yTranslation >= 0) {
// Even if we're not scrolled away we're in view and we're also not in the
// shelf. We can relax the constraints and let us scroll off the top!
float end = childViewState.yTranslation + childViewState.height + inset; //inset 上面的间距
childViewState.headsUpIsVisible = end < ambientState.getMaxHeadsUpTranslation();
}
if (isFooterView) {
if(MODIFY_ON) {
float footerY = Math.min(childViewState.yTranslation, ambientState.getInnerHeight() - childHeight);
childViewState.yTranslation = Math.max(footerY, childHeight); //56-48
} else {
childViewState.yTranslation = Math.min(childViewState.yTranslation,
ambientState.getInnerHeight() - childHeight);
}
} else if (isEmptyShadeView) {
childViewState.yTranslation = ambientState.getInnerHeight() - childHeight
+ ambientState.getStackTranslation() * 0.25f;
} else {
clampPositionToShelf(child, childViewState, ambientState);
}
currentYPosition = childViewState.yTranslation + childHeight + paddingAfterChild;
if (currentYPosition <= 0) {
childViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
}
if (childViewState.location == ExpandableViewState.LOCATION_UNKNOWN) {
Log.wtf(LOG_TAG, "Failed to assign location for child " + i);
}
childViewState.yTranslation += inset; //childViewState.yTranslation 决定最终显示位置
return currentYPosition;
}
这里MODIFY_ON
修改的内容, childViewState.yTranslation 这是赋值,真正启作用是这个 applyCurrentState()
会调用到NotificationShelf、FooterView、ExpandableNotificationRow view的 applyToView()
函数。位置才会变化
/**
* Apply the properties saved in {@link #mStateMap} to the children of the {@link #mHostView}.
* The properties are only applied if they effectively changed.
*/
public void apply() {
int numChildren = mHostView.getChildCount();
for (int i = 0; i < numChildren; i++) {
ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
ExpandableViewState state = mStateMap.get(child);
if (state == null) {
Log.wtf(CHILD_NOT_FOUND_TAG, "No child state was found when applying this state " +
"to the hostView");
continue;
}
if (state.gone) {
continue;
}
state.applyToView(child);
}
}