react native 调用原生UI组件

来源:互联网 发布:淘宝店铺怎样加粉丝 编辑:程序博客网 时间:2024/05/16 12:16

在React Native开发过程中,有时我们想要使用原生的一个UI组件或者是js比较难以实现的功能时,我们可以在react Naitve应用程序中封装和植入已有的原生组件。 本文我们实现一个VideoView的本地调用。
React Native并没有给我们提供VideoView这个组件,那我们要播放视频的话,有两种方法:一种是借助WebView,一种就是使用原生的播放器。

Java端实现

新建VideoViewManager类,并继承SimpleViewManager,SimpleViewManager类需要传入一个泛型,该泛型继承Android的View,也就是说该泛型是要使用android 平台的哪个View就传入该View,比如,我要使用android的VideoView,这个泛型就传入VideoView。相关的代码如下:

public class VideoViewManager extends SimpleViewManager<VideoView>{    @Override    public String getName() {//组件名称        return "VideoView";    }    @Override    protected VideoView createViewInstance(ThemedReactContext reactContext) {        VideoView video = new VideoView(reactContext);        return video;    }}

getName返回组件名称(可以加前缀RCT),createViewInstance方法返回实例对象,可以在初始化对象时设置一些属性。
其中,可以通过@ReactProp(或@ReactPropGroup)注解来导出属性的设置方法。该方法有两个参数,第一个参数是泛型View的实例对象,第二个参数是要设置的属性值。方法的返回值类型必须为void,而且访问控制必须被声明为public。组件的每一个属性的设置都会调用Java层被对应ReactProp注解的方法。

@ReactProp(name = "source")public void setSource(RCTVideoView videoView,@Nullable String source){    if(source != null){        videoView.setVideoURI(Uri.parse(source));        videoView.start();    }}

@ReactProp注解必须包含一个字符串类型的参数name。这个参数指定了对应属性在JavaScript端的名字。那么现在JS端可以这么设置source属性值。
但是在设置播放地址的时候,我们可能需要同时设置header信息,所以对上面的代码优化如下:

@ReactProp(name = "source")public void setSource(VideoView videoView,@Nullable ReadableMap source){    if(source != null){        if (source.hasKey("url")) {            String url = source.getString("url");            FLog.e(VideoViewManager.class,"url = "+url);            HashMap<String, String> headerMap = new HashMap<>();            if (source.hasKey("headers")) {                ReadableMap headers = source.getMap("headers");                ReadableMapKeySetIterator iter = headers.keySetIterator();                while (iter.hasNextKey()) {                    String key = iter.nextKey();                    String value = headers.getString(key);                    FLog.e(VideoViewManager.class,key+" = "+value);                    headerMap.put(key,value);                }            }            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {                videoView.setVideoURI(Uri.parse(url),headerMap);            }else{                try {                    Method setVideoURIMethod = videoView.getClass().getMethod("setVideoURI", Uri.class, Map.class);                    setVideoURIMethod.invoke(videoView, Uri.parse(url), headerMap);                } catch (Exception e) {                    e.printStackTrace();                }            }            videoView.start();        }    }}

VideoViewManager类的完整代码如下:

public class VideoViewManager extends SimpleViewManager<VideoView>{    @Override    public String getName() {        return "VideoView";    }    @Override    protected VideoView createViewInstance(ThemedReactContext reactContext) {        VideoView video = new VideoView(reactContext);        return video;    }    @Override    public void onDropViewInstance(VideoView view) {//对象销毁时        super.onDropViewInstance(view);         view.stopPlayback();//停止播放    }    @ReactProp(name = "source")    public void setSource(VideoView videoView,@Nullable ReadableMap source){        if(source != null){            if (source.hasKey("url")) {                String url = source.getString("url");                System.out.println("url = "+url);                HashMap<String, String> headerMap = new HashMap<>();                if (source.hasKey("headers")) {                    ReadableMap headers = source.getMap("headers");                    ReadableMapKeySetIterator iter = headers.keySetIterator();                    while (iter.hasNextKey()) {                        String key = iter.nextKey();                        headerMap.put(key, headers.getString(key));                    }                }                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {                    videoView.setVideoURI(Uri.parse(url),headerMap);                }else{                    try {                        Method setVideoURIMethod = videoView.getClass().getMethod("setVideoURI", Uri.class, Map.class);                        setVideoURIMethod.invoke(videoView, Uri.parse(url), headerMap);                    } catch (Exception e) {                        e.printStackTrace();                    }                }                videoView.start();            }        }    }}

接着我们需要将UI组件注册到系统中去。创建VideoViewPackage,并注册到ReactNativeHost。

public class VideoViewPackage implements ReactPackage {    @Override    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {        return Collections.emptyList();    }    @Override    public List<Class<? extends JavaScriptModule>> createJSModules() {        return Collections.emptyList();    }    @Override    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {        return Arrays.<ViewManager>asList(                new VideoViewManager()        );    }}

然后向Application注册,以前的版本是向MainActivity注册。

@Overrideprotected List<ReactPackage> getPackages() {    return Arrays.<ReactPackage>asList(            new MainReactPackage(),            new OrientationPackage(),            new VideoViewPackage()    );}

Js端实现

在项目js/component文件夹下新建VideoView.js。代码如下:

import React,{ PropTypes }from 'react';import {requireNativeComponent,View} from 'react-native';var VideoView = {    name:'VideoView',    propTypes:{        style: View.propTypes.style,        source:PropTypes.shape({            url:PropTypes.string,            headers:PropTypes.object,        }),        ...View.propTypes,//包含默认的View的属性,如果没有这句会报‘has no propType for native prop’错误    }};var RCTVideoView = requireNativeComponent('VideoView',VideoView);module.exports = RCTVideoView;

然后我们直接使用即可。调用代码如下:

import React, {Component} from 'react';import {    View,    StyleSheet,    Text,    TouchableOpacity} from 'react-native';import VideoView from './component/VideoView';export default class VideoPlayView extends Component {    constructor(props) {        super(props);    }    render() {        return (            <View style={styles.videoContainer}>                <Text style={styles.text}>康熙王朝</Text>                <VideoView                    style={styles.video}                    source={                           {                            url: 'http://ohe65w0xx.bkt.clouddn.com/test3.mp4',                            headers: {                                'refer': 'myRefer'                            }                        }                    }                />            </View>        );    }}const styles = StyleSheet.create({    videoContainer: {        flex: 1,        justifyContent: 'center',        alignItems: 'center',    },    text: {        fontSize: 20,        justifyContent: 'center',    },    video: {        marginTop:10,        height: 250,        width: 380    },});

最终运行效果:
这里写图片描述

到此,React Native调用原生组件就基本实现了,不过,native层的一些信息我们还无法获取到,比如:视频的总时长、视频当前播放的时间点等。所以我们希望实现相关的功能。

native层向js发送消息事件

声明一个VideoViewManager的内部类RCTVideoView,它继承VideoView,并实现了一些必要的接口。

private static class RCTVideoView extends VideoView implements LifecycleEventListener,        MediaPlayer.OnPreparedListener,        MediaPlayer.OnCompletionListener,        MediaPlayer.OnErrorListener,        MediaPlayer.OnInfoListener,MediaPlayer.OnBufferingUpdateListener{    public RCTVideoView(ThemedReactContext reactContext) {        super(reactContext);        reactContext.addLifecycleEventListener(this);        setOnPreparedListener(this);        setOnCompletionListener(this);        setOnErrorListener(this);    }    @Override    public void onHostResume() {        FLog.e(VideoViewManager.class,"onHostResume");    }    @Override    public void onHostPause() {        FLog.e(VideoViewManager.class,"onHostPause");        pause();    }    @Override    public void onHostDestroy() {        FLog.e(VideoViewManager.class,"onHostDestroy");    }    @Override    public void onPrepared(MediaPlayer mp) {//视频加载成功准备播放        FLog.e(VideoViewManager.class,"onPrepared duration = "+mp.getDuration());        mp.setOnInfoListener(this);        mp.setOnBufferingUpdateListener(this);    }    @Override    public void onCompletion(MediaPlayer mp) {//视频播放结束        FLog.e(VideoViewManager.class,"onCompletion");    }    @Override    public boolean onError(MediaPlayer mp, int what, int extra) {//视频播放出错        FLog.e(VideoViewManager.class,"onError what = "+ what+" extra = "+extra);        return false;    }    @Override    public boolean onInfo(MediaPlayer mp, int what, int extra) {        FLog.e(VideoViewManager.class,"onInfo");        switch (what) {            /**             * 开始缓冲             */            case MediaPlayer.MEDIA_INFO_BUFFERING_START:                FLog.e(VideoViewManager.class,"开始缓冲");                break;            /**             * 结束缓冲             */            case MediaPlayer.MEDIA_INFO_BUFFERING_END:                FLog.e(VideoViewManager.class,"结束缓冲");                break;            /**             * 开始渲染视频第一帧画面             */            case MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START:                FLog.e(VideoViewManager.class,"开始渲染视频第一帧画面");                break;            default:                break;        }        return false;    }    @Override    public void onBufferingUpdate(MediaPlayer mp, int percent) {//视频缓冲进度        FLog.e(VideoViewManager.class,"onBufferingUpdate percent = "+percent);    }}

接着我们在java层的onPrepared方法中获取视频播放时长,并想js发送事件通知。

@Overridepublic void onPrepared(MediaPlayer mp) {//视频加载成功准备播放    int duration = mp.getDuration();    FLog.e(VideoViewManager.class,"onPrepared duration = "+duration);    mp.setOnInfoListener(this);    mp.setOnBufferingUpdateListener(this);    //向js发送事件    WritableMap event = Arguments.createMap();    event.putInt("duration",duration);//key用于js中的nativeEvent    ReactContext reactContext = (ReactContext) getContext();    reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(                    getId(),//native层和js层两个视图会依据getId()而关联在一起                    "topChange",//事件名称                    event//事件携带的数据            );}

receiveEvent接收三个参数,参数说明如注释所示,这个事件名topChange在JavaScript端映射到onChange回调属性上(这个映射关系在UIManagerModuleConstants.java文件里),这个回调会被原生事件执行。
当Js层收到通知之后,我们对VideoView.js代码进行优化。

class VideoView extends Component{    constructor(props){        super(props);    }    _onChange(event){        if(!this.props.onPrepared){            return;        }        this.props.onPrepared(event.nativeEvent.duration);    }    render(){        return <RCTVideoView {...this.props} onChange={this._onChange.bind(this)}/>;    };}VideoView.name = "VideoView";VideoView.propTypes = {    onPrepared:PropTypes.func,    style: View.propTypes.style,    source:PropTypes.shape({        url:PropTypes.string,        headers:PropTypes.object,    }),    ...View.propTypes,};//需要注意下面这两句var RCTVideoView = requireNativeComponent('VideoView',VideoView,{    nativeOnly: {onChange: true}});module.exports = VideoView;

我们在java中发送的事件中携带的数据WritableMap中,定义的key与在js中event.nativeEvent.duration一致,nativeEvent和key就可以获取到value。

有时候有一些特殊的属性,想从原生组件中导出,但是又不希望它们成为对应React封装组件的属性,可以使用nativeOnly来声明。如果没有什么特殊属性需要设置的话,requireNativeComponent第三个参数可以不用。
需要注意的是,之前VideoView.js以下两句是这样:

var RCTVideoView = requireNativeComponent('VideoView',VideoView);module.exports = RCTVideoView;

我们需要将它改为下面的这样:

var RCTVideoView = requireNativeComponent('VideoView',VideoView,{    nativeOnly: {onChange: true}});module.exports = VideoView;

如果你不小心还是使用之前exports RCTVideoView 的那样,那么会一直接收不到onChange事件的回调!
VideoView增加了onPrepared回调方法,运行程序后,可以看到打印了duration信息。但是如果native层需要发送的事件比较多的情况下,那么如果我们使用单一的topChange事件,就会导致回调的onChange不是单一职责。那么,我们是否可以自定义该事件的名称呢,使每一个事件对应各自的回调方法呢?下面我们就讲讲如何自定义事件名称。

自定义事件名称

首先,在VideoViewManager类中重写getExportedCustomDirectEventTypeConstants方法,然后自定义事件名称。

@Overridepublic Map getExportedCustomDirectEventTypeConstants() {    return MapBuilder.of(            "onCompletion", MapBuilder.of("registrationName", "onCompletion"));}

第一个onCompletion字符串是java端发送事件是的名称,即receiveEvent方法的第二个参数值;第二个onCompletion字符串是定义在js端的回调方法;registrationName字符串的值是固定的,不能修改。对比一下topChange事件就知道了。

@Overridepublic Map getExportedCustomDirectEventTypeConstants() {    return MapBuilder.of(            "topChange", MapBuilder.of("registrationName", "onChange"));}

接着,在内部类RCTVideoView的onCompletion方法发送事件。相关代码如下:

@Overridepublic void onCompletion(MediaPlayer mp) {//视频播放结束    FLog.e(VideoViewManager.class,"onCompletion");    ReactContext reactContext = (ReactContext) getContext();    reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(            getId(),//native和js两个视图会依据getId()而关联在一起            "onCompletion",//事件名称            null    );}

由于只是通知js端,告诉它播放结束,不用携带任何数据,所以receiveEvent的第三个参数为null即可。然后在VideoView.js增加propTypes属性。

VideoView.propTypes = {    onCompletion:PropTypes.func,    //省略其它代码};

最后在VideoPlayScene.js中使用VideoView时,增加onCompletion属性即可。相关的逻辑如下:

<VideoView    style={{height: 250, width: 380}}    source={    {        url: 'http://qiubai-video.qiushibaike.com/A14EXG7JQ53PYURP.mp4',        headers: {            'refer': 'myRefer'        }    }    }    onPrepared={this._onPrepared}    onCompletion={()=>{        console.log("JS onCompletion");    }}/>

当我们运行时,在浏览器就可以看到相关的打印日志。
这里写图片描述

其他的事件的定义流程都一样,比如获取当前进度信息、缓存进度、错误回调等。然后,我们看看VideoViewManager的完整实现。

public class VideoViewManager extends SimpleViewManager<VideoView>{    private enum VideoEvent{        EVENT_PREPARE("onPrepared"),        EVENT_PROGRESS("onProgress"),        EVENT_UPDATE("onBufferUpdate"),        EVENT_ERROR("onError"),        EVENT_COMPLETION("onCompletion");        private String mName;        VideoEvent(String name) {            this.mName = name;        }        @Override        public String toString() {            return mName;        }    }    @Override    public String getName() {        return "VideoView";    }    @Override    protected VideoView createViewInstance(ThemedReactContext reactContext) {        RCTVideoView video = new RCTVideoView(reactContext);        return video;    }    @Nullable    @Override    public Map<String, Integer> getCommandsMap() {        return super.getCommandsMap();    }    @Override    public void receiveCommand(VideoView root, int commandId, @Nullable ReadableArray args) {        super.receiveCommand(root, commandId, args);    }    @Nullable    @Override    public Map<String, Object> getExportedCustomDirectEventTypeConstants() {        MapBuilder.Builder<String, Object> builder = MapBuilder.builder();        for (VideoEvent event:VideoEvent.values()){            builder.put(event.toString(),MapBuilder.of("registrationName", event.toString()));        }        return builder.build();    }    @Override    public void onDropViewInstance(VideoView view) {//销毁对象时释放一些资源        super.onDropViewInstance(view);        ((ThemedReactContext) view.getContext()).removeLifecycleEventListener((RCTVideoView) view);         view.stopPlayback();    }    @ReactProp(name = "source")    public void setSource(RCTVideoView videoView,@Nullable ReadableMap source){        if(source != null){            if (source.hasKey("url")) {                String url = source.getString("url");                FLog.e(VideoViewManager.class,"url = "+url);                HashMap<String, String> headerMap = new HashMap<>();                if (source.hasKey("headers")) {                    ReadableMap headers = source.getMap("headers");                    ReadableMapKeySetIterator iter = headers.keySetIterator();                    while (iter.hasNextKey()) {                        String key = iter.nextKey();                        String value = headers.getString(key);                        FLog.e(VideoViewManager.class,key+" = "+value);                        headerMap.put(key,value);                    }                }                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {                    videoView.setVideoURI(Uri.parse(url),headerMap);                }else{                    try {                        Method setVideoURIMethod = videoView.getClass().getMethod("setVideoURI", Uri.class, Map.class);                        setVideoURIMethod.invoke(videoView, Uri.parse(url), headerMap);                    } catch (Exception e) {                        e.printStackTrace();                    }                }                videoView.start();            }        }    }    private static class RCTVideoView extends VideoView implements LifecycleEventListener,            MediaPlayer.OnPreparedListener,            MediaPlayer.OnCompletionListener,            MediaPlayer.OnErrorListener,            MediaPlayer.OnInfoListener,            MediaPlayer.OnBufferingUpdateListener,            Runnable{        private Handler mHandler;        public RCTVideoView(ThemedReactContext reactContext) {            super(reactContext);            reactContext.addLifecycleEventListener(this);            setOnPreparedListener(this);            setOnCompletionListener(this);            setOnErrorListener(this);            mHandler = new Handler();        }        @Override        public void onHostResume() {            FLog.e(VideoViewManager.class,"onHostResume");        }        @Override        public void onHostPause() {            FLog.e(VideoViewManager.class,"onHostPause");            pause();        }        @Override        public void onHostDestroy() {            FLog.e(VideoViewManager.class,"onHostDestroy");            mHandler.removeCallbacks(this);        }        @Override        public void onPrepared(MediaPlayer mp) {//视频加载成功准备播放            int duration = mp.getDuration();            FLog.e(VideoViewManager.class,"onPrepared duration = "+duration);            mp.setOnInfoListener(this);            mp.setOnBufferingUpdateListener(this);            WritableMap event = Arguments.createMap();            event.putInt("duration",duration);//key用于js中的nativeEvent            dispatchEvent(VideoEvent.EVENT_PREPARE.toString(),event);            mHandler.post(this);        }        @Override        public void onCompletion(MediaPlayer mp) {//视频播放结束            FLog.e(VideoViewManager.class,"onCompletion");            dispatchEvent(VideoEvent.EVENT_COMPLETION.toString(),null);            mHandler.removeCallbacks(this);            int progress = getDuration();            WritableMap event = Arguments.createMap();            event.putInt("progress",progress);            dispatchEvent(VideoEvent.EVENT_PROGRESS.toString(),event);        }        @Override        public boolean onError(MediaPlayer mp, int what, int extra) {//视频播放出错            FLog.e(VideoViewManager.class,"onError what = "+ what+" extra = "+extra);            mHandler.removeCallbacks(this);            WritableMap event = Arguments.createMap();            event.putInt("what",what);            event.putInt("extra",what);            dispatchEvent(VideoEvent.EVENT_ERROR.toString(),event);            return true;        }        @Override        public boolean onInfo(MediaPlayer mp, int what, int extra) {            FLog.e(VideoViewManager.class,"onInfo");            switch (what) {                /**                 * 开始缓冲                 */                case MediaPlayer.MEDIA_INFO_BUFFERING_START:                    FLog.e(VideoViewManager.class,"开始缓冲");                    break;                /**                 * 结束缓冲                 */                case MediaPlayer.MEDIA_INFO_BUFFERING_END:                    FLog.e(VideoViewManager.class,"结束缓冲");                    break;                /**                 * 开始渲染视频第一帧画面                 */                case MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START:                    FLog.e(VideoViewManager.class,"开始渲染视频第一帧画面");                    break;                default:                    break;            }            return false;        }        @Override        public void onBufferingUpdate(MediaPlayer mp, int percent) {//视频缓冲进度            FLog.e(VideoViewManager.class,"onBufferingUpdate percent = "+percent);            int buffer = (int) Math.round((double) (mp.getDuration() * percent) / 100.0);            WritableMap event = Arguments.createMap();            event.putInt("buffer",buffer);            dispatchEvent(VideoEvent.EVENT_UPDATE.toString(),event);        }        @Override        public void run() {            int progress = getCurrentPosition();            WritableMap event = Arguments.createMap();            event.putInt("progress",progress);            dispatchEvent(VideoEvent.EVENT_PROGRESS.toString(),event);            mHandler.postDelayed(this,1000);        }        private void dispatchEvent(String eventName,WritableMap eventData){            ReactContext reactContext = (ReactContext) getContext();            reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(                    getId(),//native和js两个视图会依据getId()而关联在一起                    eventName,//事件名称                    eventData            );        }    }}

对于VideoView.js我们改造如下:

class VideoView extends Component{    constructor(props){        super(props);    }    /*_onChange(event){        if(!this.props.onPrepared){            return;        }        this.props.onPrepared(event.nativeEvent.duration);    }*/    _onPrepared(event){        if(!this.props.onPrepared){            return;        }        this.props.onPrepared(event.nativeEvent.duration);    }    _onError(event){        if(!this.props.onError){            return;        }        this.props.onError(event.nativeEvent);    }    _onBufferUpdate(event){        if(!this.props.onBufferUpdate){            return;        }        this.props.onBufferUpdate(event.nativeEvent.buffer);    }    _onProgress(event){        if(!this.props.onProgress){            return;        }        this.props.onProgress(event.nativeEvent.progress);    }    render(){        //return <RCTVideoView {...this.props} onChange={this._onChange.bind(this)}/>;        return <RCTVideoView            {...this.props}            onPrepared={this._onPrepared.bind(this)}            onError={this._onError.bind(this)}            onBufferUpdate={this._onBufferUpdate.bind(this)}            onProgress={this._onProgress.bind(this)}        />;    };}VideoView.name = "VideoView";VideoView.propTypes = {    onPrepared:PropTypes.func,    onCompletion:PropTypes.func,    onError:PropTypes.func,    onBufferUpdate:PropTypes.func,    onProgress:PropTypes.func,    style: View.propTypes.style,    source:PropTypes.shape({        url:PropTypes.string,        headers:PropTypes.object,    }),    ...View.propTypes,};var RCTVideoView = requireNativeComponent('VideoView',VideoView,{    nativeOnly: {onChange: true}});module.exports = VideoView;

VideoView的使用(省略其它代码),VideoPlayScene.js

<VideoView    style={{height: 250, width: 380}}    source={    {        url: 'http://qiubai-video.qiushibaike.com/A14EXG7JQ53PYURP.mp4',        headers: {            'refer': 'myRefer'        }    }    }    onPrepared={this._onPrepared}    onCompletion={()=>{        console.log("JS onCompletion");    }}    onError={(e)=>{        console.log("what="+e.what+" extra="+e.extra);    }}    onBufferUpdate={(buffer)=>{        console.log("JS buffer = "+buffer);    }}    onProgress={(progress)=>{        console.log("JS progress = "+progress);    }}/>

js层向native层发送命令

讲完native层向js发送事件后,那么js如何向native命令呢?继续往下看。比如在js端我想通过点击某个按钮,来控制视频暂停,那么就需要native层来响应这个操作,因为native掌握着VideoView的所有权,暂停可以通过调用VideoView对象的pause方法。首先,我们需要在native层定义这些命令,并在接收到命令时处理相关操作。
在VideoViewManager重写getCommandsMap方法。

private static final int COMMAND_PAUSE_ID = 1;private static final String COMMAND_PAUSE_NAME = "pause";@Overridepublic Map<String, Integer> getCommandsMap() {    return MapBuilder.of(            COMMAND_PAUSE_NAME,COMMAND_PAUSE_ID    );}

getCommandsMap接收多组命令,每组命令需要包括名称(js端调用的方法名)和命令id,如上面的COMMAND_PAUSE_NAME 和 COMMAND_PAUSE_ID。
然后重写receiveCommand方法,处理相应的命令。

@Overridepublic void receiveCommand(VideoView root, int commandId, @Nullable ReadableArray args) {    switch (commandId){        case COMMAND_PAUSE_ID:            root.pause();            break;        default:            break;    }}

Native端在接收到COMMAND_PAUSE_ID 命令时,调用了VideoView的pause方法进行暂停播放。
接下来就是js端如何发起该命令了,打开VideoView.js,添加如下代码:

import {    requireNativeComponent,    View,    UIManager,    findNodeHandle,} from 'react-native';var RCT_VIDEO_REF = 'VideoView';class VideoView extends Component{    //省略其它代码    pause(){        //向native层发送命令        UIManager.dispatchViewManagerCommand(            findNodeHandle(this.refs[RCT_VIDEO_REF]),            UIManager.VideoView.Commands.pause,//Commands.pause与native层定义的COMMAND_PAUSE_NAME一致            null//命令携带的参数数据        );    }    render(){        return <RCTVideoView            ref = {RCT_VIDEO_REF}            //省略其它代码        />;    };}

主要是定义了一个pause函数,该函数内使用UIManager.dispatchViewManagerCommand向native层发送命令,该方法接收三个参数:第一个参数是组件的实例对象;第二个是发送的命令名称,与native层定义的command name一致;第三个是命令携带的参数数据。
打开VideoPlayScene.js,给视频播放添加暂停功能。

export default class VideoPlayScene extends Component {    //暂停播放    _onPressPause(){        this.video.pause();    }    render() {        return (            <View style={{flex: 1,justifyContent: 'center',}}>                <VideoView                    ref={(video)=>{this.video = video}}                    //省略其它代码                />                <View style={{height:50,flexDirection:'row',justifyContent:'flex-start'}}>                    <Text style={{width:100}}>{this.state.time}/{this.state.totalTime}</Text>                    <TouchableOpacity style={{marginLeft:10}} onPress={this._onPressPause.bind(this)}>                        <Text>暂停</Text>                    </TouchableOpacity>                </View>            </View>        );    }}

运行程序,你发现已经可以暂停播放了。同样的流程,我们再给播放器添加‘开始播放’的功能。开发VideoViewManager.java,添加开始播放功能。

private static final int COMMAND_START_ID = 2;private static final String COMMAND_START_NAME = "start";@Overridepublic Map<String, Integer> getCommandsMap() {    return MapBuilder.of(            COMMAND_PAUSE_NAME,COMMAND_PAUSE_ID,            COMMAND_START_NAME,COMMAND_START_ID);}@Overridepublic void receiveCommand(VideoView root, int commandId, @Nullable ReadableArray args) {    FLog.e(VideoViewManager.class,"receiveCommand id = "+commandId);    switch (commandId){        case COMMAND_PAUSE_ID:            root.pause();            break;        case COMMAND_START_ID:            root.start();            break;        default:            break;    }}

VideoView.js 添加开始播放的代码

start(){    UIManager.dispatchViewManagerCommand(        findNodeHandle(this.refs[RCT_VIDEO_REF]),        UIManager.VideoView.Commands.start,        null    );}

在VideoPlayScene.js添加开始播放的功能。

_onPressPause(){    this.video.pause();}_onPressStart(){    this.video.start();}render() {    return (        <View style={{flex: 1,justifyContent: 'center',}}>            <VideoView                ref={(video)=>{this.video = video}}                //省略其它代码            />            <View style={{height:50,flexDirection:'row',justifyContent:'flex-start'}}>                <Text style={{width:100}}>{this.state.time}/{this.state.totalTime}</Text>                <TouchableOpacity style={{marginLeft:10}} onPress={this._onPressPause.bind(this)}>                    <Text>暂停</Text>                </TouchableOpacity>                <TouchableOpacity style={{marginLeft:10}} onPress={this._onPressStart.bind(this)}>                    <Text>开始</Text>                </TouchableOpacity>            </View>        </View>    );}

然后再次运行,效果如下:
这里写图片描述

ok,上面的pause和start方法都是没有带参数的,那么如果native层需要参数呢,比如seekTo(快进),这个方法需要有一个参数,设置视频快进到的位置,那么如何处理呢?关于快进等功能的实现,请访问项目稀饭APP源码

原创粉丝点击