通过从 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 的内容中被调用。