mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-02-09 09:12:06 +08:00
Further improvements in RecyclerViewBackedScrollView.
Summary: public Changed ListView to use onLayout and onContentSizeChange (new) events instead of measure. Updated ScrollView implementation to support contentSizeChange event with an implementation based on onLayout attached to the content view. For RecyclerViewBackedScrollView we need to generate that event directly as it doesn't have a concept of content view. This greatly improves performance of ListView that uses RecyclerViewBackedScrollView Reviewed By: mkonicek Differential Revision: D2679460 fb-gh-sync-id: ba26462d9d3b071965cbe46314f89f0dcfd9db9f
This commit is contained in:
committed by
facebook-github-bot-6
parent
848a151ff8
commit
1195f9c8e8
@@ -0,0 +1,40 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
package com.facebook.react.views.recyclerview;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.uimanager.PixelUtil;
|
||||
import com.facebook.react.uimanager.events.Event;
|
||||
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
||||
|
||||
/**
|
||||
* Event dispatched by {@link RecyclerViewBackedScrollView} when total height of it's children
|
||||
* changes
|
||||
*/
|
||||
public class ContentSizeChangeEvent extends Event<ContentSizeChangeEvent> {
|
||||
|
||||
public static final String EVENT_NAME = "topContentSizeChange";
|
||||
|
||||
private final int mWidth;
|
||||
private final int mHeight;
|
||||
|
||||
public ContentSizeChangeEvent(int viewTag, long timestampMs, int width, int height) {
|
||||
super(viewTag, timestampMs);
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventName() {
|
||||
return EVENT_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatch(RCTEventEmitter rctEventEmitter) {
|
||||
WritableMap data = Arguments.createMap();
|
||||
data.putDouble("width", PixelUtil.toDIPFromPixel(mWidth));
|
||||
data.putDouble("height", PixelUtil.toDIPFromPixel(mHeight));
|
||||
rctEventEmitter.receiveEvent(getViewTag(), EVENT_NAME, data);
|
||||
}
|
||||
}
|
||||
@@ -148,6 +148,7 @@ public class RecyclerViewBackedScrollView extends RecyclerView {
|
||||
|
||||
private final List<View> mViews = new ArrayList<>();
|
||||
private final ScrollOffsetTracker mScrollOffsetTracker;
|
||||
private final RecyclerViewBackedScrollView mScrollView;
|
||||
private int mTotalChildrenHeight = 0;
|
||||
|
||||
// The following `OnLayoutChangeListsner` is attached to the views stored in the adapter
|
||||
@@ -173,7 +174,7 @@ public class RecyclerViewBackedScrollView extends RecyclerView {
|
||||
int newHeight = (bottom - top);
|
||||
|
||||
if (oldHeight != newHeight) {
|
||||
mTotalChildrenHeight = mTotalChildrenHeight - oldHeight + newHeight;
|
||||
updateTotalChildrenHeight(newHeight - oldHeight);
|
||||
mScrollOffsetTracker.onHeightChange(mViews.indexOf(v), oldHeight, newHeight);
|
||||
|
||||
// Since "wrapper" view position +dimensions are not managed by NativeViewHierarchyManager
|
||||
@@ -200,7 +201,8 @@ public class RecyclerViewBackedScrollView extends RecyclerView {
|
||||
}
|
||||
};
|
||||
|
||||
public ReactListAdapter() {
|
||||
public ReactListAdapter(RecyclerViewBackedScrollView scrollView) {
|
||||
mScrollView = scrollView;
|
||||
mScrollOffsetTracker = new ScrollOffsetTracker(this);
|
||||
setHasStableIds(true);
|
||||
}
|
||||
@@ -208,7 +210,7 @@ public class RecyclerViewBackedScrollView extends RecyclerView {
|
||||
public void addView(View child, int index) {
|
||||
mViews.add(index, child);
|
||||
|
||||
mTotalChildrenHeight += child.getMeasuredHeight();
|
||||
updateTotalChildrenHeight(child.getMeasuredHeight());
|
||||
child.addOnLayoutChangeListener(mChildLayoutChangeListener);
|
||||
|
||||
notifyItemInserted(index);
|
||||
@@ -219,12 +221,19 @@ public class RecyclerViewBackedScrollView extends RecyclerView {
|
||||
if (child != null) {
|
||||
mViews.remove(index);
|
||||
child.removeOnLayoutChangeListener(mChildLayoutChangeListener);
|
||||
mTotalChildrenHeight -= child.getMeasuredHeight();
|
||||
updateTotalChildrenHeight(-child.getMeasuredHeight());
|
||||
|
||||
notifyItemRemoved(index);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTotalChildrenHeight(int delta) {
|
||||
if (delta != 0) {
|
||||
mTotalChildrenHeight += delta;
|
||||
mScrollView.onTotalChildrenHeightChange(mTotalChildrenHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConcreteViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
return new ConcreteViewHolder(new RecyclableWrapperViewGroup(parent.getContext()));
|
||||
@@ -268,6 +277,12 @@ public class RecyclerViewBackedScrollView extends RecyclerView {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean mSendContentSizeChangeEvents;
|
||||
|
||||
public void setSendContentSizeChangeEvents(boolean sendContentSizeChangeEvents) {
|
||||
mSendContentSizeChangeEvents = sendContentSizeChangeEvents;
|
||||
}
|
||||
|
||||
private int calculateAbsoluteOffset() {
|
||||
int offsetY = 0;
|
||||
if (getChildCount() > 0) {
|
||||
@@ -304,12 +319,23 @@ public class RecyclerViewBackedScrollView extends RecyclerView {
|
||||
getHeight()));
|
||||
}
|
||||
|
||||
private void onTotalChildrenHeightChange(int newTotalChildrenHeight) {
|
||||
if (mSendContentSizeChangeEvents) {
|
||||
((ReactContext) getContext()).getNativeModule(UIManagerModule.class).getEventDispatcher()
|
||||
.dispatchEvent(new ContentSizeChangeEvent(
|
||||
getId(),
|
||||
SystemClock.uptimeMillis(),
|
||||
getWidth(),
|
||||
newTotalChildrenHeight));
|
||||
}
|
||||
}
|
||||
|
||||
public RecyclerViewBackedScrollView(Context context) {
|
||||
super(context);
|
||||
setHasFixedSize(true);
|
||||
setItemAnimator(new NotAnimatedItemAnimator());
|
||||
setLayoutManager(new LinearLayoutManager(context));
|
||||
setAdapter(new ReactListAdapter());
|
||||
setAdapter(new ReactListAdapter(this));
|
||||
}
|
||||
|
||||
/*package*/ void addViewToAdapter(View child, int index) {
|
||||
|
||||
@@ -4,12 +4,17 @@ package com.facebook.react.views.recyclerview;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.common.MapBuilder;
|
||||
import com.facebook.react.uimanager.ReactProp;
|
||||
import com.facebook.react.uimanager.ThemedReactContext;
|
||||
import com.facebook.react.uimanager.ViewGroupManager;
|
||||
import com.facebook.react.views.scroll.ReactScrollViewCommandHelper;
|
||||
import com.facebook.react.views.scroll.ScrollEvent;
|
||||
|
||||
/**
|
||||
* View manager for {@link RecyclerViewBackedScrollView}.
|
||||
@@ -27,6 +32,11 @@ public class RecyclerViewBackedScrollViewManager extends
|
||||
|
||||
// TODO(8624925): Implement removeClippedSubviews support for native ListView
|
||||
|
||||
@ReactProp(name = "onContentSizeChange")
|
||||
public void setOnContentSizeChange(RecyclerViewBackedScrollView view, boolean value) {
|
||||
view.setSendContentSizeChangeEvents(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RecyclerViewBackedScrollView createViewInstance(ThemedReactContext reactContext) {
|
||||
return new RecyclerViewBackedScrollView(reactContext);
|
||||
@@ -76,4 +86,15 @@ public class RecyclerViewBackedScrollViewManager extends
|
||||
ReactScrollViewCommandHelper.ScrollToCommandData data) {
|
||||
view.scrollTo(data.mDestX, data.mDestY, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable
|
||||
Map getExportedCustomDirectEventTypeConstants() {
|
||||
return MapBuilder.builder()
|
||||
.put(ScrollEvent.EVENT_NAME, MapBuilder.of("registrationName", "onScroll"))
|
||||
.put(
|
||||
ContentSizeChangeEvent.EVENT_NAME,
|
||||
MapBuilder.of("registrationName", "onContentSizeChange"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user