Skip to content

Commit

Permalink
Add FpsListener to React Scroll Views
Browse files Browse the repository at this point in the history
Summary:
We want to give people the ability to log scroll performance (including Fb).
This adds an interface that can be enabled and disabled from the react scroll views.
This is a prerequisite to implementing the actual framerate logger that will log dropped
frames while scrolling in prod.

Reviewed By: astreet

Differential Revision: D3283588

fbshipit-source-id: ed9736cb9ed3f441511647f36b1460092bd91e56
  • Loading branch information
Nathan Spaun authored and Facebook Github Bot 5 committed May 13, 2016
1 parent fd37666 commit b67d4a2
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 4 deletions.
9 changes: 9 additions & 0 deletions Libraries/Components/ScrollView/ScrollView.js
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,15 @@ const ScrollView = React.createClass({
* @platform android
*/
endFillColor: ColorPropType,

/**
* Tag used to log scroll performance on this scroll view. Will force
* momentum events to be turned on (see sendMomentumEvents). This doesn't do
* anything out of the box and you need to implement a custom native
* FpsListener for it to be useful.
* @platform android
*/
scrollPerfTag: PropTypes.string,
},

mixins: [ScrollResponder.Mixin],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

package com.facebook.react.views.scroll;

public interface FpsListener {

/**
* Clients should call this method when they want the listener to begin recording data.
*
* @param tag
*/
void enable(String tag);

/**
* Clients should call this method when they want the listener to stop recording data.
* The listener will then report the data it collected.
*
* Calling disable on a listener that has already been disabled is a no-op.
*
* @param tag
*/
void disable(String tag);

/**
* Reports whether this listener is recording data.
*/
boolean isEnabled();
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,22 @@ public class ReactHorizontalScrollView extends HorizontalScrollView implements
private boolean mRemoveClippedSubviews;
private boolean mScrollEnabled = true;
private boolean mSendMomentumEvents;
private @Nullable FpsListener mFpsListener = null;
private @Nullable String mScrollPerfTag;
private @Nullable Drawable mEndBackground;
private int mEndFillColor = Color.TRANSPARENT;

public ReactHorizontalScrollView(Context context) {
this(context, null);
}

public ReactHorizontalScrollView(Context context, @Nullable FpsListener fpsListener) {
super(context);
mFpsListener = fpsListener;
}

public void setScrollPerfTag(@Nullable String scrollPerfTag) {
mScrollPerfTag = scrollPerfTag;
}

@Override
Expand Down Expand Up @@ -117,6 +128,7 @@ public boolean onInterceptTouchEvent(MotionEvent ev) {
NativeGestureUtil.notifyNativeGestureStarted(this, ev);
ReactScrollViewHelper.emitScrollBeginDragEvent(this);
mDragging = true;
enableFpsListener();
return true;
}

Expand Down Expand Up @@ -193,6 +205,26 @@ public void setEndFillColor(int color) {
}
}

private void enableFpsListener() {
if (isScrollPerfLoggingEnabled()) {
Assertions.assertNotNull(mFpsListener);
Assertions.assertNotNull(mScrollPerfTag);
mFpsListener.enable(mScrollPerfTag);
}
}

private void disableFpsListener() {
if (isScrollPerfLoggingEnabled()) {
Assertions.assertNotNull(mFpsListener);
Assertions.assertNotNull(mScrollPerfTag);
mFpsListener.disable(mScrollPerfTag);
}
}

private boolean isScrollPerfLoggingEnabled() {
return mFpsListener != null && mScrollPerfTag != null && !mScrollPerfTag.isEmpty();
}

@Override
public void draw(Canvas canvas) {
if (mEndFillColor != Color.TRANSPARENT) {
Expand All @@ -214,7 +246,7 @@ public void draw(Canvas canvas) {
@TargetApi(16)
private void handlePostTouchScrolling() {
// If we aren't going to do anything (send events or snap to page), we can early out.
if (!mSendMomentumEvents && !mPagingEnabled) {
if (!mSendMomentumEvents && !mPagingEnabled && !isScrollPerfLoggingEnabled()) {
return;
}

Expand Down Expand Up @@ -253,6 +285,7 @@ public void run() {
ReactScrollViewHelper.emitScrollMomentumEndEvent(ReactHorizontalScrollView.this);
}
ReactHorizontalScrollView.this.mPostTouchRunnable = null;
disableFpsListener();
} else {
ReactHorizontalScrollView.this.postOnAnimationDelayed(this, ReactScrollViewHelper.MOMENTUM_DELAY);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ public class ReactHorizontalScrollViewManager
implements ReactScrollViewCommandHelper.ScrollCommandHandler<ReactHorizontalScrollView> {

private static final String REACT_CLASS = "AndroidHorizontalScrollView";
private @Nullable FpsListener mFpsListener = null;

public ReactHorizontalScrollViewManager() {
this(null);
}

public ReactHorizontalScrollViewManager(@Nullable FpsListener fpsListener) {
mFpsListener = fpsListener;
}

@Override
public String getName() {
Expand All @@ -38,7 +47,7 @@ public String getName() {

@Override
public ReactHorizontalScrollView createViewInstance(ThemedReactContext context) {
return new ReactHorizontalScrollView(context);
return new ReactHorizontalScrollView(context, mFpsListener);
}

@ReactProp(name = "scrollEnabled", defaultBoolean = true)
Expand Down Expand Up @@ -69,6 +78,18 @@ public void setSendMomentumEvents(ReactHorizontalScrollView view, boolean sendMo
view.setSendMomentumEvents(sendMomentumEvents);
}

/**
* Tag used for logging scroll performance on this scroll view. Will force momentum events to be
* turned on (see setSendMomentumEvents).
*
* @param view
* @param scrollPerfTag
*/
@ReactProp(name = "scrollPerfTag")
public void setScrollPerfTag(ReactHorizontalScrollView view, String scrollPerfTag) {
view.setScrollPerfTag(scrollPerfTag);
}

@ReactProp(name = "pagingEnabled")
public void setPagingEnabled(ReactHorizontalScrollView view, boolean pagingEnabled) {
view.setPagingEnabled(pagingEnabled);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,28 @@ public class ReactScrollView extends ScrollView implements ReactClippingViewGrou
private boolean mRemoveClippedSubviews;
private boolean mScrollEnabled = true;
private boolean mSendMomentumEvents;
private @Nullable FpsListener mFpsListener = null;
private @Nullable String mScrollPerfTag;
private @Nullable Drawable mEndBackground;
private int mEndFillColor = Color.TRANSPARENT;

public ReactScrollView(Context context) {
this(context, null);
}

public ReactScrollView(Context context, @Nullable FpsListener fpsListener) {
super(context);
mFpsListener = fpsListener;
}

public void setSendMomentumEvents(boolean sendMomentumEvents) {
mSendMomentumEvents = sendMomentumEvents;
}

public void setScrollPerfTag(String scrollPerfTag) {
mScrollPerfTag = scrollPerfTag;
}

public void setScrollEnabled(boolean scrollEnabled) {
mScrollEnabled = scrollEnabled;
}
Expand Down Expand Up @@ -118,6 +129,7 @@ public boolean onInterceptTouchEvent(MotionEvent ev) {
NativeGestureUtil.notifyNativeGestureStarted(this, ev);
ReactScrollViewHelper.emitScrollBeginDragEvent(this);
mDragging = true;
enableFpsListener();
return true;
}

Expand All @@ -134,6 +146,7 @@ public boolean onTouchEvent(MotionEvent ev) {
if (action == MotionEvent.ACTION_UP && mDragging) {
ReactScrollViewHelper.emitScrollEndDragEvent(this);
mDragging = false;
disableFpsListener();
}
return super.onTouchEvent(ev);
}
Expand Down Expand Up @@ -175,14 +188,16 @@ public void getClippingRect(Rect outClippingRect) {
@Override
public void fling(int velocityY) {
super.fling(velocityY);
if (mSendMomentumEvents) {
if (mSendMomentumEvents || isScrollPerfLoggingEnabled()) {
mFlinging = true;
enableFpsListener();
ReactScrollViewHelper.emitScrollMomentumBeginEvent(this);
Runnable r = new Runnable() {
@Override
public void run() {
if (mDoneFlinging) {
mFlinging = false;
disableFpsListener();
ReactScrollViewHelper.emitScrollMomentumEndEvent(ReactScrollView.this);
} else {
mDoneFlinging = true;
Expand All @@ -194,6 +209,26 @@ public void run() {
}
}

private void enableFpsListener() {
if (isScrollPerfLoggingEnabled()) {
Assertions.assertNotNull(mFpsListener);
Assertions.assertNotNull(mScrollPerfTag);
mFpsListener.enable(mScrollPerfTag);
}
}

private void disableFpsListener() {
if (isScrollPerfLoggingEnabled()) {
Assertions.assertNotNull(mFpsListener);
Assertions.assertNotNull(mScrollPerfTag);
mFpsListener.disable(mScrollPerfTag);
}
}

private boolean isScrollPerfLoggingEnabled() {
return mFpsListener != null && mScrollPerfTag != null && !mScrollPerfTag.isEmpty();
}

@Override
public void draw(Canvas canvas) {
if (mEndFillColor != Color.TRANSPARENT) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ public class ReactScrollViewManager
implements ReactScrollViewCommandHelper.ScrollCommandHandler<ReactScrollView> {

private static final String REACT_CLASS = "RCTScrollView";
private @Nullable FpsListener mFpsListener = null;

public ReactScrollViewManager() {
this(null);
}

public ReactScrollViewManager(@Nullable FpsListener fpsListener) {
mFpsListener = fpsListener;
}

@Override
public String getName() {
Expand All @@ -41,7 +50,7 @@ public String getName() {

@Override
public ReactScrollView createViewInstance(ThemedReactContext context) {
return new ReactScrollView(context);
return new ReactScrollView(context, mFpsListener);
}

@ReactProp(name = "scrollEnabled", defaultBoolean = true)
Expand Down Expand Up @@ -72,6 +81,18 @@ public void setSendMomentumEvents(ReactScrollView view, boolean sendMomentumEven
view.setSendMomentumEvents(sendMomentumEvents);
}

/**
* Tag used for logging scroll performance on this scroll view. Will force momentum events to be
* turned on (see setSendMomentumEvents).
*
* @param view
* @param scrollPerfTag
*/
@ReactProp(name = "scrollPerfTag")
public void setScrollPerfTag(ReactScrollView view, String scrollPerfTag) {
view.setScrollPerfTag(scrollPerfTag);
}

/**
* When set, fills the rest of the scrollview with a color to avoid setting a background and
* creating unnecessary overdraw.
Expand Down

0 comments on commit b67d4a2

Please sign in to comment.