mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-24 04:16:00 +08:00
Implement native Animated value listeners on Android
Summary: Adds support for `Animated.Value#addListener` for native driven nodes on Android. This is based on work by skevy in the exponent RN fork. Also adds a UIExplorer example. ** Test plan ** Run unit tests Tested that by adding a listener to a native driven animated node and checked that the listener callback is called properly. Also tested that it doesn't crash on iOS that doesn't support this yet. Closes https://github.com/facebook/react-native/pull/8844 Differential Revision: D3670906 fbshipit-source-id: 15700ed7b93db140d907ce80af4dae6be3102135
This commit is contained in:
committed by
Facebook Github Bot 7
parent
30677e7193
commit
158d435f36
@@ -11,6 +11,7 @@
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var DeviceEventEmitter = require('RCTDeviceEventEmitter');
|
||||
var InteractionManager = require('InteractionManager');
|
||||
var Interpolation = require('Interpolation');
|
||||
var React = require('React');
|
||||
@@ -634,6 +635,7 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
_animation: ?Animation;
|
||||
_tracking: ?Animated;
|
||||
_listeners: {[key: string]: ValueListenerCallback};
|
||||
__nativeAnimatedValueListener: ?any;
|
||||
|
||||
constructor(value: number) {
|
||||
super();
|
||||
@@ -652,6 +654,14 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
return this._value + this._offset;
|
||||
}
|
||||
|
||||
__makeNative() {
|
||||
super.__makeNative();
|
||||
|
||||
if (Object.keys(this._listeners).length) {
|
||||
this._startListeningToNativeValueUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Directly set the value. This will stop any animations running on the value
|
||||
* and update all the bound properties.
|
||||
@@ -693,15 +703,49 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
addListener(callback: ValueListenerCallback): string {
|
||||
var id = String(_uniqueId++);
|
||||
this._listeners[id] = callback;
|
||||
if (this.__isNative) {
|
||||
this._startListeningToNativeValueUpdates();
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
removeListener(id: string): void {
|
||||
delete this._listeners[id];
|
||||
if (this.__isNative && Object.keys(this._listeners).length === 0) {
|
||||
this._stopListeningForNativeValueUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
removeAllListeners(): void {
|
||||
this._listeners = {};
|
||||
if (this.__isNative) {
|
||||
this._stopListeningForNativeValueUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
_startListeningToNativeValueUpdates() {
|
||||
if (this.__nativeAnimatedValueListener ||
|
||||
!NativeAnimatedHelper.supportsNativeListener()) {
|
||||
return;
|
||||
}
|
||||
|
||||
NativeAnimatedAPI.startListeningToAnimatedNodeValue(this.__getNativeTag());
|
||||
this.__nativeAnimatedValueListener = DeviceEventEmitter.addListener('onAnimatedValueUpdate', (data) => {
|
||||
if (data.tag !== this.__getNativeTag()) {
|
||||
return;
|
||||
}
|
||||
this._updateValue(data.value, false /* flush */);
|
||||
});
|
||||
}
|
||||
|
||||
_stopListeningForNativeValueUpdates() {
|
||||
if (!this.__nativeAnimatedValueListener ||
|
||||
!NativeAnimatedHelper.supportsNativeListener()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.__nativeAnimatedValueListener.remove();
|
||||
NativeAnimatedAPI.stopListeningToAnimatedNodeValue(this.__getNativeTag());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1204,7 +1248,7 @@ class AnimatedStyle extends AnimatedWithChildren {
|
||||
if (value instanceof Animated) {
|
||||
if (!value.__isNative) {
|
||||
// We cannot use value of natively driven nodes this way as the value we have access from JS
|
||||
// may not be up to date
|
||||
// may not be up to date.
|
||||
style[key] = value.__getValue();
|
||||
}
|
||||
} else {
|
||||
@@ -1296,9 +1340,9 @@ class AnimatedProps extends Animated {
|
||||
for (var key in this._props) {
|
||||
var value = this._props[key];
|
||||
if (value instanceof Animated) {
|
||||
if (!value.__isNative) {
|
||||
if (!value.__isNative || value instanceof AnimatedStyle) {
|
||||
// We cannot use value of natively driven nodes this way as the value we have access from JS
|
||||
// may not be up to date
|
||||
// may not be up to date.
|
||||
props[key] = value.__getValue();
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -30,6 +30,14 @@ var API = {
|
||||
assertNativeAnimatedModule();
|
||||
NativeAnimatedModule.createAnimatedNode(tag, config);
|
||||
},
|
||||
startListeningToAnimatedNodeValue: function(tag: number) {
|
||||
assertNativeAnimatedModule();
|
||||
NativeAnimatedModule.startListeningToAnimatedNodeValue(tag);
|
||||
},
|
||||
stopListeningToAnimatedNodeValue: function(tag: number) {
|
||||
assertNativeAnimatedModule();
|
||||
NativeAnimatedModule.stopListeningToAnimatedNodeValue(tag);
|
||||
},
|
||||
connectAnimatedNodes: function(parentTag: number, childTag: number): void {
|
||||
assertNativeAnimatedModule();
|
||||
NativeAnimatedModule.connectAnimatedNodes(parentTag, childTag);
|
||||
@@ -144,6 +152,11 @@ function assertNativeAnimatedModule(): void {
|
||||
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
||||
}
|
||||
|
||||
// TODO: remove this when iOS supports native listeners.
|
||||
function supportsNativeListener(): bool {
|
||||
return !!NativeAnimatedModule.startListeningToAnimatedNodeValue;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
API,
|
||||
validateProps,
|
||||
@@ -153,4 +166,5 @@ module.exports = {
|
||||
generateNewNodeTag,
|
||||
generateNewAnimationId,
|
||||
assertNativeAnimatedModule,
|
||||
supportsNativeListener,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user