Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 67 additions & 24 deletions android/src/main/java/com/rivereactnative/RiveReactNativeView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,70 +2,113 @@ package com.rivereactnative

import android.widget.FrameLayout
import app.rive.runtime.kotlin.RiveAnimationView
import app.rive.runtime.kotlin.RiveDrawable
import app.rive.runtime.kotlin.core.LayerState
import app.rive.runtime.kotlin.core.LinearAnimationInstance
import app.rive.runtime.kotlin.core.PlayableInstance
import app.rive.runtime.kotlin.core.StateMachineInstance
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.LifecycleEventListener
import com.facebook.react.bridge.ReactContext
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.events.RCTEventEmitter

class RiveReactNativeView(private val context: ThemedReactContext) : FrameLayout(context), LifecycleEventListener {
private var riveAnimationView: RiveAnimationView? = null

enum class Events(private val mName: String) {
PLAY("onPlay");

override fun toString(): String {
return mName
}
}

private var riveAnimationView: RiveAnimationView

init {
context.addLifecycleEventListener(this)
riveAnimationView = RiveAnimationView(context)
val listener = object: RiveDrawable.Listener {
override fun notifyLoop(animation: PlayableInstance) {
//TODO("Not yet implemented")
}

override fun notifyPause(animation: PlayableInstance) {
//TODO("Not yet implemented")
}

override fun notifyPlay(animation: PlayableInstance) {
if(animation is LinearAnimationInstance) {
onPlay(animation.animation.name)
}
if(animation is StateMachineInstance) {
onPlay(animation.stateMachine.name, true)
}
}

override fun notifyStateChanged(state: LayerState) {
//TODO("Not yet implemented")
}

override fun notifyStop(animation: PlayableInstance) {
//TODO("Not yet implemented")
}

}
riveAnimationView.registerListener(listener)
addView(riveAnimationView)
}

fun onPlay(animationName: String, isStateMachine: Boolean = false) {
val reactContext = context as ReactContext

val data = Arguments.createMap()
data.putString("animationName", animationName)
data.putBoolean("isStateMachine", isStateMachine)

reactContext.getJSModule(RCTEventEmitter::class.java).receiveEvent(id, Events.PLAY.toString(), data)
}

fun play() {
riveAnimationView?.play()
riveAnimationView.play()
}

fun pause() {
riveAnimationView?.pause()
riveAnimationView.pause()
}

fun stop() {
riveAnimationView?.reset()
riveAnimationView?.stop()
riveAnimationView.reset()
riveAnimationView.stop()
}

fun setResourceName(resourceName: String) {
val (propsFit, propsAlignment) = Pair(riveAnimationView?.fit, riveAnimationView?.alignment)
val (propsFit, propsAlignment) = Pair(riveAnimationView.fit, riveAnimationView.alignment)
val resId = resources.getIdentifier(resourceName, "raw", context.packageName)

if (propsFit != null) {
if (propsAlignment != null) {
riveAnimationView?.setRiveResource(resId, fit = propsFit, alignment = propsAlignment)
} else {
riveAnimationView?.setRiveResource(resId, fit = propsFit)
}
} else {
if(propsAlignment != null) {
riveAnimationView?.setRiveResource(resId, alignment = propsAlignment)
} else {
riveAnimationView?.setRiveResource(resId)
}
}
riveAnimationView.setRiveResource(resId, fit = propsFit, alignment = propsAlignment)
}

fun setFit(rnFit: RNFit) {
val riveFit = RNFit.mapToRiveFit(rnFit)
riveAnimationView?.fit = riveFit
riveAnimationView.fit = riveFit
}

fun setAlignment(rnAlignment: RNAlignment) {
val riveAlignment = RNAlignment.mapToRiveAlignment(rnAlignment)
riveAnimationView?.alignment = riveAlignment
riveAnimationView.alignment = riveAlignment
}

override fun onHostResume() {
riveAnimationView?.play()
riveAnimationView.play()
}

override fun onHostPause() {
riveAnimationView?.pause()
riveAnimationView.pause()
}

override fun onHostDestroy() {
riveAnimationView?.destroy()
riveAnimationView.destroy()
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ class RiveReactNativeViewManager : SimpleViewManager<RiveReactNativeView>() {
STOP
}


override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Map<String, String>>? {
val builder: MapBuilder.Builder<String, Map<String, String>> = MapBuilder.builder<String, Map<String, String>>()
for (event in RiveReactNativeView.Events.values()) {
builder.put(event.toString(), MapBuilder.of("registrationName", event.toString()))
}
return builder.build()
}

override fun getName() = "RiveReactNativeView"

override fun getCommandsMap(): Map<String, Int>? {
Expand Down
3 changes: 3 additions & 0 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ export default function App() {
<View style={styles.container}>
<Rive
ref={riveRef}
onPlay={(animationName, isStateMachine) => {
console.log('animation name :', animationName, isStateMachine);
}}
style={styles.box}
resourceName={Platform.OS === 'android' ? 'flying_car' : 'bird'}
/>
Expand Down
28 changes: 26 additions & 2 deletions src/Rive.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,31 @@ import {
UIManager,
findNodeHandle,
ViewStyle,
NativeSyntheticEvent,
} from 'react-native';
import type { RiveRef } from './types';
import { Alignment, Fit } from './types';

type RiveProps = {
onPlay?: (
event: NativeSyntheticEvent<{
animationName: string;
isStateMachine: boolean;
}>
) => void;
fit: Fit;
alignment: Alignment;
ref: any;
resourceName?: string;
url?: string;
style?: ViewStyle;
ref: any;
testID?: string;
alignment: Alignment;
};

const VIEW_NAME = 'RiveReactNativeView';

type Props = {
onPlay?: (animationName: string, isStateMachine: boolean) => void;
fit?: Fit;
resourceName?: string;
url?: string;
Expand All @@ -34,6 +42,7 @@ export const RiveViewManager = requireNativeComponent<RiveProps>(VIEW_NAME);
const RiveContainer = React.forwardRef<RiveRef, Props>(
(
{
onPlay,
style,
resourceName,
url,
Expand All @@ -43,6 +52,20 @@ const RiveContainer = React.forwardRef<RiveRef, Props>(
ref
) => {
const riveRef = useRef(null);

const onPlayHandler = useCallback(
(
event: NativeSyntheticEvent<{
animationName: string;
isStateMachine: boolean;
}>
) => {
const { animationName, isStateMachine } = event.nativeEvent;
onPlay?.(animationName, isStateMachine);
},
[onPlay]
);

const play = useCallback(() => {
UIManager.dispatchViewManagerCommand(
findNodeHandle(riveRef.current),
Expand Down Expand Up @@ -84,6 +107,7 @@ const RiveContainer = React.forwardRef<RiveRef, Props>(
resourceName={resourceName}
fit={fit}
url={url}
onPlay={onPlayHandler}
alignment={alignment}
/>
);
Expand Down