在如今的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里;
4.实现JavaScript模块WebView.js,index.android.js;
1.创建一个ViewManager的子类,实现createViewInstance方法,使用@ReactProp(或@ReactPropGroup)注解。
ReactWebViewManager.java类:
public class ReactWebViewManager extends SimpleViewManager<WebView>{ 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<ViewManager> createViewManagers(ReactApplicationContext reactContext) { return Arrays.<ViewManager>asList( new ReactWebViewManager(),new ReactRTCWebViewManager()); } }3.在MainActivity中的getPackages方法中添加ReactPackage的子类;
MainActivity.java类
public class MainActivity extends ReactActivity { ...... ...... @Override protected List<ReactPackage> getPackages() { return Arrays.<ReactPackage>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 ( <View style={styles.container}> <Text style={styles.hello}>Hello,World</Text> <WebView url="https://www.baidu.com" style={{width:200,height:400}}></WebView> </View> ) } } var styles = StyleSheet.create({ container: { flex: 1,justifyContent: 'center',hello: { fontSize: 20,textAlign: 'center',margin: 10,}); AppRegistry.registerComponent('AwesomeProject',() => MyAwesomeApp);5.运行效果如下:
提示1:如果在运行的过程中,报错如下
'scaleX': true,
'scaleY': true,121);"> 'testID': true,121);"> 'decomposedMatrix': true,121);"> 'backgroundColor': true,121);"> 'accessibilityComponentType': true,121);"> 'renderToHardwareTextureAndroid': true,121);"> 'translateY': true,121);"> 'translateX': true,121);"> 'accessibilityLabel': true,121);"> 'accessibilityLiveRegion': true,121);"> 'importantForAccessibility': true,121);"> 'rotation': true,121);"> 'opacity': true,121);"> 'onLayout': true,121);"> }});
二、事件
现在我们已经知道了怎么导出一个原生视图组件,并且我们可以在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<WebView>{ 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(该部分省略);
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 <RTCWebView {...this.props} onChange={this._onChange} />; } } 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 = WebViewindex.android.js文件
'use strict'; import React from 'react'; import { AppRegistry,View } from 'react-native'; var WebView=require('./RTCWebView'); class MyAwesomeApp extends React.Component { render() { return ( <View style={styles.container}> <Text style={styles.hello}>Hello,World</Text> <WebView onScrollChange={this.onWebViewScroll} url="https://www.baidu.com" style={{width:200,height:400}}></WebView> </View> ) } onWebViewScroll(event) { console.log(event); } } var styles = StyleSheet.create({ container: { flex: 1,() => 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