mirror of
https://github.com/status-im/react-native.git
synced 2025-02-26 08:05:34 +00:00
Add onLoadX support on Android
Summary: ~~This is a WIP, just finished the first bit and wanted to get some feedback to see if this approach seems appropriate, as I haven't done a lot of Android development.~~ Looks ready for review now. Closes https://github.com/facebook/react-native/pull/3791 Reviewed By: svcscm Differential Revision: D2672262 Pulled By: mkonicek fb-gh-sync-id: 1e8f1cc6658fb719a68f7da455f30a7c9b1db730
This commit is contained in:
parent
b65f1f2234
commit
ae09a10c95
@ -28,6 +28,44 @@ var base64Icon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEsAAABLCAQAAACS
|
||||
|
||||
var ImageCapInsetsExample = require('./ImageCapInsetsExample');
|
||||
|
||||
var NetworkImageCallbackExample = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
events: [],
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount() {
|
||||
this.setState({mountTime: new Date()});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var { mountTime } = this.state;
|
||||
|
||||
return (
|
||||
<View>
|
||||
<Image
|
||||
source={this.props.source}
|
||||
style={[styles.base, {overflow: 'visible'}]}
|
||||
onLoadStart={() => this._loadEventFired(`✔ onLoadStart (+${new Date() - mountTime}ms)`)}
|
||||
onLoad={() => this._loadEventFired(`✔ onLoad (+${new Date() - mountTime}ms)`)}
|
||||
onLoadEnd={() => this._loadEventFired(`✔ onLoadEnd (+${new Date() - mountTime}ms)`)}
|
||||
/>
|
||||
|
||||
<Text style={{marginTop: 20}}>
|
||||
{this.state.events.join('\n')}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
|
||||
_loadEventFired(event) {
|
||||
this.setState((state) => {
|
||||
return state.events = [...state.events, event];
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var NetworkImageExample = React.createClass({
|
||||
watchID: (null: ?number),
|
||||
|
||||
@ -92,6 +130,14 @@ exports.examples = [
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Image Loading Events',
|
||||
render: function() {
|
||||
return (
|
||||
<NetworkImageCallbackExample source={{uri: 'http://facebook.github.io/origami/public/images/blog-hero.jpg?r=1'}}/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Error Handler',
|
||||
render: function() {
|
||||
|
@ -56,6 +56,7 @@ var ImageViewAttributes = merge(ReactNativeViewAttributes.UIView, {
|
||||
resizeMode: true,
|
||||
progressiveRenderingEnabled: true,
|
||||
fadeDuration: true,
|
||||
shouldNotifyLoadEvents: true,
|
||||
});
|
||||
|
||||
var Image = React.createClass({
|
||||
@ -75,7 +76,18 @@ var Image = React.createClass({
|
||||
]).isRequired,
|
||||
progressiveRenderingEnabled: PropTypes.bool,
|
||||
fadeDuration: PropTypes.number,
|
||||
style: StyleSheetPropType(ImageStylePropTypes),
|
||||
/**
|
||||
* Invoked on load start
|
||||
*/
|
||||
onLoadStart: PropTypes.func,
|
||||
/**
|
||||
* Invoked when load completes successfully
|
||||
*/
|
||||
onLoad: PropTypes.func,
|
||||
/**
|
||||
* Invoked when load either succeeds or fails
|
||||
*/
|
||||
onLoadEnd: PropTypes.func,
|
||||
/**
|
||||
* Used to locate this view in end-to-end tests.
|
||||
*/
|
||||
@ -137,9 +149,11 @@ var Image = React.createClass({
|
||||
if (source && source.uri) {
|
||||
var {width, height} = source;
|
||||
var style = flattenStyle([{width, height}, styles.base, this.props.style]);
|
||||
var {onLoadStart, onLoad, onLoadEnd} = this.props;
|
||||
|
||||
var nativeProps = merge(this.props, {
|
||||
style,
|
||||
shouldNotifyLoadEvents: !!(onLoadStart || onLoad || onLoadEnd),
|
||||
src: source.uri,
|
||||
});
|
||||
|
||||
@ -186,6 +200,7 @@ var cfg = {
|
||||
defaultImageSrc: true,
|
||||
imageTag: true,
|
||||
progressHandlerRegistered: true,
|
||||
shouldNotifyLoadEvents: true,
|
||||
},
|
||||
};
|
||||
var RKImage = requireNativeComponent('RCTImageView', Image, cfg);
|
||||
|
@ -113,7 +113,6 @@ var Image = React.createClass({
|
||||
onLayout: PropTypes.func,
|
||||
/**
|
||||
* Invoked on load start
|
||||
* @platform ios
|
||||
*/
|
||||
onLoadStart: PropTypes.func,
|
||||
/**
|
||||
@ -128,12 +127,10 @@ var Image = React.createClass({
|
||||
onError: PropTypes.func,
|
||||
/**
|
||||
* Invoked when load completes successfully
|
||||
* @platform ios
|
||||
*/
|
||||
onLoad: PropTypes.func,
|
||||
/**
|
||||
* Invoked when load either succeeds or fails
|
||||
* @platform ios
|
||||
*/
|
||||
onLoadEnd: PropTypes.func,
|
||||
},
|
||||
|
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* 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.image;
|
||||
|
||||
import android.support.annotation.IntDef;
|
||||
|
||||
import com.facebook.react.uimanager.events.Event;
|
||||
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
public class ImageLoadEvent extends Event<ImageLoadEvent> {
|
||||
@IntDef({ON_ERROR, ON_LOAD, ON_LOAD_END, ON_LOAD_START, ON_PROGRESS})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface ImageEventType {}
|
||||
|
||||
// Currently ON_ERROR and ON_PROGRESS are not implemented, these can be added
|
||||
// easily once support exists in fresco.
|
||||
public static final int ON_ERROR = 1;
|
||||
public static final int ON_LOAD = 2;
|
||||
public static final int ON_LOAD_END = 3;
|
||||
public static final int ON_LOAD_START = 4;
|
||||
public static final int ON_PROGRESS = 5;
|
||||
|
||||
private final int mEventType;
|
||||
|
||||
public ImageLoadEvent(int viewId, long timestampMs, @ImageEventType int eventType) {
|
||||
super(viewId, timestampMs);
|
||||
mEventType = eventType;
|
||||
}
|
||||
|
||||
public static String eventNameForType(@ImageEventType int eventType) {
|
||||
switch(eventType) {
|
||||
case ON_ERROR:
|
||||
return "topError";
|
||||
case ON_LOAD:
|
||||
return "topLoad";
|
||||
case ON_LOAD_END:
|
||||
return "topLoadEnd";
|
||||
case ON_LOAD_START:
|
||||
return "topLoadStart";
|
||||
case ON_PROGRESS:
|
||||
return "topProgress";
|
||||
default:
|
||||
throw new IllegalStateException("Invalid image event: " + Integer.toString(eventType));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventName() {
|
||||
return ImageLoadEvent.eventNameForType(mEventType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getCoalescingKey() {
|
||||
// Intentionally casting mEventType because it is guaranteed to be small
|
||||
// enough to fit into short.
|
||||
return (short) mEventType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatch(RCTEventEmitter rctEventEmitter) {
|
||||
rctEventEmitter.receiveEvent(getViewTag(), getEventName(), null);
|
||||
}
|
||||
}
|
@ -11,10 +11,13 @@ package com.facebook.react.views.image;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||
import com.facebook.drawee.controller.AbstractDraweeControllerBuilder;
|
||||
import com.facebook.react.common.MapBuilder;
|
||||
import com.facebook.react.uimanager.ReactProp;
|
||||
import com.facebook.react.uimanager.SimpleViewManager;
|
||||
import com.facebook.react.uimanager.ThemedReactContext;
|
||||
@ -102,6 +105,23 @@ public class ReactImageManager extends SimpleViewManager<ReactImageView> {
|
||||
view.setFadeDuration(durationMs);
|
||||
}
|
||||
|
||||
@ReactProp(name = "shouldNotifyLoadEvents")
|
||||
public void setLoadHandlersRegistered(ReactImageView view, boolean shouldNotifyLoadEvents) {
|
||||
view.setShouldNotifyLoadEvents(shouldNotifyLoadEvents);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Map getExportedCustomDirectEventTypeConstants() {
|
||||
return MapBuilder.of(
|
||||
ImageLoadEvent.eventNameForType(ImageLoadEvent.ON_LOAD_START),
|
||||
MapBuilder.of("registrationName", "onLoadStart"),
|
||||
ImageLoadEvent.eventNameForType(ImageLoadEvent.ON_LOAD),
|
||||
MapBuilder.of("registrationName", "onLoad"),
|
||||
ImageLoadEvent.eventNameForType(ImageLoadEvent.ON_LOAD_END),
|
||||
MapBuilder.of("registrationName", "onLoadEnd")
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAfterUpdateTransaction(ReactImageView view) {
|
||||
super.onAfterUpdateTransaction(view);
|
||||
|
@ -20,11 +20,15 @@ import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.drawable.Animatable;
|
||||
import android.net.Uri;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import com.facebook.common.util.UriUtil;
|
||||
import com.facebook.drawee.controller.AbstractDraweeControllerBuilder;
|
||||
import com.facebook.drawee.controller.BaseControllerListener;
|
||||
import com.facebook.drawee.controller.ControllerListener;
|
||||
import com.facebook.drawee.controller.ForwardingControllerListener;
|
||||
import com.facebook.drawee.drawable.ScalingUtils;
|
||||
import com.facebook.drawee.generic.GenericDraweeHierarchy;
|
||||
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
||||
@ -32,11 +36,15 @@ import com.facebook.drawee.generic.RoundingParams;
|
||||
import com.facebook.drawee.interfaces.DraweeController;
|
||||
import com.facebook.drawee.view.GenericDraweeView;
|
||||
import com.facebook.imagepipeline.common.ResizeOptions;
|
||||
import com.facebook.imagepipeline.image.ImageInfo;
|
||||
import com.facebook.imagepipeline.request.BasePostprocessor;
|
||||
import com.facebook.imagepipeline.request.ImageRequest;
|
||||
import com.facebook.imagepipeline.request.ImageRequestBuilder;
|
||||
import com.facebook.imagepipeline.request.Postprocessor;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.uimanager.PixelUtil;
|
||||
import com.facebook.react.uimanager.UIManagerModule;
|
||||
import com.facebook.react.uimanager.events.EventDispatcher;
|
||||
|
||||
/**
|
||||
* Wrapper class around Fresco's GenericDraweeView, enabling persisting props across multiple view
|
||||
@ -104,8 +112,9 @@ public class ReactImageView extends GenericDraweeView {
|
||||
private boolean mIsLocalImage;
|
||||
private final AbstractDraweeControllerBuilder mDraweeControllerBuilder;
|
||||
private final RoundedCornerPostprocessor mRoundedCornerPostprocessor;
|
||||
private final @Nullable Object mCallerContext;
|
||||
private @Nullable ControllerListener mControllerListener;
|
||||
private @Nullable ControllerListener mControllerForTesting;
|
||||
private final @Nullable Object mCallerContext;
|
||||
private int mFadeDurationMs = -1;
|
||||
private boolean mProgressiveRenderingEnabled;
|
||||
|
||||
@ -127,6 +136,48 @@ public class ReactImageView extends GenericDraweeView {
|
||||
mCallerContext = callerContext;
|
||||
}
|
||||
|
||||
public void setShouldNotifyLoadEvents(boolean shouldNotify) {
|
||||
if (!shouldNotify) {
|
||||
mControllerListener = null;
|
||||
} else {
|
||||
final EventDispatcher mEventDispatcher = ((ReactContext) getContext()).
|
||||
getNativeModule(UIManagerModule.class).getEventDispatcher();
|
||||
|
||||
mControllerListener = new BaseControllerListener<ImageInfo>() {
|
||||
@Override
|
||||
public void onSubmit(String id, Object callerContext) {
|
||||
mEventDispatcher.dispatchEvent(
|
||||
new ImageLoadEvent(getId(), SystemClock.uptimeMillis(), ImageLoadEvent.ON_LOAD_START)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinalImageSet(
|
||||
String id,
|
||||
@Nullable final ImageInfo imageInfo,
|
||||
@Nullable Animatable animatable) {
|
||||
if (imageInfo != null) {
|
||||
mEventDispatcher.dispatchEvent(
|
||||
new ImageLoadEvent(getId(), SystemClock.uptimeMillis(), ImageLoadEvent.ON_LOAD_END)
|
||||
);
|
||||
mEventDispatcher.dispatchEvent(
|
||||
new ImageLoadEvent(getId(), SystemClock.uptimeMillis(), ImageLoadEvent.ON_LOAD)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(String id, Throwable throwable) {
|
||||
mEventDispatcher.dispatchEvent(
|
||||
new ImageLoadEvent(getId(), SystemClock.uptimeMillis(), ImageLoadEvent.ON_LOAD_END)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
mIsDirty = true;
|
||||
}
|
||||
|
||||
public void setBorderColor(int borderColor) {
|
||||
mBorderColor = borderColor;
|
||||
mIsDirty = true;
|
||||
@ -217,21 +268,33 @@ public class ReactImageView extends GenericDraweeView {
|
||||
.setProgressiveRenderingEnabled(mProgressiveRenderingEnabled)
|
||||
.build();
|
||||
|
||||
DraweeController draweeController = mDraweeControllerBuilder
|
||||
.reset()
|
||||
// This builder is reused
|
||||
mDraweeControllerBuilder.reset();
|
||||
|
||||
mDraweeControllerBuilder
|
||||
.setAutoPlayAnimations(true)
|
||||
.setCallerContext(mCallerContext)
|
||||
.setOldController(getController())
|
||||
.setImageRequest(imageRequest)
|
||||
.setControllerListener(mControllerListener)
|
||||
.build();
|
||||
setController(draweeController);
|
||||
.setImageRequest(imageRequest);
|
||||
|
||||
if (mControllerListener != null && mControllerForTesting != null) {
|
||||
ForwardingControllerListener combinedListener = new ForwardingControllerListener();
|
||||
combinedListener.addListener(mControllerListener);
|
||||
combinedListener.addListener(mControllerForTesting);
|
||||
mDraweeControllerBuilder.setControllerListener(combinedListener);
|
||||
} else if (mControllerForTesting != null) {
|
||||
mDraweeControllerBuilder.setControllerListener(mControllerForTesting);
|
||||
} else if (mControllerListener != null) {
|
||||
mDraweeControllerBuilder.setControllerListener(mControllerListener);
|
||||
}
|
||||
|
||||
setController(mDraweeControllerBuilder.build());
|
||||
mIsDirty = false;
|
||||
}
|
||||
|
||||
// VisibleForTesting
|
||||
public void setControllerListener(ControllerListener controllerListener) {
|
||||
mControllerListener = controllerListener;
|
||||
mControllerForTesting = controllerListener;
|
||||
mIsDirty = true;
|
||||
maybeUpdateView();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user