React 原生 webview 组件在 Android 上无法打开链接,但在 IOS 上可以正常工作,没有任何麻烦。Android 上什么都没有发生,按下后,没有任何错误。同时我尝试用 react native 超链接打开链接,效果很好。你能告诉我,我该如何解决?
const IS_IOS = Platform.OS === 'ios';
const script = `
;(function() {
var wrapper = document.createElement("div");
wrapper.id = "height-wrapper";
while (document.body.firstChild) {
wrapper.appendChild(document.body.firstChild);
}
document.body.appendChild(wrapper);
var i = 0;
function click(e) {
e.preventDefault();
window.postMessage(click, '*');
};
function updateHeight() {
document.title = wrapper.scrollHeight;
window.location.hash = ++i;
}
function linkProcessed() {
var links = document.getElementsByTagName('a');
for (var i = 0; i < links.length; i++) {
links[i].onclick = function (e) {
e.preventDefault();
window.ReactNativeWebView.postMessage(e.currentTarget.getAttribute("href"), '*');
}
}
var links = document.getElementsByTagName('img');
for (var i = 0; i < links.length; i++) {
links[i].onclick = function (e) {
e.preventDefault();
window.ReactNativeWebView.postMessage(e.currentTarget.getAttribute("src"), '*');
}
}
}
updateHeight();
setTimeout(linkProcessed, 1000);
window.addEventListener("load", function() {
updateHeight();
setTimeout(updateHeight, 1000);
});
window.addEventListener("resize", updateHeight);
}());
`;
const postMessage = `(function() {
var originalPostMessage = window.postMessage;
var patchedPostMessage = function(message, targetOrigin, transfer) {
originalPostMessage(message, targetOrigin, transfer);
};
patchedPostMessage.toString = function() {
return String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage');
};
window.postMessage = patchedPostMessage;
})();`;
const style = `
<style>
body, html, #height-wrapper {
margin: 0;
padding: 0;
}
#height-wrapper {
position: absolute;
top: 0;
left: 0;
right: 0;
}
</style>
<script>
${script}
</script>
`;
const textStyleToCss = (textStyle: StyleProp<TextStyle>, styles?: StyleProp<ViewStyle>): string => {
if (!textStyle) {
return '';
}
const {
fontSize,
fontFamily,
fontUrl,
lineHeight,
letterSpacing,
color,
} = StyleSheet.flatten(textStyle) as any;
const {
backgroundColor,
} = StyleSheet.flatten(styles) as any;
const css = IS_IOS ?
`<style type="text/css">@font-face {font-family: MyFont; font-style: normal;
font-weight: 400; src: local('${fontFamily}'), url('${fontUrl}');}
body,* {
font-family: MyFont; font-size: ${fontSize}pt;text-align: left; line-height: ${lineHeight}pt; letter-spacing: ${letterSpacing}pt; color: ${color};
background-color: ${backgroundColor};
}
img{height: auto;max-width: 100%;}
iframe {height: auto;max-width: 100%;}
div{height: auto;max-width: 100%;}
</style>` :
`<style type="text/css">@font-face {font-family: MyFont; font-style: normal;
font-weight: 400; src: url("${fontUrl}");}
body,* {
font-family: MyFont; font-size: ${fontSize}px;text-align: left; line-height: ${lineHeight}px; letter-spacing: ${letterSpacing}px; color: ${color};
background-color: ${backgroundColor};
}
img{height: auto;max-width: 100%;}
iframe {height: auto;max-width: 100%;}
div{height: auto;max-width: 100%;}
</style>`;
return css;
};
const markdownLinksMap = (text: string) => {
const markdownLinkReg = /\[([^\[\]]+)\]\(([^)]+)\)/img;
text = text.replace(markdownLinkReg, (substring, ...args) => {
const title = args[0];
const url = args[1];
const linkKey = `<a href="${url}">${title}</a>`;
return linkKey;
});
return text;
};
const codeInject = (args: { html: string, textStyle?: StyleProp<TextStyle>, style?: StyleProp<ViewStyle> }): string => {
const textWithoutMarkdown = markdownLinksMap(args.html);
return (`
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
${textStyleToCss(args.textStyle, args.style)}
</head>
<body>${'<div style="color: ' + args.textStyle!.color + '">' + textWithoutMarkdown + '</div>'}${style}</body>
`)
};
interface TextStyleProp extends TextStyle {
fontSize: number;
fontFamily: string;
lineHeight: number;
letterSpacing: number;
color: string;
fontUrl: string;
}
interface Props {
html: string;
navigateToWebview?: (url: string) => void;
style?: StyleProp<ViewStyle>;
textStyle?: StyleProp<TextStyleProp>;
}
interface State {
correctHtml: string;
height: number;
}
export class AutoHeightWebView extends React.Component<Props, State> {
public static getDerivedStateFromProps(props: Props, state: State) {
if (props.html) {
const correctHtml = codeInject(props);
return { correctHtml };
}
return null;
}
private webview: any;
constructor(props: Props) {
super(props);
this.state = {
correctHtml: codeInject(props),
height: 120,
};
}
public componentWillUnmount(): void {
if (this.webview) {
this.webview.stopLoading();
this.webview = null;
}
}
public render() {
if (!(this.props.html && this.props.html.length > 0)) {
return (<View />);
} else {
return (
<View style={[SS.container, this.props.style]}>
<WebView
ref={this.setWebView}
automaticallyAdjustContentInsets={true}
source={{ html: this.state.correctHtml, baseUrl: '' }}
onMessage={this.onMessage}
javaScriptEnabled={true}
injectedJavaScript={`${postMessage};document.body.scrollHeight;`}
style={{ height: this.state.height }}
onNavigationStateChange={this.onNavigationChange}
domStorageEnabled={true}
scalesPageToFit={Platform.select({ios: undefined, android: true})}
startInLoadingState={true}
scrollEnabled={true}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
bounces={false}
originWhitelist={['*']}
/>
</View>
);
}
}
private onNavigationChange = (event: NavState): void => {
if (event.title) {
const htmlHeight = Number(event.title); //convert to number
if (htmlHeight && this.state.height !== htmlHeight) {
this.setState({ height: htmlHeight });
}
}
}
private onMessage = (event: NativeSyntheticEvent<WebViewMessageEventData>): void => {
const url = event.nativeEvent.data;
if (this.props.navigateToWebview) {
this.props.navigateToWebview(url);
} else {
Linking.openURL(url);
}
}
private setWebView = (webview: any): void => {
this.webview = webview;
};
}
const SS = EStyleSheet.create({
container: { flex: 1, overflow: 'hidden' },
});