ホームページ > 記事 > ウェブフロントエンド > React-Native WebViewのリターン処理を呼び出さずに解決する方法
今回は、react-native WebView の return 処理を呼び出さずに解決する方法を紹介します。見てください。
1. はじめに プロジェクトには頻繁に変更されるページコンテンツがいくつかあります。これらのページを解決するために Web ページの使用を検討します。
RN プロジェクトに公開 Web ページを提供します。Web コンテンツの場合は、このインターフェイスにジャンプして表示します。
現時点での問題は、Web ページに第 1 レベルのページと第 2 レベルのページがあり、ナビゲーション バーのリターン キー (および
Androidのリターン キー) を処理するように設計されていることです。 この問題の解決策は、RN 公式 Web サイトで見つけることができます。 onNavigationStateChange コールバック メソッドを使用して現在のナビゲーション状態を記録し、前のページに戻るか、この Web ページを終了してアプリの他のインターフェイスに戻るかを決定します。
しかし、Web ページの実装が React である場合、ページがジャンプするときに onNavigationStateChange コールバック メソッドにコールバックがないことがわかります。 ! !なんて太ったんだろう! !
最初は、Web アドレスをコールバックを受信できる Baidu のものに変更しようとしましたが、すべてうまくいきましたが、リンクに変更するとうまくいかなかったので、誰が責任を負うかをバックエンドに転嫁しました。リアクションを間違って書きました。
前回のプロジェクトはタイトで、ソースコードをじっくり見る時間がなかったため、あまり完全ではない解決策を思いつきました。それは、Web ページ上で js を使用してアプリをコールバックするというものです。現在のナビゲーションのステータスを通知します。このソリューションはフレンドリーではありません。
今、ソースコードを読んで本当の理由を見つける時間が少しあります。
2. 理由
この問題の原因と解決策を分析しましょう。1. まず、ソースコードの場所を見つけます。
node_modulesreact-nativeReactAndroidsrcmainjavacomfacebookreactviewswebview
node_modulesreact-nativeLibrariesComponentsWebView
ディレクトリ構造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) Callback 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_modulesreact-nativeReactAndroidsrcmainjavacomfacebookreactuimanagerUIManagerModuleConstants.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.側)
(node_modulesreact -nativeLibrariesComponentsWebViewWebView.android.js)以下のコードでは、onLoadingStart と onLoadingFinish だけが updateNavigationState を呼び出すことがわかります。Web ページは React で実装されているため、ページは 1 つだけです。したがって、onLoadingStart と onLoadingFinish は 1 回だけ呼び出されます。詳細ページをもう一度クリックしても、新しいページにはジャンプせず、元のページが更新されます。したがって、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; //修改名称
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
以上がReact-Native WebViewのリターン処理を呼び出さずに解決する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。