【问题标题】:React Native: Extend WebViewReact Native:扩展 WebView
【发布时间】:2017-12-14 16:29:11
【问题描述】:

(关于this question

我想在 React Native 中创建一个组件,它具有与原生 WebView 完全相同的功能,只是覆盖了它的一些方法。

我遵循了 React Native 页面中的教程,with modulesUI Views(我认为这是正确的方法),但我无法得到我想要的。

我能做到的:

// .../com/project/permissionwebview/    
public class PermissionWebviewView extends LinearLayout{

    private Context context;

    public PermissionWebviewView(Context context) {
        super(context);
        this.context = context;
        init();
    }

    public void init() {
        inflate(this.context, R.layout.permissionwebview, this);
    }
}

还有我的视图经理:

// .../com/project/permissionwebview
public class PermissionWebviewViewManager extends SimpleViewManager<PermissionWebviewView> {

    public static final String REACT_CLASS = "PermissionWebviewViewManager";

    @Override
    public String getName() {
        return REACT_CLASS;
    }

    @Override
    public PermissionWebviewView createViewInstance(ThemedReactContext context) {
        return new PermissionWebviewView(context); //If your customview has more constructor parameters pass it from here.
    }
}

布局的 XML:

// .../res/layout/permissionwebview.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >
    <WebView android:id="@+id/permission_webview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
    />
</LinearLayout>

我可以从 React Native 调用它,但什么都没有显示,也没有任何反应。

我想要的只是一个调用本机元素的自定义元素(在本例中为WebView),因此它呈现完全相同(相同的道具,相同的方法),只是在必要时覆盖它的一些方法(在我的情况,onPermissionRequest())。

哪种方法是正确的?如何(以及在​​何处)调用父/超级 WebView?

编辑 1

我想我已经让它运行了,但是我不知道这是否是正确的方法,因为我仍然不知道如何传递所有参数 从 React Native 组件 (&lt;PermissionWebview /&gt;) 到原生的 WebView 元素:

// .../com/project/permissionwebview/
public class PermissionWebviewView extends LinearLayout{

    private Context context;
    private WebView myWebView;

    public PermissionWebviewView(Context context) {
        super(context);
        this.context = context;
        init();
    }

    public void init() {
        inflate(this.context, R.layout.permissionwebview, this);

        myWebView = (WebView)findViewById(R.id.permission_webview);

        myWebView.setWebChromeClient(new WebChromeClient() {
            @Override
            public void onPermissionRequest(final PermissionRequest request) {
                request.grant(request.getResources());
            }
        });

        myWebView.loadUrl("https://staticWebSite.Until/Parameters/Are/Passed");
    }
}

谢谢!

【问题讨论】:

    标签: android react-native webview super subclassing


    【解决方案1】:

    通过从 GitHub 上的 react-native-website 存储库中的这些信息开始,我最终能够在 Android 上获得扩展的 WebView。很多代码来自链接上的示例以及WebView 的实际 React Native 实现。

    我计划提交一个拉取请求,其中包含我需要做的一些更改,以便实际构建 Java,并检索对我正在包装的实际 WebView 的所需引用(不是每个人都需要这个)。

    请注意,某些类是嵌套的,名称需要比 GitHub 中的示例进一步限定:

    这是我必须做的。在我的情况下,我需要将 App Cache Manifest 功能添加到 WebView。您可能不需要所有这些 import 语句,并且可能需要添加其他语句:

    CustomWebViewManager.java:

    import android.os.Build;
    import android.view.ViewGroup.LayoutParams;
    import android.webkit.ConsoleMessage;
    import android.webkit.GeolocationPermissions;
    import android.webkit.WebChromeClient;
    import android.webkit.WebView;
    import android.webkit.WebViewClient;
    import android.webkit.JavascriptInterface;
    import android.webkit.ValueCallback;
    import android.webkit.WebSettings;
    import android.webkit.CookieManager;
    
    import com.facebook.react.common.build.ReactBuildConfig;
    import com.facebook.react.module.annotations.ReactModule;
    import com.facebook.react.uimanager.ThemedReactContext;
    import com.facebook.react.views.webview.WebViewConfig;
    import com.facebook.react.views.webview.ReactWebViewManager;
    
    @ReactModule(name = CustomWebViewManager.REACT_CLASS)
    public class CustomWebViewManager extends ReactWebViewManager {
      protected static final String REACT_CLASS = "MyCustomWebView";
    
      protected static class CustomReactWebViewClient extends ReactWebViewManager.ReactWebViewClient { }
    
      protected static class CustomReactWebView extends ReactWebViewManager.ReactWebView {
        public CustomReactWebView(ThemedReactContext reactContext) {
          super(reactContext);
        }
      }
    
      public CustomWebViewManager() {
        mWebViewConfig = new WebViewConfig() {
          public void configWebView(WebView webView) {
          }
        };
      }
    
      public CustomWebViewManager(WebViewConfig webViewConfig) {
        mWebViewConfig = webViewConfig;
      }
    
      @Override
      public String getName() {
        return REACT_CLASS;
      }
    
      @Override
      protected ReactWebView createReactWebViewInstance(ThemedReactContext reactContext) {
        return new CustomReactWebView(reactContext);
      }
    
      // I had to override this in order to enable my needed WebView functionality
      @Override
      protected WebView createViewInstance(ThemedReactContext reactContext) {
        ReactWebView webView = createReactWebViewInstance(reactContext);
    
        webView.setWebChromeClient(new WebChromeClient() {
          @Override
          public boolean onConsoleMessage(ConsoleMessage message) {
            if (ReactBuildConfig.DEBUG) {
              return super.onConsoleMessage(message);
            }
            // Ignore console logs in non debug builds.
            return true;
          }
    
          @Override
          public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
            callback.invoke(origin, true, false);
          }
        });
        reactContext.addLifecycleEventListener(webView);
        mWebViewConfig.configWebView(webView);
        webView.getSettings().setBuiltInZoomControls(true);
        webView.getSettings().setDisplayZoomControls(false);
        webView.getSettings().setDomStorageEnabled(true);
    
        webView.getSettings().setAppCacheMaxSize(1024*1024*8);
        webView.getSettings().setAllowFileAccess(true);
        webView.getSettings().setAppCacheEnabled(true);
        // not sure if the below is needed, but at least one post indicated that it was so I did this with my package name to be safe
        webView.getSettings().setAppCachePath("data/data/<your package name here>/cache");
        webView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
        webView.getSettings().setAllowFileAccessFromFileURLs(true);
    
        // Fixes broken full-screen modals/galleries due to body height being 0.
        webView.setLayoutParams(
                new LayoutParams(LayoutParams.MATCH_PARENT,
                    LayoutParams.MATCH_PARENT));
    
        if (ReactBuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
          WebView.setWebContentsDebuggingEnabled(true);
        }
    
        return webView;
      }
    
      @Override
      protected void addEventEmitters(ThemedReactContext reactContext, WebView view) {
        // Do not register default touch emitter and let WebView implementation handle touches
        view.setWebViewClient(new CustomReactWebViewClient());
      }
    }
    

    CustomWebViewPackage.java

      import java.util.Arrays;
      import java.util.Collections;
      import java.util.ArrayList;
      import java.util.List;
    
      import com.facebook.react.ReactPackage;
      import com.facebook.react.bridge.NativeModule;
      import com.facebook.react.bridge.ReactApplicationContext;
      import com.facebook.react.uimanager.ViewManager;
    
      public class CustomWebViewPackage implements ReactPackage {
          @Override
          public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
            return Collections.emptyList();
          }
    
          @Override
          public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
            return Arrays.<ViewManager>asList(new CustomWebViewManager());
          }
      }
    

    CustomWebView.jsx

      'use strict';
    
      import * as PropTypes from 'prop-types';
      import * as React from 'react';
      import {
        requireNativeComponent,
        WebView
      } from 'react-native';
    
      /**
       * Renders a native WebView via our custom wrapper. 
       * Necessary because the RN WebView does not turn on the App Cache functionality by default. 
       * Can't make this a .tsx file due to TS problems around spreading the this.props into the child WebView.
       */
      class CustomWebView extends React.Component {
        static propTypes = WebView.propTypes;
    
        render() {
          return (
            <WebView 
              {...this.props}
              ref={(view) => {
                this.props.wrappedRef(view);
              }}
              nativeConfig={{component: MyCustomWebView}} 
            />
          );
        }
      }
    
      const MyCustomWebView = requireNativeComponent('MyCustomWebView', CustomWebView, 
        WebView.extraNativeComponentConfig);
    
      export default CustomWebView;
    

    注意wrappedRef 属性。我添加了这个,因为我的CustomWebView 的消费者需要对实际正在渲染的WebView 的引用。这是因为它需要引用那里的功能,例如postMessage()。消费者传递一个箭头函数,该函数设置对消费类中属性的引用。如您所见,该函数在传递给ref 的内容中被调用。

    【讨论】:

    • 你也有ios实现吗?
    • @Kaushik 不抱歉。
    猜你喜欢
    • 2020-10-08
    • 1970-01-01
    • 1970-01-01
    • 2016-12-26
    • 1970-01-01
    • 2018-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多