国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > JavaScript > 正文

詳解react-native WebView 返回處理(非回調(diào)方法可解決)

2019-11-19 14:17:23
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

1.前言

項(xiàng)目中有些頁(yè)面內(nèi)容是變更比較頻繁的,這些頁(yè)面我們會(huì)考慮用 網(wǎng)頁(yè) 來(lái)解決。

在RN項(xiàng)目中提供一個(gè)公用的Web頁(yè),如果是網(wǎng)頁(yè)內(nèi)容,就跳轉(zhuǎn)到這個(gè)界面展示。

此時(shí)會(huì)有一個(gè)問(wèn)題是,網(wǎng)頁(yè)會(huì)有一級(jí)頁(yè)面,二級(jí)頁(yè)面,這就會(huì)設(shè)計(jì)到導(dǎo)航欄返回鍵的處理(以及在Android上返回鍵的處理)。

這個(gè)問(wèn)題,在RN官網(wǎng)就可找到解決方式。就是用 onNavigationStateChange 這個(gè)回調(diào)方法記錄當(dāng)前的導(dǎo)航狀態(tài),從而判斷是返回上一級(jí)頁(yè)面還是退出這個(gè)網(wǎng)頁(yè),回到App的其他界面。

但是,當(dāng)網(wǎng)頁(yè)的實(shí)現(xiàn)是React時(shí),就會(huì)有問(wèn)題了,你會(huì)發(fā)現(xiàn),當(dāng)頁(yè)面跳轉(zhuǎn)的時(shí)候,onNavigationStateChange這個(gè)回調(diào)方法沒(méi)有回調(diào)?。。≡趺捶仕模?!

一開(kāi)始嘗試了把網(wǎng)頁(yè)地址換成百度的,可以接收回調(diào),一切都運(yùn)行的很好,可是換成我們的鏈接就不行,所以就把鍋甩給了后臺(tái),以為是React哪邊寫(xiě)的不對(duì)。

因?yàn)樯弦粋€(gè)項(xiàng)目時(shí)間緊,沒(méi)有時(shí)間好好去看一下源碼,就想了一個(gè)不是很完善的解決方案,就是網(wǎng)頁(yè)用js來(lái)回調(diào)App來(lái)告知現(xiàn)在的導(dǎo)航狀態(tài),這樣的解決方式顯示是不友好的。

現(xiàn)在稍微有點(diǎn)時(shí)間看了源碼才知道真正原因。

2.原因

下面就分析一下這個(gè)問(wèn)題的原因和我的解決方式。

1.首先,先找到源碼的位置。

node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/webview

node_modules/react-native/Libraries/Components/WebView

目錄結(jié)構(gòu)是這樣的:

 

2.實(shí)現(xiàn)的代碼段 (JAVA端)

RN的實(shí)際運(yùn)行代碼都是原生代碼,所以,像WebView組件的一些事件回調(diào),其實(shí)都是原生代碼中的回調(diào)觸發(fā)的。如下

(ReactWebViewManager.java) rn版本0.47.1

protected static class ReactWebViewClient extends WebViewClient { //WebViewClient就是我們?cè)趯?xiě)Android原生代碼時(shí),監(jiān)聽(tīng)網(wǎng)頁(yè)加載情況使用的工具。   protected static final String REACT_CLASS = "RCTWebView"; //定義的原生組件名,在后面JS中會(huì)對(duì)應(yīng)到。  //...  @Override  public void onPageStarted(WebView webView, String url, Bitmap favicon) { //有很多回調(diào)方法,此處只舉一例   super.onPageStarted(webView, url, favicon);   mLastLoadFailed = false;   dispatchEvent(     webView,     new TopLoadingStartEvent(   //自己定義的時(shí)間,dispatch后,事件會(huì)傳給js       webView.getId(),       createWebViewEvent(webView, url)));  }  //... }

(ReactWebViewManager.java) rn版本0.43.3  ,RN不同版本會(huì)有代碼調(diào)整,所以RN升級(jí)的時(shí)候,需要仔細(xì)的回歸測(cè)試。

protected static class ReactWebViewClient extends WebViewClient { //WebViewClient就是我們?cè)趯?xiě)Android原生代碼時(shí),監(jiān)聽(tīng)網(wǎng)頁(yè)加載情況使用的工具。   protected static final String REACT_CLASS = "RCTWebView"; //定義的原生組件名,在后面JS中會(huì)對(duì)應(yīng)到。  //...  @Override  public void onPageStarted(WebView webView, String url, Bitmap favicon) { //有很多回調(diào)方法,此處只舉一例   super.onPageStarted(webView, url, favicon);   mLastLoadFailed = false;   dispatchEvent(     webView,     new TopLoadingStartEvent(   //自己定義的時(shí)間,dispatch后,事件會(huì)傳給js       webView.getId(),       createWebViewEvent(webView, url)));  }  @Override  public void doUpdateVisitedHistory(WebView webView, String url, boolean isReload) { //坑在這,這里就是導(dǎo)航有變化的時(shí)候會(huì)回調(diào)在這個(gè)版本是有這個(gè)處理的,但是不知道在哪個(gè)版本刪掉了 -.-   super.doUpdateVisitedHistory(webView, url, isReload);   dispatchEvent(     webView,     new TopLoadingStartEvent(       webView.getId(),       createWebViewEvent(webView, url)));  }  //... }

(TopLoadingStartEvent.java) 回調(diào)JS的Event

public class TopLoadingStartEvent extends Event<TopLoadingStartEvent> { public static final String EVENT_NAME = "topLoadingStart";  //對(duì)應(yīng)方法是onLoadingStart, 因?yàn)閷?duì)RN的結(jié)構(gòu)不熟悉,在此處花了很長(zhǎng)時(shí)間研究是怎么對(duì)應(yīng)的,最后找到了定義對(duì)應(yīng)的文件 private WritableMap mEventData; public TopLoadingStartEvent(int viewId, WritableMap eventData) {  super(viewId);  mEventData = eventData; } @Override public String getEventName() {  return EVENT_NAME; } @Override public boolean canCoalesce() {  return false; } @Override public short getCoalescingKey() {  // All events for a given view can be coalesced.  return 0; } @Override public void dispatch(RCTEventEmitter rctEventEmitter) {  rctEventEmitter.receiveEvent(getViewTag(), getEventName(), mEventData); }}

(node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstants.java)

這個(gè)文件里,定義了對(duì)應(yīng)關(guān)系

/** * Constants exposed to JS from {@link UIManagerModule}. *//* package */ class UIManagerModuleConstants { /* package */ static Map getDirectEventTypeConstants() {  return MapBuilder.builder()    .put("topContentSizeChange", MapBuilder.of("registrationName", "onContentSizeChange"))    .put("topLayout", MapBuilder.of("registrationName", "onLayout"))    .put("topLoadingError", MapBuilder.of("registrationName", "onLoadingError"))    .put("topLoadingFinish", MapBuilder.of("registrationName", "onLoadingFinish"))    .put("topLoadingStart", MapBuilder.of("registrationName", "onLoadingStart"))    .put("topSelectionChange", MapBuilder.of("registrationName", "onSelectionChange"))    .put("topMessage", MapBuilder.of("registrationName", "onMessage"))    .build(); }}

3.實(shí)現(xiàn)的代碼段 (JS端)

(node_modules/react-native/Libraries/Components/WebView/WebView.android.js)

在下面的代碼中可以看到只有 onLoadingStart    和 onLoadingFinish 才會(huì)調(diào)用  updateNavigationState ,問(wèn)題就出現(xiàn)在這了,由于我們的網(wǎng)頁(yè)實(shí)現(xiàn)是React,只有一個(gè)頁(yè)面啊!所以只會(huì)調(diào)用一次 onLoadingStart  和 onLoadingFinish 。再點(diǎn)擊詳情頁(yè)并不會(huì)跳轉(zhuǎn)到新頁(yè)面,而是刷新原來(lái)的頁(yè)面。所以也就沒(méi)有 updateNavigationState 回調(diào)了。

class WebView extends React.Component { static propTypes = {  //給外部定義的可設(shè)置的屬性  ...ViewPropTypes,  renderError: PropTypes.func,  renderLoading: PropTypes.func,  onLoad: PropTypes.func,  //...  } render() { //繪制頁(yè)面內(nèi)容  //...  var webView =   <RCTWebView    ref={RCT_WEBVIEW_REF}    key="webViewKey"    style={webViewStyles}    source={resolveAssetSource(source)}    onLoadingStart={this.onLoadingStart}    onLoadingFinish={this.onLoadingFinish}    onLoadingError={this.onLoadingError}/>;  return (   <View style={styles.container}>    {webView}    {otherView}   </View>  ); } onLoadingStart = (event) => {  var onLoadStart = this.props.onLoadStart;  onLoadStart && onLoadStart(event);  this.updateNavigationState(event); }; onLoadingFinish = (event) => {  var {onLoad, onLoadEnd} = this.props;  onLoad && onLoad(event);  onLoadEnd && onLoadEnd(event);  this.setState({   viewState: WebViewState.IDLE,  });  this.updateNavigationState(event); }; updateNavigationState = (event) => {  if (this.props.onNavigationStateChange) {   this.props.onNavigationStateChange(event.nativeEvent);  } };}var RCTWebView = requireNativeComponent('RCTWebView', WebView, {  //對(duì)應(yīng)上面JAVA中的 ‘RCTWebView' nativeOnly: { messagingEnabled: PropTypes.bool, }, }); module.exports = WebView; 

2.解決方法

既然原因找到了,就容易解決了

解決方式:自定義WebView,添加 doUpdateVisitedHistory 處理,在每次導(dǎo)航變化的時(shí)候,通知JS。

1. 拷貝下圖中的文件到我們自己項(xiàng)目中的Android代碼目錄下

拷貝完后的Android目錄:

ReactWebViewManager.java中需要修改幾個(gè)地方

public class ReactWebViewManager extends SimpleViewManager<WebView> { protected static final String REACT_CLASS = "RCTWebView1"; //此處修改一下名字 protected static class ReactWebViewClient extends WebViewClient {    @Override    public void doUpdateVisitedHistory(WebView webView, String url, boolean isReload) {      super.doUpdateVisitedHistory(webView, url, isReload);      dispatchEvent(    //在導(dǎo)航變化的時(shí)候,dispatchEvent          webView,          new TopCanGoBackEvent(              webView.getId(),              createCanGoBackWebViewEvent(webView, url)));    } }}

TopCanGoBackEvent是我自己添加的一個(gè)Event,專(zhuān)門(mén)用來(lái)通知導(dǎo)航變化

TopCanGoBackEvent.java

public class TopCanGoBackEvent extends Event<TopCanGoBackEvent> { public static final String EVENT_NAME = "topChange";  private WritableMap mEventData; public TopCanGoBackEvent(int viewId, WritableMap eventData) {  super(viewId);  mEventData = eventData; } @Override public String getEventName() {  return EVENT_NAME; } @Override public boolean canCoalesce() {  return false; } @Override public short getCoalescingKey() {  // All events for a given view can be coalesced.  return 0; } @Override public void dispatch(RCTEventEmitter rctEventEmitter) {  rctEventEmitter.receiveEvent(getViewTag(), getEventName(), mEventData); }}

新建 ReactWebViewPage.java

public class ReactWebViewPackage implements ReactPackage {  @Override  public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {    return Collections.emptyList();  }  @Override  public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {    return Arrays.<ViewManager>asList(        new ReactWebViewManager()    );  }}

然后在MainApplication中添加這個(gè)模塊

public class MainApplication extends Application implements ReactApplication {  @Override  protected List<ReactPackage> getPackages() {   return Arrays.<ReactPackage>asList(     new MainReactPackage(),     new ReactWebViewPackage()  //WebView   );  }}

以上就是Android需要修改的地方,ios我沒(méi)有嘗試過(guò),應(yīng)該大差不差同一個(gè)道理。

2. 拷貝下圖中的文件到我們自己項(xiàng)目中的JS代碼目錄下,并修改一下名字

JS代碼目錄:

CustomWebView.android.js 有幾個(gè)地方需要修改。

/** * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule CustomWebView  //此處需要修改名稱(chēng) */var RCT_WEBVIEW_REF = 'webview1'; //此處需要修改名稱(chēng) render() {  var webView =   <NativeWebView    onLoadingStart={this.onLoadingStart}    onLoadingFinish={this.onLoadingFinish}    onLoadingError={this.onLoadingError}    onChange={this.onChange} //添加方法   />;  return (   <View style={styles.container}>    {webView}    {otherView}   </View>  ); } onChange = (event) => {  //添加方法  this.updateNavigationState(event); };}var RCTWebView = requireNativeComponent('RCTWebView1', CustomWebView, CustomWebView.extraNativeComponentConfig); //修改名稱(chēng)module.exports = CustomWebView; //修改名稱(chēng)

至此就完成自定義WebView模塊。也可以解決網(wǎng)頁(yè)是React實(shí)現(xiàn),不能導(dǎo)航的問(wèn)題。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持武林網(wǎng)。

發(fā)表評(píng)論 共有條評(píng)論
用戶(hù)名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 鄂伦春自治旗| 万载县| 金川县| 牡丹江市| 丰镇市| 灌阳县| 谢通门县| 普宁市| 澄江县| 集安市| 嫩江县| 岳池县| 广东省| 巩义市| 定兴县| 塔河县| 惠东县| 兰西县| 阳朔县| 囊谦县| 呼玛县| 瑞金市| 南雄市| 玉龙| 尚义县| 长春市| 锡林郭勒盟| 南郑县| 互助| 台北市| 泽普县| 隆德县| 双流县| 苏尼特右旗| 和政县| 噶尔县| 新邵县| 隆尧县| 襄垣县| 太仆寺旗| 密云县|