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) {
this._root.setNativeProps(nativeProps);
};
startPlayer = (urlvalue) =>
{
this.setNativeProps({url:urlvalue});
}
stop = () =>
{
this.setNativeProps({url:"-1"});
}
render ()
{
const nativeProps = Object.assign({}, this.props);
Object.assign(nativeProps, {
});
return (
<PlayerView
ref={this._assignRoot}
{...nativeProps}
/>);
}
}
Player.PropTypes = {
url:PropTypes.string,
startPlayer:PropTypes.func,
stopPlayer:PropTypes.func,
scaleX: PropTypes.number,
scaleY: PropTypes.number,
translateX: PropTypes.number,
translateY: PropTypes.number,
rotation: PropTypes.number,
...ViewPropTypes,
}
const PlayerView = requireNativeComponent('PlayerView', Player)
好了我们可以跟其他的组件一样开始愉快的使用了,使用的组件是
App.js
下面这个是一个项目的Demo,里面就有Player导入,其他的不用管就看Player那里
import Player from ‘./Player.js’,以及使用
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'
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 = () => {
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{
componentDidMount()
{
this.eventProxy=DeviceEventEmitter.addListener('session_event',this.onEventResult);
this.registerProxy=DeviceEventEmitter.addListener('register_event',this.onRegisterResult);
this.errorProxy=DeviceEventEmitter.addListener('error',this.onErrorResult);
}
onEventResult = (e)=> {
}
onRegisterResult = (e)=> {
}
onErrorResult = (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()
}}/>
<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"
this.myPlayer.startPlayer(url);
}}/>
<Button title={"停止播放"} onPress={() => {
this.myPlayer.stop();
}}/>
<Player ref={(view) => this.myPlayer = view} style={styles.playerStyle
}
></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组件的使用过程就介绍完了,如果你觉得还不错,能够帮助到你,就点个赞吧~~~
|