-
Notifications
You must be signed in to change notification settings - Fork 24.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix Issue 23870: Android API - View.getGlobalVisibleRect() does not properly clip result rect for ReactClippingViewGroups #26334
Changes from 6 commits
6cf12aa
4a8359c
d5d8096
c33899c
85febce
57383f6
149ef3e
ee4f12c
587f26c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,7 @@ | |
import android.view.MotionEvent; | ||
import android.view.View; | ||
import android.view.ViewGroup; | ||
import android.view.ViewParent; | ||
import android.view.ViewStructure; | ||
import android.view.animation.Animation; | ||
import androidx.annotation.NonNull; | ||
|
@@ -455,6 +456,50 @@ private void updateSubviewClipStatus(View subview) { | |
} | ||
} | ||
|
||
@Override | ||
public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) { | ||
// This is based on the Android ViewGroup implementation, modified to clip child rects | ||
// if overflow is set to ViewProps.HIDDEN. This effectively solves Issue #23870 which | ||
// appears to have been introduced by FLAG_CLIP_CHILDREN being forced false | ||
// regardless of whether clipping is desired. | ||
final RectF rect = new RectF(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @d4vidi I tried to do it that way at first, but unfortunately this class doesn't have access to the superclass members required to use those optimizations. Especially without some getters that weren't introduced until Android API 18 (don't want to break 16 for RN users unnecessarily) |
||
rect.set(r); | ||
|
||
child.getMatrix().mapRect(rect); | ||
|
||
final int dx = child.getLeft() - getScrollX(); | ||
int dy = child.getTop() - getScrollY(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oops. Must have been a copy-paste error (I was comparing against different versions of the Android implementations) ;-). |
||
|
||
rect.offset(dx, dy); | ||
|
||
if (offset != null) { | ||
float[] position = new float[2]; | ||
position[0] = offset.x; | ||
position[1] = offset.y; | ||
child.getMatrix().mapPoints(position); | ||
offset.x = Math.round(position[0]) + dx; | ||
offset.y = Math.round(position[1]) + dy; | ||
} | ||
|
||
final int width = getRight() - getLeft(); | ||
final int height = getBottom() - getTop(); | ||
|
||
boolean rectIsVisible = true; | ||
|
||
ViewParent parent = getParent(); | ||
if (parent == null || ViewProps.HIDDEN.equals(mOverflow)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎉 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The magic line ;-) |
||
rectIsVisible = rect.intersect(0, 0, width, height); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's about the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @d4vidi, yeah I considered that too, but again due to access level, it didn't appear to be possible. |
||
|
||
r.set((int) Math.floor(rect.left), (int) Math.floor(rect.top), | ||
(int) Math.ceil(rect.right), (int) Math.ceil(rect.bottom)); | ||
|
||
if (rectIsVisible && parent != null) { | ||
rectIsVisible = parent.getChildVisibleRect(this, r, offset); | ||
} | ||
return rectIsVisible; | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Last but not least -- lot's of code duplication here... how about exporting all of this to an external util of some sort? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree entirely. Was torn on this as well but decided to go with this way to get feedback first. I suppose I can always pass the value of mOverflow or any other dependencies. ReactClippingViewGroupHelper.java seems like a good candidate for such a function. I'll take a look. |
||
@Override | ||
protected void onSizeChanged(int w, int h, int oldw, int oldh) { | ||
super.onSizeChanged(w, h, oldw, oldh); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you ought to override the method flavor that has the
forceParentCheck
param and take it into account. It's pretty easy to add, isn't it?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@d4vidi
I wanted to, but since the original for that flavor has
@hide
in the comment, it's not available to override. I tried implementing it anyway, but when the ViewGroup implementation tries to call it (via a cast) it never reaches the subclass implementation anyway :(There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/ViewGroup.java#6185
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apparently, the ViewGroup implementation of getChildVisibleRect() is the only one that calls into an auxiliary function to force recursion when not necessary. It seems it was written for some kind of internal testing, and then marked
@hide
to prevent it from being used by the public.