React Native自适应高度的WebView在安卓设备上无法正常工作。

3

我正在实现一个具有动态高度的WebView。我在iOS上找到了完美运行的解决方案,但在安卓上无法运行。该解决方案使用WV内部的JS将标题设置为内容高度的值。以下是代码:

...
this.state = {webViewHeight: 0};
...
<WebView
    source={{html: this.wrapWevViewHtml(this.state.content)}}
    style={{width: Dimensions.get('window').width - 20, height: this.state.webViewHeight}}
    scrollEnabled={false}
    javaScriptEnabled={true}
    injectedJavaScript="window.location.hash = 1;document.title = document.height;"
    onNavigationStateChange={this.onWebViewNavigationStateChange.bind(this)}
/>
...
onWebViewNavigationStateChange(navState) {
    // navState.title == height on iOS and html content on android
    if (navState.title) {
        this.setState({
            webViewHeight: Number(navState.title)
        });
    }
}
...

但是在安卓系统中,在 onWebViewNavigationStateChange 中 title 的值等于页面内容。
我做错了什么?
2个回答

5
我也感到困惑。它确实能够工作,但是很难调试为什么不起作用,因为Chrome远程调试在Android上的React Native WebViews上未启用。
我有两个问题:
1.注入到Webview的脚本包含一些单行注释,在Android上所有换行符都被删除了(另一个bug?)。这导致WebView中出现语法错误。
2.在第一次调用时,标题内容确实是Webview的完整内容。不知道为什么,但在后续调用中,它是高度。所以只需处理那种情况。
以下是我现在正在使用的代码,适用于React Native 0.22的Android和iOS。
import React, {WebView, View, Text} from "react-native";


const BODY_TAG_PATTERN = /\<\/ *body\>/;

// Do not add any comments to this! It will break line breaks will removed for
// some weird reason.
var 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 updateHeight() {
    document.title = wrapper.clientHeight;
    window.location.hash = ++i;
}
updateHeight();

window.addEventListener("load", function() {
    updateHeight();
    setTimeout(updateHeight, 1000);
});

window.addEventListener("resize", updateHeight);
}());
`;


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 codeInject = (html) => html.replace(BODY_TAG_PATTERN, style + "</body>");


/**
 * Wrapped Webview which automatically sets the height according to the
 * content. Scrolling is always disabled. Required when the Webview is embedded
 * into a ScrollView with other components.
 *
 * Inspired by this SO answer https://dev59.com/S1wY5IYBdhLWcg3wJU94#33012545
 * */
var WebViewAutoHeight = React.createClass({

    propTypes: {
        source: React.PropTypes.object.isRequired,
        injectedJavaScript: React.PropTypes.string,
        minHeight: React.PropTypes.number,
        onNavigationStateChange: React.PropTypes.func,
        style: WebView.propTypes.style,
    },

    getDefaultProps() {
        return {minHeight: 100};
    },

    getInitialState() {
        return {
            realContentHeight: this.props.minHeight,
        };
    },

    handleNavigationChange(navState) {
        if (navState.title) {
            const realContentHeight = parseInt(navState.title, 10) || 0; // turn NaN to 0
            this.setState({realContentHeight});
        }
        if (typeof this.props.onNavigationStateChange === "function") {
            this.props.onNavigationStateChange(navState);
        }
    },

    render() {
        const {source, style, minHeight, ...otherProps} = this.props;
        const html = source.html;

        if (!html) {
            throw new Error("WebViewAutoHeight supports only source.html");
        }

        if (!BODY_TAG_PATTERN.test(html)) {
            throw new Error("Cannot find </body> from: " + html);
        }

        return (
            <View>
                <WebView
                    {...otherProps}
                    source={{html: codeInject(html)}}
                    scrollEnabled={false}
                    style={[style, {height: Math.max(this.state.realContentHeight, minHeight)}]}
                    javaScriptEnabled
                    onNavigationStateChange={this.handleNavigationChange}
                />
                {process.env.NODE_ENV !== "production" &&
                <Text>Web content height: {this.state.realContentHeight}</Text>}
            </View>
        );
    },

});


export default WebViewAutoHeight;

这是一个名为“gist”的网站,可以让您轻松地共享代码和文本片段。你可以把你的代码放到gist上,并与他人分享链接。此外,您还可以使用gist来备份您的代码或创建代码片段以供以后使用。请点击此处了解更多信息。


如果你遇到了困难,需要知道为什么你的代码在Android上无法运行,我使用了这个评论:https://github.com/facebook/react-native/issues/14586#issuecomment-314172889 - MrBrownser

0

在设备上加载本地HTML文件并注入JS是我发现的唯一正确设置Android标题/哈希的方法。

/app/src/main/assets/blank.html

<!doctype html>
<html>
<head>
<title id="title">Go Web!</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
</style>
</head>
<body>
<div id="content"></div>
<script>
var content = document.getElementById('content');

var fireEvent = function(event, data) {
  document.title = data;
  window.location.hash = event;
};

var setContent = function(html) {
  content.innerHTML = html;
};
</script>
</body>
</html>

还有这个组件

class ResizingWebView extends Component {
  constructor(props) {
    super(props)

    this.state = {
      height: 0
    }
  }
  onNavigationStateChange(navState) {
    var event = navState.url.split('#')[1]
    var data = navState.title

    console.log(event, data)
    if (event == 'resize') {
      this.setState({ height: data })
    }
  }
  render() {
    var scripts = "setContent('<h1>Yay!</h1>');fireEvent('resize', '300')";
    return (
      <WebView
        source={{ uri: 'file:///android_asset/blank.html' }}
        injectedJavaScript={ scripts }
        scalesPageToFit={ false }
        style={{ height: this.state.height }}
        onNavigationStateChange={ this.onNavigationStateChange.bind(this) }
      />
    )
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接