RN如何使用原生的AndroidUI组件

RN如何使用原生的AndroidUI组件

我们做RN开发的时候,如果RN的组件不能满足要求,或者一个功能写好的android组件,直接在RN上使用,想嗲用RN的组件一样使用,这个时候就需要今天讲的知识了------自定义rn原生的android UI组件

比如我们想实现个视频播放器,但是这个播放器是android里面特定开发的,想把这个播放器给RN中以组件Component的形式调用,来我们就来看这个例子,这个例子只实现了简单的播放和暂停

一、android端代码

RNPlayer

public class RNPlayer extends FrameLayout {
    private PlayerView playerView;


    public RNPlayer(Context context) {
        super(context);
        //addTextView();
        post(new Runnable() {
            @Override
            public void run() {
                playerView=new PlayerView(getContext());
                FrameLayout.LayoutParams lp=new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT);
                playerView.setLayoutParams(lp);
                addView(playerView);
            }
        });

    }

    public void startPlayer(String url)
    {
    if (playerView!=null)
        {
            playerView.startPlayer(url);
        }

    }
    public void destory()
    {
        if (playerView!=null)
        {
            playerView.destory();
        }
    }
    public void stopPlayer()
    {
        if (playerView!=null)
        {
            playerView.stopPlayer();
        }
    }
}

PlayerView

public class PlayerView extends FrameLayout {
    private QySurfaceView streamView;
    private CustomView customView;
    private int playerId;
    private  boolean isPlaying = false;
    public PlayerView(@NonNull Context context) {
        super(context);
        addPlayerView();

    }

    public PlayerView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        addPlayerView();
    }

    public CustomView getCustomView() {
        return customView;
    }

    public void addPlayerView()
    {
        streamView = new QySurfaceView(getContext());
        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
        streamView.setLayoutParams(lp);
        addView(streamView);

        customView = new CustomView(getContext());
        customView.setLayoutParams(lp);

        addView(customView);
        setBackgroundColor(Color.BLACK);
    }
    public void startPlayer(String url){
        if (TextUtils.isEmpty(url))return;
        playerId = NativeApi.getInstance().createPlayer(url,"");
        if (playerId <= 0 || isPlaying){
            return;
        }
        streamView.setVisibility(VISIBLE);
        customView.setVisibility(VISIBLE);
        isPlaying = true;
        streamView.start(playerId);

    }

    public void stopPlayer(){
        if (isPlaying){

            NativeApi.getInstance().stopPlay(playerId);
            isPlaying = false;
            streamView.setVisibility(INVISIBLE);
            customView.setVisibility(INVISIBLE);
        }
    }
    public void destory(){
        if (isPlaying){

            streamView.destory();
            isPlaying = false;
            streamView.setVisibility(INVISIBLE);
            customView.setVisibility(INVISIBLE);
        }
    }
}

QySurfaceView

public class QySurfaceView extends SurfaceView {

    public static final String TAG="QySurfaceView";
    private SurfaceHolder holder;
    private int  playerId ;

    public QySurfaceView(Context context) {
        super(context);

        init();

    }

    public QySurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public QySurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init()
    {
        holder = getHolder();
        holder.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder surfaceHolder) {
                Log.d(TAG,"surfaceCreated");
                holder = surfaceHolder;
            }

            @Override
            public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
                Log.d(TAG,"i:"+i+ " i1:"+i1 + " i2:"+i2);
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
                destory();

            }
        });
    }



    public void start(int id){
        this.playerId = id;
        Log.d(TAG,"start() playerId="+playerId);
        if (holder==null)throw new RuntimeException("holder is null");
        NativeApi.getInstance().startPlay(playerId,holder.getSurface());
    }

    public void destory(){
        Log.d(TAG,"destory");
        NativeApi.getInstance().stopPlay(playerId);
        NativeApi.getInstance().closePlayer(playerId);

    }

}

最后NativeApi 是c++的native 方法,视频的拉去,编解码 都在这里做的,就不看了

上面的代码很简单,做过Android的同学都可以看懂,就是定义了一个android播放器视图

接下来我们来看Android和RN部分的代码

为了你module中可以引用到RN的库先导入:

implementation "com.facebook.react:react-native:+"

RNVGManager

//泛型里面是之前那个RNPlayer的类
public class RNVGManager extends ViewGroupManager<RNPlayer> {
    private static final String MANAGER_NAME = "PlayerView";
    @Nonnull
    @Override
    public String getName() {
    //这个是RN代码那边获取组件时候需要的名字,要一致,自己随便写,我们先记住这个PlayerView
        return MANAGER_NAME;
    }

    @Nonnull
    @Override
    protected RNPlayer createViewInstance(@Nonnull ThemedReactContext themedReactContext) {
    //返回RNPlayer
        RNPlayer reactViewGroup = new RNPlayer(themedReactContext);
        return reactViewGroup;
    }

	//这个关键了
	//这个是RN那边的组将 需要的props,到时候组件可以引用这个url的props了
    @ReactProp(name = "url")
    //第一个参数一定是RNPlayer,就是这个播放器的对象,第二个是props
    public void setUrl(RNPlayer player,String url)
    {
    //下面就是自己对应的实现
        if (!TextUtils.isEmpty(url))
        {
            if (url.equals("-1"))
            {
            //-1 就停止
                player.stopPlayer();
            }
            else
            {
            //播放
                player.startPlayer(url);
            }
        }

    }

}

PlayerPackage

//android要把自己的东西 给RN调用,必须通过ReactPackage 来实现,
//最后在Application中添加这个
public class PlayerPackage implements ReactPackage {
    @Nonnull
    @Override
    public List<NativeModule> createNativeModules(@Nonnull ReactApplicationContext reactContext) {
        List<NativeModule> moduleList=new ArrayList<>();
        moduleList.add(new PlayerModule(reactContext));
        return moduleList;
    }

    @Nonnull
    @Override
    public List<ViewManager> createViewManagers(@Nonnull ReactApplicationContext reactApplicationContext) {
    //这个就把上面那个RNVGManager 返回去了
        List<ViewManager> viewManagers=new ArrayList<>();
        viewManagers.add(new RNVGManager());
        return viewManagers;
    }
}

MainApplication

public class MainApplication extends NgnApplication implements ReactApplication {

  private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
    @Override
    public boolean getUseDeveloperSupport() {
      return BuildConfig.DEBUG;
    }

    @Override
    protected List<ReactPackage> getPackages() {
      return Arrays.<ReactPackage>asList(
          new MainReactPackage()
              ,new AVPackage()
              //添加刚刚那个包
              ,new PlayerPackage()
      );
    }

    @Override
    protected String getJSMainModuleName() {
      return "index";
    }
  };

  @Override
  public ReactNativeHost getReactNativeHost() {
    return mReactNativeHost;
  }

  @Override
  public void onCreate() {
    super.onCreate();
    SoLoader.init(this, /* native exopackage */ false);
  }
}

这样Android这的代码就完成了

我们继续看下RN的代码如何写

RN:

Player.js

我们首先定义一个Player组件

import React, {Component} from 'react';
import {requireNativeComponent, View, ViewPropTypes} from 'react-native';
import PropTypes from 'prop-types'

export default  class Player extends Component 
{
  constructor(props) {
    super(props);

  
  }
  _assignRoot = (component) => {
    this._root = component;
  };
  setNativeProps(nativeProps) {
      //调用这个组件的setNativeProps方法,设置android原生定义个props
    this._root.setNativeProps(nativeProps);
  };

  startPlayer = (urlvalue) =>
  {
      //调用了android控件那边定义的url  props
    this.setNativeProps({url:urlvalue});
  }

  stop = () =>
  {
      //停止就传-1,都是前面定义好的
    this.setNativeProps({url:"-1"});
  }

  render ()
  {
    const nativeProps = Object.assign({}, this.props);
    Object.assign(nativeProps, {

    });
    return (
      <PlayerView
      ref={this._assignRoot}
      {...nativeProps}
   
    />);
  }
    

}
Player.PropTypes = {
    //这个是props 是一个属性
  url:PropTypes.string,
  startPlayer:PropTypes.func,//方法类型
  stopPlayer:PropTypes.func,//方法类型

    //让这个组件继承View组件的props
  scaleX: PropTypes.number,
  scaleY: PropTypes.number,
  translateX: PropTypes.number,
  translateY: PropTypes.number,
  rotation: PropTypes.number,
  ...ViewPropTypes,


}

//从android 或者ios那边 获取PlayerView这个控件,这个名字就是之前android 那边播放器 定义的名字
//把这个PlayerView 添加到Player
//Player 里面的render里渲染就是返回的PlayerView
const PlayerView = requireNativeComponent('PlayerView', Player)

好了我们可以跟其他的组件一样开始愉快的使用了,使用的组件是

App.js

下面这个是一个项目的Demo,里面就有Player导入,其他的不用管就看Player那里

import Player from ‘./Player.js’,以及使用

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow
 */

import React, {Component} from 'react';
import {props,Button, NativeModules, Platform, StyleSheet, Text, View,Alert,TextInput} from 'react-native';
import { DeviceEventEmitter } from 'react-native';
import Player from './Player.js'

//导入avsdk 调用android代码
const avsdk =  NativeModules.avmodule;
const playersdk =  NativeModules.avplayer;
var username1 = "023100003"
var password1 = "023100002"


var username2 = "023100001"
var password2 = "112233"

var domain = "119.91.140.155"
var host = "119.91.140.155"
var port = 5060




const buttonClick1 = () => {
  //String domain,String serverHost,int port,String userid,String password
 //很多的方法可以同步返回值
  var result=avsdk.register(domain,host,port,username1,password1)
  if(result == 1)
  {
    avsdk.toast("注册成功");
  }

};
const buttonClick2 = () => {
  var result=avsdk.register(domain,host,port,username2,password2)
  if(result == 1)
  {
    avsdk.toast("注册成功");
  }

};
const buttonClick_room1 = () => {
  var result=avsdk.makeCall("023200001");
  avsdk.toast(result?"进入成功":"进入失败");

};
const buttonClick_room2 = () => {
  var result=avsdk.makeCall("023200002");
  avsdk.toast(result?"进入成功":"进入失败");

};



 class App extends Component{

 
  /*
   public static final String SESSION_EVENT="session_event";
    public static final String REGISTER_EVENT="register_event";
    public static final String ERROR="error";
   */
  componentDidMount()
  {
    //监听事件名为onevent的事件
    this.eventProxy=DeviceEventEmitter.addListener('session_event',this.onEventResult);
      //监听事件名为onerror的事件
    this.registerProxy=DeviceEventEmitter.addListener('register_event',this.onRegisterResult);
      //监听事件是error的事件
    this.errorProxy=DeviceEventEmitter.addListener('error',this.onErrorResult);

  }
  onEventResult = (e)=> {
    //avsdk.toast('event:'+e)
    
  
}
  onRegisterResult = (e)=> {
    //avsdk.toast('register:'+e)
 
  }
  onErrorResult = (e)=> {
    //avsdk.toast('error:'+e)
 
  }
  componentWillUnmount()
  {
    this.eventProxy.remove();
    this.registerProxy.remove();
    this.errorProxy.remove();
  }




  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.text}>domain:{domain}  host:{host}  port:{port}</Text>
        <Text style={styles.text}>用户一:{username1}  {password1}</Text>
        <Text style={styles.text}>用户二:{username2}  {password2}</Text>

        <Button title={"用户一注册"} onPress={buttonClick1}/>
        
        
        <Button title={"用户二注册"}  onPress={buttonClick2}/>

        <Button title={"进入会议室023200001"}  onPress={buttonClick_room1}/>
        <Button title={"进入会议室023200002"}  onPress={buttonClick_room2}/>
        <Button title={"挂断"}  onPress={
          () =>
          {
             var result=avsdk.hangUpCall();
             avsdk.toast(result==1?"挂断成功":"关断失败");
          }
        }/>
        <Button title={"扬声器和听筒的切换"}  onPress={
          () =>
          {
             avsdk.toggleSpeakerphone();
       
          }
        }/>
        <Button title={"是否已经注册"}  onPress={() => {
          avsdk.isRegister()
                  //avsdk.toast(avsdk.isRegister()+"") ;
                }}/>

        <Button title={"销毁"}  onPress={() => {
          avsdk.destory()
           avsdk.toast("已经销毁");
                }}/>

         <Button title={"跳转视频"}  onPress={() => {
          playersdk.startPlayer("http://119.91.140.155:18080/live/0AF00011/0000/0001.flv");
          
                }}/>
//播放
          <Button title={"播放RN组件Player"}  onPress={() => {
               var url = "http://119.91.140.155:18080/live/0AF00011/0000/0001.flv"
               var uu="/storage/emulated/0/DCIM/Camera/t.mp4"
                //alert(this.myPlayer.props);
               // this.myPlayer.props.url(url);
               this.myPlayer.startPlayer(url);
               

                }}/>
//停止
          <Button title={"停止播放"}  onPress={() => {
               this.myPlayer.stop();
               

                }}/>
          <Player ref={(view) => this.myPlayer = view} style={styles.playerStyle
          
            //url="http://119.91.140.155:18080/live/0AF00011/0000/0001.flv"  
        }
         
          ></Player>
      </View>
    );
  }
}


const styles = StyleSheet.create({

  playerStyle:{
    width:200,
    height:100,
  }
  ,
  

  buttomStyle:{
    backgroundColor:'blue',
    width:100,
    height:500,
    marginTop:20,

    
  },
  text: {
    flexDirection:'column',
    backgroundColor: '#F5FCFF',
    height:50,
    fontSize:19
  },
  container: {
    flex: 1,
    flexDirection:'column',
    backgroundColor: '#F5FCFF',
    marginVertical:10
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});
export default App

好了,RN中使用android的UI组件的使用过程就介绍完了,如果你觉得还不错,能够帮助到你,就点个赞吧~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值