mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Add ability to control scroll animation duration for Android (#22884)
Summary: Motivation: ---------- This is one of the more sought after feature requests for RN: react-native.canny.io/feature-requests/p/add-speed-attribute-to-scrollto This PR adds the support to add a "duration" whenever using "scrollTo" or "scrollToEnd" with a scrollView. Currently this only exists for Android as the iOS implementation will be somewhat more involved. This PR is also backwards compatible and does not yet deprecate the "animated" boolean. It may not make sense to ever deprecate "animated", as it could be the flag that is used when devs want the system default duration (which is 250ms for Android). I'm not sure what it is for iOS. It would simplify things to remove "animated", though. Pull Request resolved: https://github.com/facebook/react-native/pull/22884 Differential Revision: D13860038 Pulled By: cpojer fbshipit-source-id: f06751d063a33d7046241c95348b6abbb327d36f
This commit is contained in:
committed by
Facebook Github Bot
parent
8afa0378cd
commit
7e8b810499
+22
@@ -7,6 +7,8 @@
|
||||
|
||||
package com.facebook.react.views.scroll;
|
||||
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
@@ -55,6 +57,7 @@ public class ReactHorizontalScrollView extends HorizontalScrollView implements
|
||||
private final Rect mRect = new Rect();
|
||||
|
||||
private boolean mActivelyScrolling;
|
||||
private @Nullable ObjectAnimator mAnimator = null;
|
||||
private @Nullable Rect mClippingRect;
|
||||
private @Nullable String mOverflow = ViewProps.HIDDEN;
|
||||
private boolean mDragging;
|
||||
@@ -183,6 +186,20 @@ public class ReactHorizontalScrollView extends HorizontalScrollView implements
|
||||
awakenScrollBars();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for animating to a ScrollView position with a given duration,
|
||||
* instead of using "smoothScrollTo", which does not expose a duration argument.
|
||||
*/
|
||||
public void animateScroll(int mDestX, int mDestY, int mDuration) {
|
||||
if (mAnimator != null) {
|
||||
mAnimator.cancel();
|
||||
}
|
||||
PropertyValuesHolder scrollX = PropertyValuesHolder.ofInt("scrollX", mDestX);
|
||||
PropertyValuesHolder scrollY = PropertyValuesHolder.ofInt("scrollY", mDestY);
|
||||
mAnimator = ObjectAnimator.ofPropertyValuesHolder(this, scrollX, scrollY);
|
||||
mAnimator.setDuration(mDuration).start();
|
||||
}
|
||||
|
||||
public void setOverflow(String overflow) {
|
||||
mOverflow = overflow;
|
||||
invalidate();
|
||||
@@ -266,6 +283,11 @@ public class ReactHorizontalScrollView extends HorizontalScrollView implements
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mAnimator != null) {
|
||||
mAnimator.cancel();
|
||||
mAnimator = null;
|
||||
}
|
||||
|
||||
mVelocityHelper.calculateVelocity(ev);
|
||||
int action = ev.getAction() & MotionEvent.ACTION_MASK;
|
||||
if (action == MotionEvent.ACTION_UP && mDragging) {
|
||||
|
||||
+14
-4
@@ -171,8 +171,13 @@ public class ReactHorizontalScrollViewManager
|
||||
@Override
|
||||
public void scrollTo(
|
||||
ReactHorizontalScrollView scrollView, ReactScrollViewCommandHelper.ScrollToCommandData data) {
|
||||
if (data.mAnimated) {
|
||||
scrollView.smoothScrollTo(data.mDestX, data.mDestY);
|
||||
if (data.mAnimated && data.mDuration != 0) {
|
||||
if (data.mDuration > 0) {
|
||||
// data.mDuration set to -1 to fallbacks to default platform behavior
|
||||
scrollView.animateScroll(data.mDestX, data.mDestY, data.mDuration);
|
||||
} else {
|
||||
scrollView.smoothScrollTo(data.mDestX, data.mDestY);
|
||||
}
|
||||
} else {
|
||||
scrollView.scrollTo(data.mDestX, data.mDestY);
|
||||
}
|
||||
@@ -185,8 +190,13 @@ public class ReactHorizontalScrollViewManager
|
||||
// ScrollView always has one child - the scrollable area
|
||||
int right =
|
||||
scrollView.getChildAt(0).getWidth() + scrollView.getPaddingRight();
|
||||
if (data.mAnimated) {
|
||||
scrollView.smoothScrollTo(right, scrollView.getScrollY());
|
||||
if (data.mAnimated && data.mDuration != 0) {
|
||||
if (data.mDuration > 0) {
|
||||
// data.mDuration set to -1 to fallbacks to default platform behavior
|
||||
scrollView.animateScroll(right, scrollView.getScrollY(), data.mDuration);
|
||||
} else {
|
||||
scrollView.smoothScrollTo(right, scrollView.getScrollY());
|
||||
}
|
||||
} else {
|
||||
scrollView.scrollTo(right, scrollView.getScrollY());
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
package com.facebook.react.views.scroll;
|
||||
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.annotation.TargetApi;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
@@ -53,6 +55,7 @@ public class ReactScrollView extends ScrollView implements ReactClippingViewGrou
|
||||
private final VelocityHelper mVelocityHelper = new VelocityHelper();
|
||||
private final Rect mRect = new Rect(); // for reuse to avoid allocation
|
||||
|
||||
private @Nullable ObjectAnimator mAnimator = null;
|
||||
private boolean mActivelyScrolling;
|
||||
private @Nullable Rect mClippingRect;
|
||||
private @Nullable String mOverflow = ViewProps.HIDDEN;
|
||||
@@ -171,6 +174,20 @@ public class ReactScrollView extends ScrollView implements ReactClippingViewGrou
|
||||
awakenScrollBars();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for animating to a ScrollView position with a given duration,
|
||||
* instead of using "smoothScrollTo", which does not expose a duration argument.
|
||||
*/
|
||||
public void animateScroll(int mDestX, int mDestY, int mDuration) {
|
||||
if (mAnimator != null) {
|
||||
mAnimator.cancel();
|
||||
}
|
||||
PropertyValuesHolder scrollX = PropertyValuesHolder.ofInt("scrollX", mDestX);
|
||||
PropertyValuesHolder scrollY = PropertyValuesHolder.ofInt("scrollY", mDestY);
|
||||
mAnimator = ObjectAnimator.ofPropertyValuesHolder(this, scrollX, scrollY);
|
||||
mAnimator.setDuration(mDuration).start();
|
||||
}
|
||||
|
||||
public void setOverflow(String overflow) {
|
||||
mOverflow = overflow;
|
||||
invalidate();
|
||||
@@ -255,6 +272,11 @@ public class ReactScrollView extends ScrollView implements ReactClippingViewGrou
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mAnimator != null) {
|
||||
mAnimator.cancel();
|
||||
mAnimator = null;
|
||||
}
|
||||
|
||||
mVelocityHelper.calculateVelocity(ev);
|
||||
int action = ev.getAction() & MotionEvent.ACTION_MASK;
|
||||
if (action == MotionEvent.ACTION_UP && mDragging) {
|
||||
|
||||
+10
-5
@@ -32,22 +32,25 @@ public class ReactScrollViewCommandHelper {
|
||||
|
||||
public static class ScrollToCommandData {
|
||||
|
||||
public final int mDestX, mDestY;
|
||||
public final int mDestX, mDestY, mDuration;
|
||||
public final boolean mAnimated;
|
||||
|
||||
ScrollToCommandData(int destX, int destY, boolean animated) {
|
||||
ScrollToCommandData(int destX, int destY, boolean animated, int duration) {
|
||||
mDestX = destX;
|
||||
mDestY = destY;
|
||||
mAnimated = animated;
|
||||
mDuration = duration;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ScrollToEndCommandData {
|
||||
|
||||
public final int mDuration;
|
||||
public final boolean mAnimated;
|
||||
|
||||
ScrollToEndCommandData(boolean animated) {
|
||||
ScrollToEndCommandData(boolean animated, int duration) {
|
||||
mAnimated = animated;
|
||||
mDuration = duration;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,12 +77,14 @@ public class ReactScrollViewCommandHelper {
|
||||
int destX = Math.round(PixelUtil.toPixelFromDIP(args.getDouble(0)));
|
||||
int destY = Math.round(PixelUtil.toPixelFromDIP(args.getDouble(1)));
|
||||
boolean animated = args.getBoolean(2);
|
||||
viewManager.scrollTo(scrollView, new ScrollToCommandData(destX, destY, animated));
|
||||
int duration = (int) Math.round(args.getDouble(3));
|
||||
viewManager.scrollTo(scrollView, new ScrollToCommandData(destX, destY, animated, duration));
|
||||
return;
|
||||
}
|
||||
case COMMAND_SCROLL_TO_END: {
|
||||
boolean animated = args.getBoolean(0);
|
||||
viewManager.scrollToEnd(scrollView, new ScrollToEndCommandData(animated));
|
||||
int duration = (int) Math.round(args.getDouble(1));
|
||||
viewManager.scrollToEnd(scrollView, new ScrollToEndCommandData(animated, duration));
|
||||
return;
|
||||
}
|
||||
case COMMAND_FLASH_SCROLL_INDICATORS:
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
package com.facebook.react.views.scroll;
|
||||
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
|
||||
|
||||
+14
-4
@@ -191,8 +191,13 @@ public class ReactScrollViewManager
|
||||
@Override
|
||||
public void scrollTo(
|
||||
ReactScrollView scrollView, ReactScrollViewCommandHelper.ScrollToCommandData data) {
|
||||
if (data.mAnimated) {
|
||||
scrollView.smoothScrollTo(data.mDestX, data.mDestY);
|
||||
if (data.mAnimated && data.mDuration != 0) {
|
||||
if (data.mDuration > 0) {
|
||||
// data.mDuration set to -1 to fallbacks to default platform behavior
|
||||
scrollView.animateScroll(data.mDestX, data.mDestY, data.mDuration);
|
||||
} else {
|
||||
scrollView.smoothScrollTo(data.mDestX, data.mDestY);
|
||||
}
|
||||
} else {
|
||||
scrollView.scrollTo(data.mDestX, data.mDestY);
|
||||
}
|
||||
@@ -257,8 +262,13 @@ public class ReactScrollViewManager
|
||||
// ScrollView always has one child - the scrollable area
|
||||
int bottom =
|
||||
scrollView.getChildAt(0).getHeight() + scrollView.getPaddingBottom();
|
||||
if (data.mAnimated) {
|
||||
scrollView.smoothScrollTo(scrollView.getScrollX(), bottom);
|
||||
if (data.mAnimated && data.mDuration != 0) {
|
||||
if (data.mDuration > 0) {
|
||||
// data.mDuration set to -1 to fallbacks to default platform behavior
|
||||
scrollView.animateScroll(scrollView.getScrollX(), bottom, data.mDuration);
|
||||
} else {
|
||||
scrollView.smoothScrollTo(scrollView.getScrollX(), bottom);
|
||||
}
|
||||
} else {
|
||||
scrollView.scrollTo(scrollView.getScrollX(), bottom);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user