Skip to content

Commit

Permalink
Handle transitioning views when clipping
Browse files Browse the repository at this point in the history
  • Loading branch information
lnikkila committed Aug 1, 2018
1 parent abd3fd9 commit 5245abe
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
import com.facebook.react.uimanager.ViewGroupDrawingOrderHelper;
import com.facebook.react.uimanager.ViewProps;
import com.facebook.yoga.YogaConstants;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;

/**
Expand Down Expand Up @@ -106,6 +108,7 @@ public void onLayoutChange(
private @Nullable ChildrenLayoutChangeListener mChildrenLayoutChangeListener;
private @Nullable ReactViewBackgroundDrawable mReactBackgroundDrawable;
private @Nullable OnInterceptTouchEventListener mOnInterceptTouchEventListener;
private @Nullable List<View> mTransitioningViews;
private boolean mNeedsOffscreenAlphaCompositing = false;
private final ViewGroupDrawingOrderHelper mDrawingOrderHelper;
private @Nullable Path mPath;
Expand Down Expand Up @@ -334,16 +337,16 @@ public void updateClippingRect() {

private void updateClippingToRect(Rect clippingRect) {
Assertions.assertNotNull(mAllChildren);
int clippedSoFar = 0;
int childIndexOffset = 0;
for (int i = 0; i < mAllChildrenCount; i++) {
updateSubviewClipStatus(clippingRect, i, clippedSoFar);
if (mAllChildren[i].getParent() == null) {
clippedSoFar++;
updateSubviewClipStatus(clippingRect, i, childIndexOffset);
if (!isChildInViewGroup(mAllChildren[i])) {
childIndexOffset++;
}
}
}

private void updateSubviewClipStatus(Rect clippingRect, int idx, int clippedSoFar) {
private void updateSubviewClipStatus(Rect clippingRect, int idx, int childIndexOffset) {
View child = Assertions.assertNotNull(mAllChildren)[idx];
sHelperRect.set(child.getLeft(), child.getTop(), child.getRight(), child.getBottom());
boolean intersects = clippingRect
Expand All @@ -360,10 +363,10 @@ private void updateSubviewClipStatus(Rect clippingRect, int idx, int clippedSoFa
if (!intersects && child.getParent() != null && !isAnimating) {
// We can try saving on invalidate call here as the view that we remove is out of visible area
// therefore invalidation is not necessary.
super.removeViewsInLayout(idx - clippedSoFar, 1);
super.removeViewsInLayout(idx - childIndexOffset, 1);
needUpdateClippingRecursive = true;
} else if (intersects && child.getParent() == null) {
super.addViewInLayout(child, idx - clippedSoFar, sDefaultLayoutParam, true);
super.addViewInLayout(child, idx - childIndexOffset, sDefaultLayoutParam, true);
invalidate();
needUpdateClippingRecursive = true;
} else if (intersects) {
Expand Down Expand Up @@ -399,19 +402,25 @@ private void updateSubviewClipStatus(View subview) {
boolean oldIntersects = (subview.getParent() != null);

if (intersects != oldIntersects) {
int clippedSoFar = 0;
int childIndexOffset = 0;
for (int i = 0; i < mAllChildrenCount; i++) {
if (mAllChildren[i] == subview) {
updateSubviewClipStatus(mClippingRect, i, clippedSoFar);
updateSubviewClipStatus(mClippingRect, i, childIndexOffset);
break;
}
if (mAllChildren[i].getParent() == null) {
clippedSoFar++;
if (!isChildInViewGroup(mAllChildren[i])) {
childIndexOffset++;
}
}
}
}

private boolean isChildInViewGroup(View view) {
// A child is in the group if it's not clipped and it's not transitioning.
return view.getParent() != null
&& (mTransitioningViews == null || !mTransitioningViews.contains(view));
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
Expand Down Expand Up @@ -509,13 +518,13 @@ protected void dispatchSetPressed(boolean pressed) {
addInArray(child, index);
// we add view as "clipped" and then run {@link #updateSubviewClipStatus} to conditionally
// attach it
int clippedSoFar = 0;
int childIndexOffset = 0;
for (int i = 0; i < index; i++) {
if (mAllChildren[i].getParent() == null) {
clippedSoFar++;
if (!isChildInViewGroup(mAllChildren[i])) {
childIndexOffset++;
}
}
updateSubviewClipStatus(mClippingRect, index, clippedSoFar);
updateSubviewClipStatus(mClippingRect, index, childIndexOffset);
child.addOnLayoutChangeListener(mChildrenLayoutChangeListener);
}

Expand All @@ -525,14 +534,14 @@ protected void dispatchSetPressed(boolean pressed) {
Assertions.assertNotNull(mAllChildren);
view.removeOnLayoutChangeListener(mChildrenLayoutChangeListener);
int index = indexOfChildInAllChildren(view);
if (mAllChildren[index].getParent() != null) {
int clippedSoFar = 0;
if (isChildInViewGroup(mAllChildren[index])) {
int childIndexOffset = 0;
for (int i = 0; i < index; i++) {
if (mAllChildren[i].getParent() == null) {
clippedSoFar++;
if (!isChildInViewGroup(mAllChildren[i])) {
childIndexOffset++;
}
}
super.removeViewsInLayout(index - clippedSoFar, 1);
super.removeViewsInLayout(index - childIndexOffset, 1);
}
removeFromArray(index);
}
Expand All @@ -547,6 +556,26 @@ protected void dispatchSetPressed(boolean pressed) {
mAllChildrenCount = 0;
}

/*package*/ void startViewTransitionWithSubviewClippingEnabled(View view) {
// We're mirroring ViewGroup's mTransitioningViews since when a transitioning child is removed,
// its parent is not set to null unlike a regular child. Normally this wouldn't be an issue as
// ViewGroup pretends the transitioning child doesn't exist when calling any methods that expose
// child views, but we keep track of our children directly when subview clipping is enabled and
// need to be aware of these.
if (mTransitioningViews == null) {
mTransitioningViews = new ArrayList<>();
}
mTransitioningViews.add(view);
startViewTransition(view);
}

/*package*/ void endViewTransitionWithSubviewClippingEnabled(View view) {
if (mTransitioningViews != null) {
mTransitioningViews.remove(view);
}
endViewTransition(view);
}

private int indexOfChildInAllChildren(View child) {
final int count = mAllChildrenCount;
final View[] children = Assertions.assertNotNull(mAllChildren);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,4 +297,24 @@ public void removeAllViews(ReactViewGroup parent) {
parent.removeAllViews();
}
}

@Override
public void startViewTransition(ReactViewGroup parent, View view) {
boolean removeClippedSubviews = parent.getRemoveClippedSubviews();
if (removeClippedSubviews) {
parent.startViewTransitionWithSubviewClippingEnabled(view);
} else {
parent.startViewTransition(view);
}
}

@Override
public void endViewTransition(ReactViewGroup parent, View view) {
boolean removeClippedSubviews = parent.getRemoveClippedSubviews();
if (removeClippedSubviews) {
parent.endViewTransitionWithSubviewClippingEnabled(view);
} else {
parent.endViewTransition(view);
}
}
}

0 comments on commit 5245abe

Please sign in to comment.