-
Notifications
You must be signed in to change notification settings - Fork 24.4k
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
Android: Keep ScrollView content visible after edits #11000
Conversation
By analyzing the blame information on this pull request, we identified @astreet and @janicduplessis to be potential reviewers. |
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.
Thanks for fixing this! Just some small changes before landing.
Style nit: we don't prepend functions/private vars with this.
@@ -98,6 +101,8 @@ public ReactScrollView(ReactContext context, @Nullable FpsListener fpsListener) | |||
} else { | |||
mScroller = null; | |||
} | |||
|
|||
this.setOnHierarchyChangeListener(this); |
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.
nit: don't prefix with this
int currentScrollY = getScrollY(); | ||
int thisHeight = this.getMeasuredHeight(); | ||
|
||
int maxScrollY = Math.max(0, contentHeight - thisHeight); |
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.
We also need to factor in padding (see this from line 337):
int scrollRange = Math.max(
0,
getChildAt(0).getHeight() - (getHeight() - getPaddingBottom() - getPaddingTop()));
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.
(please extract this into a private helper)
this.onContentLayoutChange(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom); | ||
} | ||
|
||
private void onContentLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { |
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.
Just inline this above? Or create a inner class to make things more obvious that onLayoutChange refers to the content view
|
||
} | ||
|
||
private void setContentView(int expectedChildCount) { |
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.
nit: seems like we can just put part of this logic in onChildViewAdded and the rest in onChildViewRemoved. There are already assertions in android code that a scrollview can only have 1 child so I'm not really worried about that.
Thanks for fixing this! I'll let @astreet review. |
a2c2973
to
511188d
Compare
@astreet Thanks for the feedback. I pushed changes to address your comments and rebased my branch on master. I'd like to point out one possible concern with the refactoring of the max scroll y logic into a helper. I noticed that one place that calls it used to use |
I think |
getChildAt(0).getHeight() - (getHeight() - getPaddingBottom() - getPaddingTop())); | ||
if (scrollY >= scrollRange) { | ||
int scrollRange = getMaxScrollY(); | ||
if (scrollY >= getMaxScrollY()) { |
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.
Can you just update this to use scrollRange?
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.
@astreet I'm not sure what you mean. Can you elaborate?
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 just meant you're calling getMaxScrollY twice when you already have the result :)
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.
@astreet Oops 😊 . I made this change, rebased, and pushed my branch.
} | ||
|
||
/** | ||
* Called when a mContentView's layout has changed. |
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.
Can you add a comment that we use this to fix scroll position if it's too large when the content resizes?
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.
Thanks for the fixes, just two small things before landing
4f5bb1e
to
ea5bd49
Compare
import android.widget.OverScroller; | ||
import android.widget.ScrollView; | ||
|
||
import com.facebook.react.bridge.ReactContext; | ||
import com.facebook.common.logging.FLog; |
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.
You'll need to add this as a dependency in the buck file... that's why the builds are failing. Or just remove this and the FLog statement below, I don't think they're that useful ;)
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.
@astreet Thanks for diagnosing my build failure. I removed the logging.
I verified the problematic build step is now passing locally by running these commands (got them from the circleci log):
buck fetch ReactAndroid/src/main/java/com/facebook/react/shell/
buck build ReactAndroid/src/main/java/com/facebook/react/shell/
Is there an easy way to do a full local build with buck so I can be more confident my build will succeed on the CI server? I build with gradle locally so I didn't catch this error.
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.
If I recall correctly you can build UIExplorer with buck by running buck build uiexplorer
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.
(might need to buck fetch uiexplorer
too)
(Also, I appreciate you sticking with this, sorry it's taking so long) |
Suppose that the user is scrolled to the bottom of a ScrollView. Next, the ScrollView's content is edited such that the height of the content changes and the current scroll position is larger than the new height of the content. Consequently, the user sees a blank ScrollView. As soon as the user interacts with the ScrollView, the ScrollView will jump to its max scroll position. This change improves this scenario by ensuring that the user is never staring at a blank ScrollView when the ScrollView has content in it. It does this by moving the ScrollView to its max scroll position when the scroll position after an edit is larger than the max scroll position of the ScrollView.
Android's ScrollView already asserts there's only 1 child which is why we can make this assumption.
Also, refactored this logic into a helper.
ea5bd49
to
36f0821
Compare
@facebook-github-bot shipit Thanks!! |
@astreet has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator. |
Summary: Suppose that the user is scrolled to the bottom of a ScrollView. Next, the ScrollView's content is edited such that the height of the content changes and the current scroll position is larger than the new height of the content. Consequently, the user sees a blank ScrollView. As soon as the user interacts with the ScrollView, the ScrollView will jump to its max scroll position. This change improves this scenario by ensuring that the user is never staring at a blank ScrollView when the ScrollView has content in it. It does this by moving the ScrollView to its max scroll position when the scroll position after an edit is larger than the max scroll position of the ScrollView. Here are some pictures to illustrate how this PR improves the scenario described above: ![image](https://cloud.githubusercontent.com/assets/199935/20408839/0e731774-accc-11e6-9f0a-3d77198645e9.png) ![image](https://cloud.githubusercontent.com/assets/199935/20408844/12877bb6-accc-11e6-8fe2-1c1bb26569cc.png) **Test plan (require Closes facebook#11000 Differential Revision: D4250792 Pulled By: astreet fbshipit-source-id: 940fff6282ad29c796726f68b4519cbdabbfe554
Summary: Suppose that the user is scrolled to the bottom of a ScrollView. Next, the ScrollView's content is edited such that the height of the content changes and the current scroll position is larger than the new height of the content. Consequently, the user sees a blank ScrollView. As soon as the user interacts with the ScrollView, the ScrollView will jump to its max scroll position. This change improves this scenario by ensuring that the user is never staring at a blank ScrollView when the ScrollView has content in it. It does this by moving the ScrollView to its max scroll position when the scroll position after an edit is larger than the max scroll position of the ScrollView. Here are some pictures to illustrate how this PR improves the scenario described above: ![image](https://cloud.githubusercontent.com/assets/199935/20408839/0e731774-accc-11e6-9f0a-3d77198645e9.png) ![image](https://cloud.githubusercontent.com/assets/199935/20408844/12877bb6-accc-11e6-8fe2-1c1bb26569cc.png) **Test plan (require Closes facebook#11000 Differential Revision: D4250792 Pulled By: astreet fbshipit-source-id: 940fff6282ad29c796726f68b4519cbdabbfe554
Suppose that the user is scrolled to the bottom of a ScrollView. Next, the ScrollView's content is edited such that the height of the content changes and the current scroll position is larger than the new height of the content. Consequently, the user sees a blank ScrollView. As soon as the user interacts with the ScrollView, the ScrollView will jump to its max scroll position.
This change improves this scenario by ensuring that the user is never staring at a blank ScrollView when the ScrollView has content in it. It does this by moving the ScrollView to its max scroll position when the scroll position after an edit is larger than the max scroll position of the ScrollView.
Here are some pictures to illustrate how this PR improves the scenario described above:
Test plan (required)
Verified ScrollView keeps the content visible after an edit in a test app. Also, my team uses this change in our app.
Adam Comella
Microsoft Corp.