博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
React Native 原生视图封装全解析:视频播放器示例
阅读量:6602 次
发布时间:2019-06-24

本文共 8477 字,大约阅读时间需要 28 分钟。

以视频播放器为例,封装一个可供android和ios使用的react native视频播放组件,展现基本上React Native封装原生组件会需要用到的全部。以使用方法简单的支持多平台使用的七牛播放器第三方库视频库导出到React Native使用。

android

依赖安装

官方github,查看其相关文档,把jar和so下载复制进项目中。

实现

自定义视频播放器view

在android视图渲染机制中,子视图改变大小,事件一直冒泡到根视图被处理,而在react native中根视图的处理方法是空的,即不做任何处理,所以在view中如果要改变视图大小,必须手动在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() {        seekTo(0);        MyPLVideoView.this.start();        sendEvent("onPlayEnd", null);      }    });  }@Overridepublic void requestLayout() {  super.requestLayout();  // 避免在切换分辨率后无法正常  reLayout();}  public void reLayout() {    if (getWidth() > 0 && getHeight() > 0) {      int w = MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY);      int h = MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY);      measure(w, h);      layout(getPaddingLeft() + getLeft(), getPaddingTop() + getTop(), getWidth() + getPaddingLeft() + getLeft(), getHeight() + getPaddingTop() + getTop());    }  }  // 事件发送  public void sendEvent(String name, @Nullable WritableMap event) {    ReactContext reactContext = (ReactContext) getContext();    reactContext.getJSModule(RCTEventEmitter.class)        .receiveEvent(getId(), name, event);  }}复制代码

视图中需要暴露给视图管理器相关的方法,在更新prop时调用,如需要发送事件到js端,则需要使用RCTEventEmitter,该方法在视图中封装。

视图管理器

ViewGroupManager用于容器视图,其提供addView等方法,SimpleViewManager用于普通视图,视图管理器主要导出视图props,提供js -> native调用,native -> js调用。

@ReactProp注解导出prop,在组件设置或者修改prop时会调用该函数,第一个参数为当前视图,第二个参数为prop的值。

getName返回组件名,在js层用这个名称来找到native组件。

native -> js: prop类型为函数的需在getExportedCustomDirectEventTypeConstants注册,在触发回调时sendEvent。

js -> native: ref的方法在getCommandsMap中注册,在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
{ private static final String TAG = "PLVideoViewManager"; @Override public String getName() { return "RTCPLVideo"; } @Override protected MyPLVideoView createViewInstance(ThemedReactContext reactContext) { return new MyPLVideoView(reactContext); } // 视频uri prop @ReactProp(name = "uri") public void uri(MyPLVideoView root, String uri) { root.setVideoURI(Uri.parse(uri)); } // 视频暂停 prop @ReactProp(name = "paused") public void paused(MyPLVideoView root, Boolean paused) { if (paused) { root.pause(); } else { root.start(); } } @Nullable @Override public Map
getCommandsMap() { Map
commandsMap = new HashMap<>(); // ref方法注册 commandsMap.put("stop", 1); return commandsMap; } @Override public void receiveCommand(MyPLVideoView root, int commandId, @Nullable ReadableArray args) { switch (commandId) { case 1: // 停止播放,释放播放器 root.stopPlayback(); break; } } @Nullable @Override public Map
getExportedCustomDirectEventTypeConstants() { MapBuilder.Builder
builder = MapBuilder.builder(); // prop函数注册 String[] events = { "onPlayEnd" }; for (String event: events) { builder.put(event, MapBuilder.of("registrationName", event)); } return builder.build(); }}复制代码

视图导出

public class MyReactPackage implements ReactPackage {  @Override  public List
createViewManagers(ReactApplicationContext reactContext) { return Arrays.
asList( new PLVideoViewManager() ); }}复制代码

包导出

public class MainApplication extends Application implements ReactApplication {  @Override    protected List
getPackages() { return Arrays.asList( new MyReactPackage() );}复制代码

ios

依赖安装

官方github,查看其集成说明,使用pod或手动集成。

实现

视图

.h

#import 
#import
#import
#import
@class RCTEventDispatcher;@interface RTCPLVideo : UIView
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;// prop函数@property (nonatomic, copy) RCTBubblingEventBlock onPlayEnd;- (void) stop;@end复制代码

.m

#import "RTCPLVideo.h"@interface RTCPLVideo()
@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) { // 调用prop函数 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]; }}@end复制代码

视图管理

.h

#import 
@interface RTCPLVideoManager : RCTViewManager@end复制代码

.m

#import "RTCPLVideoManager.h"#import "RTCPLVideo.h"@implementation RTCPLVideoManager// 导出模块RCT_EXPORT_MODULE()//导出propRCT_EXPORT_VIEW_PROPERTY(onPlayEnd, RCTBubblingEventBlock)RCT_EXPORT_VIEW_PROPERTY(uri, NSString)RCT_EXPORT_VIEW_PROPERTY(paused, BOOL)- (UIView *)view{  return [[RTCPLVideo alloc] initWithEventDispatcher:self.bridge.eventDispatcher];}typedef void(^js_call_black)(RTCPLVideo *view);// js -> native调用不在主线程,执行view相关方法需要切到主线程- (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) {    // 执行相应方法  }];}@end复制代码

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
('RTCPLVideo');export default class PLVideo extends React.Component
{ private plVideo?: any; private callNative(name: string, args: Array
= []) { 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 (
this.plVideo = plVideo!} {...this.props}/> ); }}复制代码

总结

在React Native原生视图封装中,知道prop导出、js -> native、native -> js就能封装导出绝大部分的原生组件。

转载地址:http://ddzso.baihongyu.com/

你可能感兴趣的文章
RHEL/Centos7新功能
查看>>
Drupal 7 数据库 操作
查看>>
第一部分 思科九年 一(1)
查看>>
DBA日常工作职责
查看>>
Redis的持久化
查看>>
linux安装NFS服务器学习
查看>>
Planner .NET日历日程控件能给你的应用程序提供多种日历日程功能
查看>>
我的友情链接
查看>>
Linux压力测试
查看>>
JAVA中的线程机制(二)
查看>>
nginx安装与配置2(转载)
查看>>
Linux下Mongodb安装和启动配置
查看>>
2015 成长计划
查看>>
沈阳一饭店凌晨爆燃,燃气报警器时刻预防
查看>>
Redis 与 数据库处理数据的两种模式
查看>>
VUE2中axios的使用方法
查看>>
assert 断言
查看>>
CS 229 notes Supervised Learning
查看>>
2018.10.27-dtoj-3996-Lesson5!(johnny)
查看>>
DataTable转换成json字符串
查看>>