在如今的App中,已经有成千上万的原生UI组件了——其中的一些是平台的一部分,另一些可能来自于一些第三方库,而且可能你自己还收藏了很多。React Native已经封装了大部分最常见的组件,譬如ScrollView和TextInput,但不可能封装全部组件。而且,说不定你曾经为自己以前的App还封装过一些组件,React Native肯定没法包含它们。幸运的是,在React Naitve应用程序中封装和植入已有的组件非常简单。
本向导会引导你如何构建一个原生UI组件,带领你了解React Native核心库中WebView组件的具体实现。
一、WebView样例
在这个例子里,我们来看看为了让JavaScript中可以使用WevView,我们以react-native init AwesomeProject创建示例项目进行演示,项目完成目录如下:
提供原生视图很简单,主要有如下几个步骤:
1.创建一个ViewManager的子类,实现createViewInstance方法,使用@ReactProp(或@ReactPropGroup)注解;
2.创建一个把ReactPackage的子类,把这个视图管理类注册到应用程序包的createViewManagers里;
3.在MainActivity中的getPackages方法中添加ReactPackage的子类;
4.实现JavaScript模块WebView.js,index.android.js;
ReactWebViewManager.java类:
public class ReactWebViewManager extends SimpleViewManager{
public static final String REACT_CLASS = "MyWebView";
@Override
public String getName() {
return REACT_CLASS;
}
@Override
protected WebView createViewInstance(ThemedReactContext reactContext) {
WebView webView= new WebView(reactContext);
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
return webView;
}
@ReactProp(name = "url")
public void setUrl(WebView view,@Nullable String url) {
Log.e("TAG", "setUrl");
view.loadUrl(url);
}
@ReactProp(name = "html")
public void setHtml(WebView view,@Nullable String html) {
Log.e("TAG", "setHtml");
view.loadData(html, "text/html; charset=utf-8", "UTF-8");
}
}
2.创建一个把ReactPackage的子类,把这个视图管理类注册到应用程序包的createViewManagers里;
AppReactPackage.java类
public class AppReactPackage implements ReactPackage{
...... ......
@Override
public List createViewManagers(ReactApplicationContext reactContext) {
return Arrays.asList(
new ReactWebViewManager(),new ReactRTCWebViewManager());
}
}
3.在MainActivity中的getPackages方法中添加ReactPackage的子类;
MainActivity.java类
public class MainActivity extends ReactActivity {
...... ......
@Override
protected List getPackages() {
return Arrays.asList(
new MainReactPackage(),new AppReactPackage()
);
}
}
4.实现JavaScript模块WebView.js,index.android.js;
WebView.js文件
var { requireNativeComponent,PropTypes } = require('react-native');
var iface = {
name: 'WebView',
propTypes: {
url: PropTypes.string,
html: PropTypes.string,
},
};
module.exports = requireNativeComponent('MyWebView', iface);
index.android.js文件
import React from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
var WebView=require('./WebView');
class MyAwesomeApp extends React.Component {
render() {
return (
Hello, World
)
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
hello: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
AppRegistry.registerComponent('AwesomeProject', () => MyAwesomeApp);
5.运行效果如下:
提示1:如果在运行的过程中,报错如下
处理1:修改该部分代码如下,module.exports = requireNativeComponent('MyWebView', iface,{nativeOnly:{
'scaleX': true,
'scaleY': true,
'testID': true,
'decomposedMatrix': true,
'backgroundColor': true,
'accessibilityComponentType': true,
'renderToHardwareTextureAndroid': true,
'translateY': true,
'translateX': true,
'accessibilityLabel': true,
'accessibilityLiveRegion': true,
'importantForAccessibility': true,
'rotation': true,
'opacity': true,
'onLayout': true,
}});
二、事件
现在我们已经知道了怎么导出一个原生视图组件,并且我们可以在JS里很方便的控制它了。不过我们怎么才能处理来自用户的事件,譬如缩放操作或者拖动?当一个原生事件发生的时候,它应该也能触发JavaScript端视图上的事件,这两个视图会依据getId()而关联在一起。
下面我们就以基于原始WebView组件,在JavaScript层处理它的滚动事件,实现如下:
1. 创建RTCWebView继承WebView,重写对应的事件,然后将事件传递给javascript层了
public class RTCWebView extends WebView {
public RTCWebView(Context context) {
super(context);
}
public RTCWebView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RTCWebView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
Log.e("TAG","onScrollChanged");
WritableMap event = Arguments.createMap();
event.putInt("ScrollX", l);
event.putInt("ScrollY", t);
ReactContext reactContext = (ReactContext)getContext();
reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
getId(), "topChange", event);
}
}
2.创建ReactRTCWebViewManager,在createViewInstance方法,在里面返回我们实现的子类RTCWebView
public class ReactRTCWebViewManager extends SimpleViewManager{
public static final String REACT_CLASS = "MyRTCWebView";
...... ......
@Override
protected WebView createViewInstance(ThemedReactContext reactContext) {
WebView webView= new RTCWebView(reactContext);
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
return webView;
}
...... ......
}
3. 仍然再AppAppReactPackage注册实现ReactRTCWebViewManager(该部分省略);
4.JavaScript层创建RTCWebView.js文件,改造index.android.js文件:
RTCWebView.js文件
'use strict';
var React = require('react');
var ReactNative = require('react-native');
var {
requireNativeComponent,
PropTypes
} = ReactNative;
class WebView extends React.Component {
constructor() {
super();
this._onChange = this._onChange.bind(this);
}
_onChange(event: Event) {
if (!this.props.onScrollChange) {
return;
}
this.props.onScrollChange({ScrollX:event.nativeEvent.ScrollX,ScrollY:event.nativeEvent.ScrollY});
}
render() {
return ;
}
}
WebView.propTypes = {
url: React.PropTypes.string,
html: React.PropTypes.string,
onScrollChange: React.PropTypes.func,
};
var RTCWebView = requireNativeComponent('MyRTCWebView', WebView,{nativeOnly:{
'scaleX': true,
'scaleY': true,
'testID': true,
'decomposedMatrix': true,
'backgroundColor': true,
'accessibilityComponentType': true,
'renderToHardwareTextureAndroid': true,
'translateY': true,
'translateX': true,
'accessibilityLabel': true,
'accessibilityLiveRegion': true,
'importantForAccessibility': true,
'rotation': true,
'opacity': true,
'onLayout': true,
'source': true,
'javaScriptEnabled': true,
'domStorageEnabled': true,
'injectedJavaScript': true,
'userAgent': true,
'onChange': true,
}});
module.exports = WebView
index.android.js文件
'use strict';
import React from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
var WebView=require('./RTCWebView');
class MyAwesomeApp extends React.Component {
render() {
return (
Hello, World
)
}
onWebViewScroll(event) {
console.log(event);
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
hello: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
AppRegistry.registerComponent('AwesomeProject', () => MyAwesomeApp);
5.Debug Js,运行后滑动Webview,在Chrome的Develop Tools中显示如下:
提示2:如果在Debug JS的过程中,出现如下报错,导致无法正常Debug
The method /launch-chrome-devtools is deprecated. You are probably using an application created with an older CLI with the packager of a newer CLI. Please upgrade your application: https://facebook.github.io/react-native/docs/upgrading.html
处理2:尝试安装新版的Chrome浏览器版本,和react-native的版本。该demo使用的react-native版本为0.27.1-rc2,具体如何升级请查看《版本升级》章节。