react-native表格自適應

需求背景:react-native的ScrollView組件內嵌入WebView,縱向的FlatList切換item時切換WebView的url。
難點:解決WebView和外部ScrollView的滑動衝突

<ScrollView>
	<FlatList />
	<WebView />
</ScrollView>

在這裏插入圖片描述
方案一: WebView的高度、寬度固定,手勢落在WebView內時滑動時阻止外部ScrollView的滑動事件,手勢離開時恢復ScrollView的滑動事件。

<ScrollView scrollEnabled={this.state.scrollEnabled}>
	<WebView 
       source={{uri: currentUrl}}
          style={styles.webview}
          scalesPageToFit={false}
          onLoad={() => {this.setSafeState({load: true})}}
          onTouchStart={() => {this.setSafeState({scrollEnabled: false})}}
          onTouchMove={() => {this.setSafeState({scrollEnabled: false})}}
          onTouchEnd={() => {this.setSafeState({scrollEnabled: true})}}
     />
	...
</ScrollView>

問題:由於頻繁的滑動手勢,setState的異步效率過慢導致scrollEnabled並沒有隨着手勢改變而立馬改變,導致在WebView內部上下滑動時,整個頁面(ScrollView)也會跟着滑動。
解決方案:react-native的直接操作setNativePropsAPI

setScrollEnabled = (bool) => {
	this._WebViewCom.setNativeProps({scrollEnabled: bool})
}

<ScrollView scrollEnabled={this.state.scrollEnabled}>
	<WebView 
       source={{uri: currentUrl}}
          style={styles.webview}
          scalesPageToFit={false}
          bounces={false}//webview 內容到達底部時是否進行回彈
          ref={component => {this._WebViewCom = component}}
          onLoad={() => {this.setSafeState({load: true})}}
          onTouchStart={this.setScrollEnabled.bind(this, false)}
          onTouchMove={this.setScrollEnabled.bind(this, false)}
          onTouchEnd={this.setScrollEnabled.bind(this, true)}
     />
	...
</ScrollView>

方案二:設置WebView的高度和表格的高度一樣,這樣也可以完美避開ScrollView和WebView的滑動衝突,WebView內表格還可以左右滑動。

1、使用postMessage實現native和webView的通信,webView告訴native表格的高度,naitve再設置webView的高度。

 // postMessage接收表格的高度,實現表格高度自適應
 onMessage = (e) => {
     const event = e.nativeEvent;
     const height = parseInt(event.data);
     this.setSafeState({height});
 }


<WebView 
	onMessage={this.onMessage}
    source={{uri: currentUrl}}
    style={[styles.webview, height > 0 && {height}]}
    onLoad={() => {this.setSafeState({load: true})}}
    javaScriptEnabled={true}
    onError={this.onError}
    injectedJavaScript={`
        // 頁面自適用
        let metaTag=document.createElement('meta');
        metaTag.name = "viewport"
        metaTag.content = "width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
        document.getElementsByTagName('head')[0].appendChild(metaTag);
        // 獲取表格的高度
        let height = window.getComputedStyle(document.getElementsByTagName('table')[0]).height;
        let h = parseInt(height) + 44;
        window.ReactNativeWebView.postMessage(h);
    `}
/>

問題: WebView頁面是load完成之後從執行injectedJavaScript的js代碼,所以WebView是有一個固定的高度,等接收到postMessage傳過來的height之後,將WebView的高度設成這個height。一開始比較快,但是切換WebView的url時,如果表格比較大會很慢。

解決方案:讓後端在返回的url的h5的<head>里加上頁面自適應的代碼:

<meta name="viewport" content="height=device-height, initial-scale=1, maximum-scale=1.0, user-scalable=0" />

並且頁面onload完之後返回h5表格的高度, (也是加在h5的<hea>下)

<script>
    window.onload=function(){
        let height = window.getComputedStyle(document.getElementsByTagName('table')[0]).height;
        let h = parseInt(height, 10);
        document.title = h;
    }
</script>

react-native這邊使用onNavigationStateChange(當導航狀態發生變化的時候調用),頁面不再需要之前使用postMessage那樣等頁面onload完之後執行js才能拿到height。

// 接收表格的高度,實現表格高度自適,title的值爲table的高度
onNavigationStateChange = (navState) => {
   if (navState.title) {
       const height = parseInt(navState.title, 10) || 0; // turn NaN to 0
       this.setSafeState({height: height})
   }
}

<WebView 
	source={{uri: currentUrl}}
    style={[styles.webview, height > 0 && {height}]}
    onLoad={() => {this.setSafeState({load: true})}}
    onError={this.onError}
    bounces={false}
    onNavigationStateChange={this.onNavigationStateChange}
/>

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章