Basic steps
Create native UI and register this into ReactPackage
1. Create a view
- rn_view_example.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="300dp" android:layout_height="100dp" android:text="Test"/> </LinearLayout>
class RNViewExample: LinearLayout { var myContext: ThemedReactContext = context as ThemedReactContext; constructor(context: ThemedReactContext): super(context) { init(); } private fun init() { inflate(context, R.layout.rn_view_example, this) } }
2. Create a view manager to use this view in React Native
class RNViewManagerExample : SimpleViewManager<RNViewExample>() { override fun getName() = "RNViewExample" override fun createViewInstance(reactContext: ThemedReactContext): RNViewExample { return RNViewExample(reactContext) } }
3. Register View Manager into ReactPackage
class RNAircastPackage : ReactPackage { override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> { return emptyList() } override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> { return mutableListOf( RNViewManagerExample() ) } }
Create a view component in JS/TS
1. Create a view component
import React from 'react'; import { requireNativeComponent, ViewProps } from 'react-native'; export const NativeViewExampleRaw = requireNativeComponent<{}>( 'RNViewExample' ); type Props = ViewProps; const NativeViewExample: React.FC<Props> = (props) => { return <NativeViewExampleRaw {...props} />; }; export default NativeViewExample;
2. Load this React component
... <NativeViewExample style={styles.player} /> ... const styles = StyleSheet.create({ player: { width: '100%', height: '50%', backgroundColor: '#ddd', marginTop: 15, }, });
Reference bridging-native-ui-components
How to expose a prop in view manager?
1. Expose a prop name url in native code
// ViewManager class RNViewManagerExample : SimpleViewManager<RNViewExample>() { ... @ReactProp(name = "url") fun setUrl(view: RNViewExample, url: String) { view.url = url } }
// ExampleView class RNViewExample: LinearLayout { ... var url: String? = null set(value) { field = value } ... }
2. Pass this prop in JS
const NativeViewExample: React.FC<Props> = () => { ... return <NativeViewExampleRaw style={style} url={url} />; }; export default NativeViewExample;
How to expose methods in native view?
Expose these 2 methods
class RNViewExample: LinearLayout { ... fun play() { player.play() } fun stop() { player.stop() } };
1. Create a command map in view manager
class RNViewManagerExample : SimpleViewManager<RNViewExample>() { ... override fun getCommandsMap(): Map<String, Int> { return ViewProps.Commands.toCommandsMap() } override fun receiveCommand( root: RNViewExample, commandId: String?, args: ReadableArray? ) { super.receiveCommand(root, commandId, args) // you can also get argument like this: args!!.getString(0) when (commandId) { ViewProps.Commands.PLAY.ordinal.toString() -> root.play() ViewProps.Commands.STOP.ordinal.toString() -> root.stop() } } ... }
// ViewProps import com.facebook.react.common.MapBuilder class ViewProps { enum class Commands(val action: String) { PLAY("play"), STOP("stop"); companion object { fun toCommandsMap(): Map<String, Int> { return values().associate { it.action to it.ordinal } } } } }
2. Expose these methods in JS view component
type NativeProps = { style: ViewStyle; url: string; }; export type NativeMethods = { play: () => void; stop: () => void; }; /////////////////////////////////// /////////////////////////////////// export const NativeViewManager = requireNativeComponent<NativeProps>('RNViewExample'); const JSViewExample = forwardRef<NativeMethods, NativeProps>( ({ style, url }, ref) => { const nativeRef = useRef<typeof NativeViewManager | null>(null); useImperativeHandle(ref, () => ({ play: () => { UIManager.dispatchViewManagerCommand( findNodeHandle(nativeRef.current), UIManager.getViewManagerConfig( 'RNViewExample' ).Commands.play.toString(), [] // you can pass argument to method as well ); }, stop: () => { UIManager.dispatchViewManagerCommand( findNodeHandle(nativeRef.current), UIManager.getViewManagerConfig( 'RNViewExample' ).Commands.stop.toString(), [] ); }, })); return ( <NativeViewManager style={style} url={url} ref={nativeRef as any} /> ); } ); export default JSViewExample;
3. Render this JS View component and trigger native method
const MyComponent = () => { const playerRef = useRef<any>(null); const [play, setPlay] = useState(false); const playStop = () => { if (play) { playerRef.current.play(); // call native method } else { playerRef.current.stop(); // call native method } setPlay(!play); }; return ( <> <Button onPress={playStop}>{!play ? 'STOP' : 'PLAY'}</Button> <JSViewExample style={styles.player} url={'srt://xxxxxxxxxxx'} ref={playerRef} /> </> ); }; const styles = StyleSheet.create({ player: { width: '100%', height: '10%', backgroundColor: '#ddd', marginTop: 15, }, }); export default MyComponent;
Reference
pass-onpictureinpicturemodechanged-result-into-a-react-native-module
how-to-implement-a-react-native-ui-component-method-in-android
How to send event from native to JS?
Method 1
Native side
- View
class RNViewExample(reactContext: ReactApplicationContext): LinearLayout { private val reactContext = reactContext fun play() { ... val event = Arguments.createMap() event.putString("data", "my name") reactContext.getJSModule( RCTEventEmitter::class.java ).receiveEvent( id, "play", // event name event ) } };
- ViewManager
class RNViewManagerExample : SimpleViewManager<RNViewExample>() { ... override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any> { return MapBuilder.of( "play", // event name MapBuilder.of("registrationName", "onPlay")) // onPlay is the prop name } }
JS Side
export const NativeViewManager = requireNativeComponent<NativeProps>('RNViewExample'); const JSViewExample = forwardRef<NativeMethods, NativeProps>( ({ style, url }, ref) => { ... return ( <NativeViewManager ... onPlay={(event: { nativeEvent: { data: any } }) => console.log(event.nativeEvent.data) } /> ); } ); export default JSViewExample;
Q&A
- How to pass reactContext into View?
Pass “reactContext” into ViewManager at ReactPackage file
class RNAircastPackage : ReactPackage { ... override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> { return listOf( RNViewManagerExample(reactContext) ).toMutableList() } }
Method 2
Native side
val params = Arguments.createMap() params.putString("data", "Your data is here...") reactContext .getJSModule(RCTDeviceEventEmitter::class.java) .emit("onEventEmit", params)
JS Side
import { DeviceEventEmitter } from 'react-native'; ... useEffect(() => { const onEventEmit = (event) => { console.log(event); }; DeviceEventEmitter.addListener('onEventEmit', onEventEmit); }, []);
How to override Activity life cycle in this native UI
Extend the view with LifecycleEventListener
class RNViewExample(reactContext: ReactApplicationContext): LinearLayout, LifecycleEventListener { // ... override fun onHostResume() { } override fun onHostPause() { } override fun onHostDestroy() { stopPlaying() } }
Leave a Reply