您好,登錄后才能下訂單哦!
這篇文章主要介紹了如何解決react-native WebView返回處理問題,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
1.前言
項目中有些頁面內容是變更比較頻繁的,這些頁面我們會考慮用 網頁 來解決。
在RN項目中提供一個公用的Web頁,如果是網頁內容,就跳轉到這個界面展示。
此時會有一個問題是,網頁會有一級頁面,二級頁面,這就會設計到導航欄返回鍵的處理(以及在Android上返回鍵的處理)。
這個問題,在RN官網就可找到解決方式。就是用 onNavigationStateChange 這個回調方法記錄當前的導航狀態,從而判斷是返回上一級頁面還是退出這個網頁,回到App的其他界面。
但是,當網頁的實現是React時,就會有問題了,你會發現,當頁面跳轉的時候,onNavigationStateChange這個回調方法沒有回調!!!怎么肥四!!
一開始嘗試了把網頁地址換成百度的,可以接收回調,一切都運行的很好,可是換成我們的鏈接就不行,所以就把鍋甩給了后臺,以為是React哪邊寫的不對。
因為上一個項目時間緊,沒有時間好好去看一下源碼,就想了一個不是很完善的解決方案,就是網頁用js來回調App來告知現在的導航狀態,這樣的解決方式顯示是不友好的。
現在稍微有點時間看了源碼才知道真正原因。
2.原因
下面就分析一下這個問題的原因和我的解決方式。
1.首先,先找到源碼的位置。
node_modules\react-native\ReactAndroid\src\main\java\com\facebook\react\views\webview
node_modules\react-native\Libraries\Components\WebView
目錄結構是這樣的:
2.實現的代碼段 (JAVA端)
RN的實際運行代碼都是原生代碼,所以,像WebView組件的一些事件回調,其實都是原生代碼中的回調觸發的。如下
(ReactWebViewManager.java) rn版本0.47.1
protected static class ReactWebViewClient extends WebViewClient { //WebViewClient就是我們在寫Android原生代碼時,監聽網頁加載情況使用的工具。 protected static final String REACT_CLASS = "RCTWebView"; //定義的原生組件名,在后面JS中會對應到。 //... @Override public void onPageStarted(WebView webView, String url, Bitmap favicon) { //有很多回調方法,此處只舉一例 super.onPageStarted(webView, url, favicon); mLastLoadFailed = false; dispatchEvent( webView, new TopLoadingStartEvent( //自己定義的時間,dispatch后,事件會傳給js webView.getId(), createWebViewEvent(webView, url))); } //... }
(ReactWebViewManager.java) rn版本0.43.3 ,RN不同版本會有代碼調整,所以RN升級的時候,需要仔細的回歸測試。
protected static class ReactWebViewClient extends WebViewClient { //WebViewClient就是我們在寫Android原生代碼時,監聽網頁加載情況使用的工具。 protected static final String REACT_CLASS = "RCTWebView"; //定義的原生組件名,在后面JS中會對應到。 //... @Override public void onPageStarted(WebView webView, String url, Bitmap favicon) { //有很多回調方法,此處只舉一例 super.onPageStarted(webView, url, favicon); mLastLoadFailed = false; dispatchEvent( webView, new TopLoadingStartEvent( //自己定義的時間,dispatch后,事件會傳給js webView.getId(), createWebViewEvent(webView, url))); } @Override public void doUpdateVisitedHistory(WebView webView, String url, boolean isReload) { //坑在這,這里就是導航有變化的時候會回調在這個版本是有這個處理的,但是不知道在哪個版本刪掉了 -.- super.doUpdateVisitedHistory(webView, url, isReload); dispatchEvent( webView, new TopLoadingStartEvent( webView.getId(), createWebViewEvent(webView, url))); } //... }
(TopLoadingStartEvent.java) 回調JS的Event
public class TopLoadingStartEvent extends Event<TopLoadingStartEvent> { public static final String EVENT_NAME = "topLoadingStart"; //對應方法是onLoadingStart, 因為對RN的結構不熟悉,在此處花了很長時間研究是怎么對應的,最后找到了定義對應的文件 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)
這個文件里,定義了對應關系
/** * 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.實現的代碼段 (JS端)
(node_modules\react-native\Libraries\Components\WebView\WebView.android.js)
在下面的代碼中可以看到只有 onLoadingStart 和 onLoadingFinish 才會調用 updateNavigationState ,問題就出現在這了,由于我們的網頁實現是React,只有一個頁面啊!所以只會調用一次 onLoadingStart 和 onLoadingFinish 。再點擊詳情頁并不會跳轉到新頁面,而是刷新原來的頁面。所以也就沒有 updateNavigationState 回調了。
class WebView extends React.Component { static propTypes = { //給外部定義的可設置的屬性 ...ViewPropTypes, renderError: PropTypes.func, renderLoading: PropTypes.func, onLoad: PropTypes.func, //... } render() { //繪制頁面內容 //... 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, { //對應上面JAVA中的 ‘RCTWebView' nativeOnly: { messagingEnabled: PropTypes.bool, }, }); module.exports = WebView;
2.解決方法
既然原因找到了,就容易解決了
解決方式:自定義WebView,添加 doUpdateVisitedHistory 處理,在每次導航變化的時候,通知JS。
1. 拷貝下圖中的文件到我們自己項目中的Android代碼目錄下
拷貝完后的Android目錄:
ReactWebViewManager.java中需要修改幾個地方
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( //在導航變化的時候,dispatchEvent webView, new TopCanGoBackEvent( webView.getId(), createCanGoBackWebViewEvent(webView, url))); } } }
TopCanGoBackEvent是我自己添加的一個Event,專門用來通知導航變化
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中添加這個模塊
public class MainApplication extends Application implements ReactApplication { @Override protected List<ReactPackage> getPackages() { return Arrays.<ReactPackage>asList( new MainReactPackage(), new ReactWebViewPackage() //WebView ); } }
以上就是Android需要修改的地方,ios我沒有嘗試過,應該大差不差同一個道理。
2. 拷貝下圖中的文件到我們自己項目中的JS代碼目錄下,并修改一下名字
JS代碼目錄:
CustomWebView.android.js 有幾個地方需要修改。
/** * 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 //此處需要修改名稱 */ var RCT_WEBVIEW_REF = 'webview1'; //此處需要修改名稱 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); //修改名稱 module.exports = CustomWebView; //修改名稱
至此就完成自定義WebView模塊。也可以解決網頁是React實現,不能導航的問題。
感謝你能夠認真閱讀完這篇文章,希望小編分享的“如何解決react-native WebView返回處理問題”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。