Skip to content
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

feat(renderer): support scrollEventThrottle on Android and iOS #3581

Closed
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export default class PagerExample extends React.Component {
onPageSelected={this.onPageSelected}
onPageScrollStateChanged={this.onPageScrollStateChanged}
onPageScroll={this.onPageScroll}
scrollEventThrottle={1000}
>
{
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ public void run() {
// Reusable int array to be passed to method calls that mutate it in order to "return" two ints.
private final int[] mScrollOffsetPair = new int[2];
private int mNestedScrollOffset = 0;
//scroll throttle time ms
protected int mScrollEventThrottle = 0;
arvinwli marked this conversation as resolved.
Show resolved Hide resolved

private void init(Context context, boolean isVertical) {
setCallPageChangedOnFirstLayout(true);
Expand Down Expand Up @@ -328,6 +330,10 @@ public void setOverflow(String overflow) {
invalidate();
}

public void setScrollEventThrottle(int scrollEventThrottle) {
mScrollEventThrottle = scrollEventThrottle;
}

@Override
public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int axes) {
if (!isScrollEnabled()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ public void setOverflow(HippyViewPager pager, String overflow) {
pager.setOverflow(overflow);
}

@HippyControllerProps(name = "scrollEventThrottle", defaultType = HippyControllerProps.NUMBER, defaultNumber = 0)
public void setScrollEventThrottle(HippyViewPager pager,int scrollEventThrottle) {
pager.setScrollEventThrottle(scrollEventThrottle);
}
arvinwli marked this conversation as resolved.
Show resolved Hide resolved

private void resolveInvalidParams(@Nullable Promise promise) {
if (promise != null) {
String msg = "Invalid parameter!";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.tencent.mtt.hippy.views.viewpager;

import android.os.SystemClock;
import android.view.View;

import androidx.annotation.NonNull;
Expand All @@ -34,21 +35,53 @@ public class ViewPagerPageChangeListener implements ViewPager.OnPageChangeListen
private int mLastPageIndex = 0;
private int mCurrPageIndex = 0;
private final HippyViewPager mPager;
private long mLastScrollEventTimeStamp = -1;
private boolean mHasUnsentScrollEvent;
private int onPageScrolledPosition = 0;
private float onPageScrollPositionOffset = 0;

public ViewPagerPageChangeListener(@NonNull HippyViewPager pager) {
mPager = pager;
}

/**
* Check whether scroll events need to be sent
* @return
*/
protected boolean checkSendOnScrollEvent() {
arvinwli marked this conversation as resolved.
Show resolved Hide resolved
long currTime = SystemClock.elapsedRealtime();
if (currTime - mLastScrollEventTimeStamp >= mPager.mScrollEventThrottle) {
mLastScrollEventTimeStamp = currTime;
return true;
}
return false;
}

@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (mPager != null) {
Map<String, Object> params = new HashMap<>();
params.put(PAGE_ITEM_POSITION, position);
params.put(PAGE_ITEM_OFFSET, positionOffset);
EventUtils.sendComponentEvent(mPager, EventUtils.EVENT_PAGE_SCROLL, params);
onPageScrolledPosition = position;
onPageScrollPositionOffset = positionOffset;
if (mPager == null) {
return;
}
if (!mPager.isScrollEnabled()) {
return;
}
if (checkSendOnScrollEvent()) {
sendPageScrollEvent(position, positionOffset);
} else {
mHasUnsentScrollEvent = true;
}
}

private void sendPageScrollEvent(int position, float positionOffset) {
mHasUnsentScrollEvent = false;
Map<String, Object> params = new HashMap<>();
params.put(PAGE_ITEM_POSITION, position);
params.put(PAGE_ITEM_OFFSET, positionOffset);
EventUtils.sendComponentEvent(mPager, EventUtils.EVENT_PAGE_SCROLL, params);
}

@Override
public void onPageSelected(int position) {
if (mPager != null) {
Expand All @@ -75,6 +108,11 @@ private void onScrollStateChangeToIdle() {
if (mPager == null || mCurrPageIndex == mLastPageIndex) {
return;
}
mLastScrollEventTimeStamp = -1;
// Supplementary sending page scroll event
if (mHasUnsentScrollEvent) {
sendPageScrollEvent(onPageScrolledPosition, onPageScrollPositionOffset);
}
Promise promise = mPager.getCallBackPromise();
if (promise != null) {
Map<String, Object> result = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ typedef void (^ViewPagerItemsCountChanged)(NSUInteger count);
@property (nonatomic, strong) HippyDirectEventBlock onPageScrollStateChanged;

@property (nonatomic, assign) NSInteger initialPage;
@property (nonatomic, assign) double scrollEventThrottle;
@property (nonatomic, assign) CGPoint targetOffset;
@property (nonatomic, assign, readonly) NSUInteger pageCount;
@property (nonatomic, copy) ViewPagerItemsCountChanged itemsChangedBlock;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ @interface NativeRenderViewPager ()

@property (nonatomic, assign) CGFloat previousStopOffset;
@property (nonatomic, assign) NSUInteger lastPageSelectedCallbackIndex;

@property (nonatomic, assign) double _lastScrollDispatchTime;
@property (nonatomic, assign) double mHasUnsentScrollEvent;
@property (nonatomic, assign) NSUInteger onPageScrolledPosition;
@property (nonatomic, assign) CGFloat onPageScrollPositionOffset;
@end

@implementation NativeRenderViewPager
Expand All @@ -62,6 +65,8 @@ - (instancetype)initWithFrame:(CGRect)frame {
self.previousFrame = CGRectZero;
self.scrollViewListener = [NSHashTable weakObjectsHashTable];
self.lastPageIndex = NSUIntegerMax;
self.scrollEventThrottle = 0.0;
self._lastScrollDispatchTime = -1;
self.targetContentOffsetX = CGFLOAT_MAX;
if (@available(iOS 11.0, *)) {
self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
Expand Down Expand Up @@ -99,7 +104,7 @@ - (void)insertHippySubview:(UIView *)view atIndex:(NSInteger)atIndex {
}
[super insertHippySubview:view atIndex:(NSInteger)atIndex];
[self.viewPagerItems insertObject:view atIndex:atIndex];

if ([view isKindOfClass:[NativeRenderViewPagerItem class]]) {
NativeRenderViewPagerItem *item = (NativeRenderViewPagerItem *)view;
__weak NativeRenderViewPager *weakPager = self;
Expand All @@ -117,7 +122,7 @@ - (void)insertHippySubview:(UIView *)view atIndex:(NSInteger)atIndex {
return frame;
};
}

self.needsLayoutItems = YES;
if (_itemsChangedBlock) {
_itemsChangedBlock([self.viewPagerItems count]);
Expand Down Expand Up @@ -175,14 +180,14 @@ - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat currentContentOffset = self.contentOffset.x;
CGFloat offset = currentContentOffset - self.previousStopOffset;
CGFloat offsetRatio = offset / CGRectGetWidth(self.bounds);

if (offsetRatio > 1) {
offsetRatio -= floor(offsetRatio);
}
if (offsetRatio < -1) {
offsetRatio -= ceil(offsetRatio);
}

NSUInteger currentPageIndex = [self currentPageIndex];
NSInteger nextPageIndex = ceil(offsetRatio) == offsetRatio ? currentPageIndex : currentPageIndex + ceil(offsetRatio);
if (nextPageIndex < 0) {
Expand All @@ -191,14 +196,18 @@ - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (nextPageIndex >= [self.viewPagerItems count]) {
nextPageIndex = [self.viewPagerItems count] - 1;
}

if (self.onPageScroll) {
self.onPageScroll(@{
@"position": @(nextPageIndex),
@"offset": @(offsetRatio),
});
if ([self checkSendOnScrollEvent]) {
self.onPageScrolledPosition = nextPageIndex;
self.onPageScrollPositionOffset = offsetRatio;
[self sendOnPageScrollEvent:self.onPageScrolledPosition positionOffset:self.onPageScrollPositionOffset];
} else {
self.mHasUnsentScrollEvent = true;
}

}

for (NSObject<UIScrollViewDelegate> *scrollViewListener in _scrollViewListener) {
if ([scrollViewListener respondsToSelector:@selector(scrollViewDidScroll:)]) {
[scrollViewListener scrollViewDidScroll:scrollView];
Expand Down Expand Up @@ -240,6 +249,10 @@ - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL
if (!decelerate) {
self.isScrolling = NO;
}

if(!decelerate) {
[self onScrollIdle];
}
if (self.onPageScrollStateChanged) {
NSString *state = decelerate ? @"settling" : @"idle";
self.onPageScrollStateChanged(@{ @"pageScrollState": state });
Expand All @@ -255,6 +268,7 @@ - (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView {
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
[self onScrollIdle];
if (self.onPageScrollStateChanged) {
self.onPageScrollStateChanged(@{ @"pageScrollState": @"idle" });
}
Expand Down Expand Up @@ -490,4 +504,31 @@ - (void)autoPageDown {
}
}

/**Check whether scroll events need to be sent*/
- (bool)checkSendOnScrollEvent {
NSTimeInterval now = CACurrentMediaTime();
if (self.scrollEventThrottle < (now - self._lastScrollDispatchTime) * 1000) {
self._lastScrollDispatchTime = now;
return true;
}
return false;
}

- (void)sendOnPageScrollEvent: (NSUInteger)position positionOffset:(CGFloat) positionOffset{
self.mHasUnsentScrollEvent = false;
self.onPageScroll(@{
@"position": @(position),
@"offset": @(positionOffset),
});
}

- (void)onScrollIdle {
//reset on scroll idle
self._lastScrollDispatchTime = -1;
if(self.mHasUnsentScrollEvent) {
self.mHasUnsentScrollEvent = false;
[self sendOnPageScrollEvent:self.onPageScrolledPosition positionOffset:self.onPageScrollPositionOffset];
}
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ - (UIView *)view {
HIPPY_EXPORT_VIEW_PROPERTY(bounces, BOOL)
HIPPY_EXPORT_VIEW_PROPERTY(initialPage, NSInteger)
HIPPY_EXPORT_VIEW_PROPERTY(scrollEnabled, BOOL)
HIPPY_EXPORT_VIEW_PROPERTY(scrollEventThrottle, double)

HIPPY_EXPORT_VIEW_PROPERTY(onPageSelected, HippyDirectEventBlock)
HIPPY_EXPORT_VIEW_PROPERTY(onPageScroll, HippyDirectEventBlock)
Expand Down
Loading