Maison > interface Web > js tutoriel > Gestion de la méthode de retour sans rappel via WebView dans React-Native

Gestion de la méthode de retour sans rappel via WebView dans React-Native

亚连
Libérer: 2018-06-04 11:42:09
original
2178 Les gens l'ont consulté

Cet article présente principalement l'explication détaillée du traitement des retours WebView natif de réaction (peut être résolu par des méthodes sans rappel). Maintenant, je le partage avec vous et le donne comme référence.

1. Préface

Certaines pages du projet changent fréquemment. Nous envisagerons d'utiliser des pages Web pour résoudre ces pages.

Fournissez une page Web publique dans le projet RN S'il s'agit de contenu Web, accédez à cette interface pour l'afficher.

Un problème en ce moment est que la page Web aura une page de premier niveau et une page de deuxième niveau, qui seront conçues pour gérer la touche retour de la barre de navigation (et la gestion du retour clé sur Android).

La solution à ce problème se trouve sur le site officiel du RN. Utilisez simplement la méthode de rappel onNavigationStateChange pour enregistrer l'état de navigation actuel, afin de déterminer s'il faut revenir à la page précédente ou quitter cette page Web et revenir à d'autres interfaces de l'application.

Cependant, lorsque l'implémentation de la page Web est React, il y aura un problème. Vous constaterez que lorsque la page saute, la méthode de rappel onNavigationStateChange n'a pas de rappel ! ! ! Comme c'est gros ! !

Au début, j'ai essayé de changer l'adresse Web pour celle de Baidu, qui peut recevoir des rappels. Tout s'est très bien passé, mais cela n'a pas fonctionné si je l'ai changé pour notre lien, j'ai donc rejeté la faute sur le. backend, pensant que c'était React. Quelle partie est mal écrite.

Comme le dernier projet était serré et que je n'ai pas eu le temps de regarder le code source, j'ai pensé à une solution pas très complète, qui consiste à utiliser js sur la page web pour rappeler l'App pour informer l'état actuel de la navigation. Cette solution L'affichage n'est pas convivial.

Maintenant que j'ai eu le temps de lire le code source, je peux comprendre la vraie raison.

2. Raisons

Laissez-moi analyser la cause de ce problème et ma solution.

1. Tout d'abord, trouvez l'emplacement du code source.

node_modulesreact-nativeReactAndroidsrcmainjavacomfacebookreactviewswebview

node_modulesreact-nativeLibrariesComponentsWebView

La structure des répertoires est la suivante :

2. Segment de code implémenté (côté JAVA)

Le code en cours d'exécution de RN est du code natif, donc certains rappels d'événements comme le composant WebView, en fait Ils sont tous déclenchés par des rappels en code natif. Comme suit

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

  //...
 }
Copier après la connexion

(ReactWebViewManager.java) rn version 0.43.3, RN Il y aura des ajustements de code dans différentes versions, donc lors de la mise à niveau de RN, des tests de régression minutieux sont nécessaires.

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

  //...
 }
Copier après la connexion

(TopLoadingStartEvent.java) Événement JS de rappel

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);
 }
}
Copier après la connexion

(node_modulesreact-nativeReactAndroidsrcmainjavacomfacebookreactuimanagerUIManagerModuleConstants.java)

Dans ce fichier, la relation correspondante est définie

/**
 * 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();
 }
}
Copier après la connexion

3. Code d'implémentation extrait (côté JS)

(node_modulesreact-nativeLibrariesComponentsWebViewWebView.android.js)

Dans le code ci-dessous, vous pouvez voir que seuls onLoadingStart et onLoadingFinish appelleront updateNavigationState, et le problème survient Ici, puisque notre page web est implémentée en React, il n’y a qu’une seule page ! Ainsi, onLoadingStart et onLoadingFinish ne seront appelés qu'une seule fois. Cliquer à nouveau sur la page de détails ne passera pas à une nouvelle page, mais actualisera la page d'origine. Il n’y a donc pas de rappel 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(&#39;RCTWebView&#39;, WebView, {  //对应上面JAVA中的 ‘RCTWebView&#39;
 nativeOnly: { messagingEnabled: PropTypes.bool, }, });


 module.exports = WebView;
Copier après la connexion

2. Solution

Maintenant que la cause est trouvée, elle est facile à résoudre

Solution : personnalisez WebView, ajoutez le traitement doUpdateVisitedHistory et informez JS à chaque fois que la navigation change.

1. Copiez les fichiers de l'image ci-dessous dans le répertoire de code Android de notre propre projet

Le répertoire Android copié :

Il y a plusieurs endroits qui doivent être modifiés dans 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)));
    }
 }
}
Copier après la connexion

TopCanGoBackEvent est un événement que j'ai moi-même ajouté . Spécialement utilisé pour notifier les modifications de navigation

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);
 }
}
Copier après la connexion

Nouveau 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()
    );
  }
}
Copier après la connexion

Ensuite, ajoutez ce module dans MainApplication

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

Ce qui précède est ce qui doit être modifié pour Android, Je ne l'ai pas pour iOS, je l'ai essayé, et ça devrait être à peu près pareil.

2. Copiez le fichier dans l'image ci-dessous dans le répertoire de code JS de notre propre projet et changez le nom

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 = &#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; //修改名称
Copier après la connexion

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

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

在vue中如何解决v-for使用报红并出现警告的问题(详细教程)

在Vuejs中如何实现搜索匹配功能方法(详细教程)

在vue.js中利用select下拉框实现绑定和取值方法

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal