Take the Video player as an example and package a React Native video player component for Android and ios to show basically everything that a React Native component needs. React Native uses the React Native video library, which supports multiple platforms and is easy to use.
android
Depend on the installation
Official githubPLDroidPlayer, check its related documentation, download jar and so copy into the project.
implementation
Customize video player view
In Android view rendering, subviews change size and events bubble up until the root view is processed. In React Native, the root view handles nothing, so if you want to resize a view, you have to manually resize it in requestLayout.
import android.content.Context;
import android.util.Log;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.uimanager.events.RCTEventEmitter;
import com.pili.pldroid.player.PLOnCompletionListener;
import com.pili.pldroid.player.PLOnPreparedListener;
import com.pili.pldroid.player.widget.PLVideoView;
import javax.annotation.Nullable;
public class MyPLVideoView extends PLVideoView {
private final static String TAG = "MyPLVideoView";
public MyPLVideoView(Context context) {
super(context);
setOnPreparedListener(new PLOnPreparedListener() {
@Override
public void onPrepared(int i) { reLayout(); }}); setOnCompletionListener(new PLOnCompletionListener() {
@Override
public void onCompletion(a) {
seekTo(0);
MyPLVideoView.this.start();
sendEvent("onPlayEnd".null); }}); }@Override
public void requestLayout(a) {
super.requestLayout();
// Avoid not working properly after switching resolution
reLayout();
}
public void reLayout(a) {
if (getWidth() > 0 && getHeight() > 0) {
int w = MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY);
inth = MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY); measure(w, h); layout(getPaddingLeft() + getLeft(), getPaddingTop() + getTop(), getWidth() + getPaddingLeft() + getLeft(), getHeight() + getPaddingTop() + getTop()); }}// The event is sent
public void sendEvent(String name, @Nullable WritableMap event) { ReactContext reactContext = (ReactContext) getContext(); reactContext.getJSModule(RCTEventEmitter.class) .receiveEvent(getId(), name, event); }}Copy the code
Methods that need to be exposed to the view manager need to be called when updating prop. If you need to send events to the JS side, you need to use RCTEventEmitter, which is encapsulated in the view.
View manager
ViewGroupManager is used for container views and provides addView and other methods. SimpleViewManager is used for general views. The view manager mainly exports view props and provides JS -> native calls and native -> JS calls.
The @reactProp annotation exports a prop and calls this function when the component sets or modifies the prop. The first argument is the current view and the second argument is the value of the prop.
GetName returns the component name, which is used to find native components in the JS layer.
Native – > js: prop type for functions need to be registered in getExportedCustomDirectEventTypeConstants adds when triggered the callback.
Js -> native: ref methods are registered in getCommandsMap and handled in receiveCommand.
import android.net.Uri;
import android.util.Log;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewGroupManager;
import com.facebook.react.uimanager.annotations.ReactProp;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;
public class PLVideoViewManager extends SimpleViewManager<MyPLVideoView> {
private static final String TAG = "PLVideoViewManager";
@Override
public String getName(a) {
return "RTCPLVideo";
}
@Override
protected MyPLVideoView createViewInstance(ThemedReactContext reactContext) {
return new MyPLVideoView(reactContext);
}
// Video URI prop
@ReactProp(name = "uri")
public void uri(MyPLVideoView root, String uri) {
root.setVideoURI(Uri.parse(uri));
}
// Video pause prop
@ReactProp(name = "paused")
public void paused(MyPLVideoView root, Boolean paused) {
if (paused) {
root.pause();
} else{ root.start(); }}@Nullable
@Override
public Map<String, Integer> getCommandsMap(a) {
Map<String, Integer> commandsMap = new HashMap<>();
// ref method registration
commandsMap.put("stop".1);
return commandsMap;
}
@Override
public void receiveCommand(MyPLVideoView root, int commandId, @Nullable ReadableArray args) {
switch (commandId) {
case 1:
// Stop playing and release the player
root.stopPlayback();
break; }}@Nullable
@Override
public Map<String, Object> getExportedCustomDirectEventTypeConstants(a) {
MapBuilder.Builder<String, Object> builder = MapBuilder.builder();
// prop function registration
String[] events = {
"onPlayEnd"
};
for (String event: events) {
builder.put(event, MapBuilder.of("registrationName", event));
}
returnbuilder.build(); }}Copy the code
View the export
public class MyReactPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList(
newPLVideoViewManager() ); }}Copy the code
Package export
public class MainApplication extends Application implements ReactApplication {
@Override
protected List<ReactPackage> getPackages(a) {
return Arrays.asList(
new MyReactPackage()
);
}
Copy the code
ios
Depend on the installation
Official githubPLPlayerKit, check its integration instructions, use POD or manual integration.
implementation
view
.h
#import <Foundation/Foundation.h> #import <UIKit/UIKit.h> #import <React/RCTView.h> #import <PLPlayerKit/PLPlayerKit.h> @class RCTEventDispatcher; @interface RTCPLVideo : UIView<PLPlayerDelegate> - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; // prop @Property (nonatomic, copy) RCTBubblingEventBlock onPlayEnd; - (void) stop; @endCopy the code
.m
#import "RTCPLVideo.h" @interface RTCPLVideo()<PLPlayerDelegate> @property (nonatomic, strong) PLPlayer *player; @end @implementation RTCPLVideo { RCTEventDispatcher *_eventDispatcher; } - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher { if ((self = [super init])) { } return self; } - (void)player:(nonnull PLPlayer *)player statusDidChange:(PLPlayerStatus)state { if (state == PLPlayerStatusCompleted) { CMTime start = CMTimeMakeWithSeconds(0, 600); [self.player seekTo: start]; If (self.onPlayEnd) {// Call prop function self.onPlayEnd(@{}); } } } - (void) setUri:(NSString *) uri { NSURL *url = [NSURL URLWithString:uri]; if (self.player == nil) { PLPlayerOption *option = [PLPlayerOption defaultOption]; [option setOptionValue:@15 forKey:PLPlayerOptionKeyTimeoutIntervalForMediaPackets]; self.player = [PLPlayer playerWithURL:url option:option]; self.player.delegate = self; [self addSubview:self.player.playerView]; [self.player play]; } else { [self.player playWithURL:url sameSource:NO]; } } - (void) setPaused: (BOOL) paused { if (self.player) { if (paused) { [self.player pause]; } else { [self.player play]; } } } - (void) cache:(NSString *)url { if (self.player) { NSURL *uri = [NSURL URLWithString:url]; [self.player openPlayerWithURL:uri]; } } - (void) stop { if (self.player) { [self.player stop]; } } @endCopy the code
View management
.h
#import <React/RCTUIManager.h>
@interface RTCPLVideoManager : RCTViewManager
@end
Copy the code
.m
#import "rtcplvideomanager.h" #import "rtcplvideo.h" @implementation RTCPLVideoManager // Export prop RCT_EXPORT_VIEW_PROPERTY(onPlayEnd, RCTBubblingEventBlock) NSString) RCT_EXPORT_VIEW_PROPERTY(paused, BOOL) - (UIView *)view { return [[RTCPLVideo alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; } typedef void(^js_call_black)(RTCPLVideo *view); Void js_call: (NSNumber *) node black: void js_call: (NSNumber *) node black: (js_call_black) black { dispatch_async(dispatch_get_main_queue(), ^(){ UIView* temp = [self.bridge.uiManager viewForReactTag:node]; if ([[temp class] isEqual:[RTCPLVideo class]]) { RTCPLVideo* view = (RTCPLVideo*) temp; black(view); }}); } RCT_EXPORT_METHOD(stop: (nonnull NSNumber *) node) {[self js_call:node black:^(RTCPLVideo *view) {// execute the method}]; } @endCopy the code
typescript
import React from 'react'; import {findNodeHandle, requireNativeComponent, UIManager, ViewStyle} from 'react-native'; interface IProps { uri: string; paused: boolean; style? : ViewStyle; onPlayEnd: () => void; } const RTCPLVideo = requireNativeComponent<IProps>('RTCPLVideo'); export default class PLVideo extends React.Component<IProps> { private plVideo? : any; private callNative(name: string, args: Array<any> = []) { const commandId = (UIManager as any).RTCPLVideo.Commands[name]; (UIManager as any).dispatchViewManagerCommand(findNodeHandle(this.plVideo), commandId, args); } private stop() { this.plVideo && this.callNative('stop'); } componentWillUnmount() { this.stop(); } render() { return ( <RTCPLVideo ref={plVideo => this.plVideo = plVideo! } {... this.props}/> ); }}Copy the code
conclusion
In React Native view packages, knowing prop exports, JS -> Native, and Native -> JS can package and export most Native components.