ReactNative 调用Android 原生(一)——原生模块(一)

ReactNative 调用Android 原生(一)——原生模块(二):

这是demo链接:​​https://github.com/danfengfirst/VisitAndroid​​​ 先说下我遇到的坑:
原本学完React Native调用原生模块我觉得挺顺利的,也挺容易理解的,觉得调用Android原生组件也是不成问题,应该挺快的,结果React Native 调原生组件一直不显示,在网上找了好多文章对比看,觉得自己代码、步骤都没有问题,调用控件不显示的原因文章末尾跟代码的注释里面我都将说明,免得有跟我一样入坑的!

步骤

1、继承SimpleViewManager
2、实现ReactPackage
3、向MainApplication添加ReactPackage
4、RN中导出View
5、使用导出的View

使用

如果你看过ReactNative 调用Android 原生(一)——原生模块(一)、(二)就很熟悉这个流程了,下面这个Demo是个倒计时的效果

ReactNative 调用Android 原生(二)——原生组件_android


1、继承SimpleViewManager,

(1)getName()返回的名字在RN中将被用到

(2) @ReactProp(name = “start”) 中@ReactProp注解用于定义方法,name = "start"中的start就是RN中导出的View可以使用的属性名

public class TestUI extends SimpleViewManager<TimerView> {
@Override
public String getName() {
return "TimerView";
}

@Override
protected TimerView createViewInstance(ThemedReactContext reactContext) {

return new TimerView(reactContext);
}

@ReactProp(name = "start")
public void startTimer(TimerView timerView, Integer totalCount) {
timerView.startTimer(totalCount);
}
}

补充:

public class TimerView extends LinearLayout {
private Timer mTimer;
private TimerTask mTimerTask;
private TextView mTimerTv;
private Context mContext;
private int mCount;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
mTimerTv.setText(mCount + "");
int w = MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY);
int h = MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY);
measure(
w,
h);
layout(getLeft(), getTop(), getRight(), getBottom());
}
};

public TimerView(Context context) {
this(context,null);
}

public TimerView(Context context, @Nullable AttributeSet attrs) {
super(context);
init(context);
}


private void init(Context context) {
mContext = context;
//布局的话随便加载
View view = LayoutInflater.from(context).inflate(R.layout.timer_view, this);
view.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT));
mTimerTv = view.findViewById(R.id.timer_tv);
mTimerTv.setText(10 + "");
}

//调用该方法,开始倒计时
public void startTimer(int totalcount) {
mCount = totalcount;
mTimerTv.setText(totalcount + "");
mTimer = new Timer();
mTimerTask = new TimerTask() {
@Override
public void run() {
if (mCount >= 0) {
--mCount;
mHandler.sendEmptyMessage(0);
} else {
if (mTimer != null) {
mTimer.cancel();
mTimer = null;
}
}
}
};
mTimer.schedule(mTimerTask, 0, 1000);
}
}

2、实现ReactPackage

public class TestPackages implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> nativeModules = new ArrayList<>();
nativeModules.add(new TestModules(reactContext));
return nativeModules;
}

@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList(
new TestUI()
);
}
}

3、向MainApplication添加ReactPackage

public class MainApplication extends Application 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 TestPackages()
);
}

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

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

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

4、RN中导出View
这里就是坑了,必须写style并设置宽高,否则不显示
属性名:start对应上面的

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

//Android 原生View 映射成AndroidTimerView,导出为NativeTimerView
const AndroidTimerView = requireNativeComponent('TimerView', NativeTimerView);
export default class NativeTimerView extends Component<Props> {
static propTypes = {
stimer: PropTypes.number,
};

render() {
return <AndroidTimerView
style={{width: 100, height: 50, borderRadius: 20}}
start={this.props.stimer}
/>
}
}

5、使用导出的View
如果你在第4步中没有设置style 在这里对NativeTimerView设置style的宽高,运行后仍然会什么都不显示,因此必须在第4步中设置宽高

export default class App extends Component<Props> {
constructor() {
super();
this.state = {
name: 'To get started, edit App.js',
sendEvent: 'Send Event'
}
}

toast = () => {
let obj = {
id: 1,
name: "xiaohong"
};
NativeModules.TestModules.Toasts(obj);
};
toastFinishCallback = () => {
NativeModules.TestModules.ToastFinished("调用了原生并带有回调", (result) => {
this.setState({
name: result.name
});
});
};

sendEvent = () => {
NativeModules.TestModules.sendEvent();
};

componentWillMount() {
DeviceEventEmitter.addListener('sendEvent', (e: Event) => {
this.setState({
sendEvent: e.name
});
})
};

startActivity = () => {
NativeModules.TestModules.startActivity();
};

render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}
onPress={this.toast}
>Welcome to React Native!</Text>
<Text style={styles.instructions}
onPress={this.toastFinishCallback}
>{this.state.name}</Text>
<Text style={styles.instructions}
onPress={this.sendEvent}
>{this.state.sendEvent}</Text>

<Text
style={styles.instructions}
onPress={this.startActivity}
>点击开启activity</Text>

<NativeTimerView
style={{width: 100, height: 50, borderRadius: 20}}
stimer={60}
/>
</View>
);
}
}

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});

总结

我的之前显示不出来就是因为我在第5步设置的宽高,而没有在第4步设置宽高,结果就一直显示不出来,希望不要有人再犯我这样的错误了。