Heim  >  Artikel  >  Web-Frontend  >  React-native WebView gibt die Verarbeitungsmethode zurück

React-native WebView gibt die Verarbeitungsmethode zurück

小云云
小云云Original
2018-03-01 09:12:562453Durchsuche

Der Inhalt einiger Seiten im Projekt ändert sich häufig. Wir werden die Verwendung von Webseiten zur Lösung dieser Seiten in Betracht ziehen.

Stellen Sie eine öffentliche Webseite im RN-Projekt bereit. Wenn es sich um Webinhalte handelt, springen Sie zur Anzeige zu dieser Schnittstelle.

Ein Problem zu diesem Zeitpunkt besteht darin, dass die Webseite eine Seite der ersten Ebene und eine Seite der zweiten Ebene haben wird, die für die Handhabung der Return-Taste der Navigationsleiste (und die Handhabung der Rückkehr) ausgelegt sind Schlüssel auf Android).

Die Lösung für dieses Problem finden Sie auf der offiziellen RN-Website. Verwenden Sie einfach die Rückrufmethode onNavigationStateChange, um den aktuellen Navigationsstatus aufzuzeichnen, um zu bestimmen, ob Sie zur vorherigen Seite zurückkehren oder diese Webseite verlassen und zu anderen Schnittstellen der App zurückkehren möchten.

Wenn die Implementierung der Webseite jedoch React ist, tritt ein Problem auf. Wenn die Seite springt, hat die Rückrufmethode onNavigationStateChange keinen Rückruf! ! ! Wie fett! !

Zuerst habe ich versucht, die Webadresse auf die von Baidu zu ändern, die Rückrufe empfangen kann, aber es hat nicht funktioniert, wenn ich sie auf unseren Link geändert habe, also habe ich die Schuld dem gegeben Backend, ich dachte, es sei React. Welcher Teil ist falsch geschrieben.

Da das letzte Projekt knapp war und ich keine Zeit hatte, mir den Quellcode anzusehen, dachte ich über eine nicht sehr vollständige Lösung nach, nämlich die Verwendung von js auf der Webseite, um die App zurückzurufen Informieren Sie sich über den aktuellen Navigationsstatus. Die Anzeige ist unfreundlich.

Jetzt, da ich etwas Zeit hatte, den Quellcode zu lesen, kann ich den wahren Grund verstehen.

Lassen Sie uns die Ursache dieses Problems und meine Lösung analysieren.

1. Suchen Sie zunächst den Speicherort des Quellcodes.

node_modulesreact-nativeReactAndroidsrcmainjavacomfacebookreactviewswebview

node_modulesreact-nativeLibrariesComponentsWebView

Die Verzeichnisstruktur ist wie folgt:

2. Implementiertes Codesegment (JAVA-Seite)

Der tatsächlich laufende Code von RN ist nativer Code, also tatsächlich einige Ereignisrückrufe wie die WebView-Komponente Sie werden alle durch Rückrufe im nativen Code ausgelöst. Wie folgt

(ReactWebViewManager.java) rn Version 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 Version 0.43.3, verschiedene Versionen von RN wird Codeanpassungen vornehmen, daher sind bei einem RN-Upgrade sorgfältige Regressionstests erforderlich.


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)

In dieser Datei ist die entsprechende Beziehung


/**
 * 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. Implementiertes Codesegment (JS-Seite)

(node_modulesreact-nativeLibrariesComponentsWebViewWebView.android.js)

Im folgenden Code können Sie sehen, dass nur onLoadingStart und onLoadingFinish updateNavigationState aufrufen. Das Problem tritt hier aufgrund der Implementierung unserer Webseite auf Reagieren Sie, es gibt nur eine Seite! Daher werden onLoadingStart und onLoadingFinish nur einmal aufgerufen. Durch erneutes Klicken auf die Detailseite wird nicht zu einer neuen Seite gesprungen, sondern die Originalseite wird aktualisiert. Es gibt also keinen updateNavigationState-Rückruf.


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(&#39;RCTWebView&#39;, WebView, {  //对应上面JAVA中的 ‘RCTWebView&#39;
 nativeOnly: { messagingEnabled: PropTypes.bool, }, });


 module.exports = WebView;

2. Lösung

Da nun die Ursache gefunden ist, ist sie leicht zu beheben

Lösungsmethode: WebView anpassen, doUpdateVisitedHistory-Verarbeitung hinzufügen und JS jedes Mal benachrichtigen, wenn sich die Navigation ändert.

1. Kopieren Sie die Dateien im Bild unten in das Android-Codeverzeichnis in unserem eigenen Projekt

Das kopierte Android-Verzeichnis:

Es gibt mehrere Stellen, die in ReactWebViewManager.java geändert werden müssen


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 ist ein Ereignis, das ich selbst speziell für hinzugefügt habe Navigationsänderungen benachrichtigen

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);
 }
}

Neue 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()
    );
  }
}

Dann fügen Sie dieses Modul in MainApplication hinzu


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

Das oben Gesagte muss für Android geändert werden. Für iOS sollte es ganz anders sein .

2. Kopieren Sie die Datei im Bild unten in das JS-Codeverzeichnis in unserem eigenen Projekt und ändern Sie den Namen

JS-Codeverzeichnis:

CustomWebView.android.js weist mehrere Stellen auf, die geändert werden müssen.


/**
 * 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 = &#39;webview1&#39;; //此处需要修改名称

 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(&#39;RCTWebView1&#39;, CustomWebView, CustomWebView.extraNativeComponentConfig); //修改名称

module.exports = CustomWebView; //修改名称

至此就完成自定义WebView模块。也可以解决网页是React实现,不能导航的问题。

Das obige ist der detaillierte Inhalt vonReact-native WebView gibt die Verarbeitungsmethode zurück. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn