在 RN 中有兩種常見的測量組件尺寸方式:
有些時候會需要知道當前視圖的實際尺寸為何就可以用到 onLayout
。
當組件佈局發生變化時(例如組件的尺寸、位置等改變),React Native 會調用 onLayout 函數,就能獲得組件的新尺寸。
RN 內建的以下幾個組件都具有 onLayout 屬性:
onLayout?: ((event: LayoutChangeEvent) => void) | undefined
LayoutChangeEvent
中有 width, height, x, y 四個值,分別代表:
import { View, Image, Text, type LayoutChangeEvent } from "react-native"
export const App = () => {
const onLayout = (event: LayoutChangeEvent) => {
const { width, height, x, y } = event.nativeEvent.layout
console.log(width, height, x, y); // 320 320 80 349.3333435058594
}
return (
<View style={{ flex: 1 }}>
<View style={{ backgroundColor: 'white', padding: 10 }} onLayout={onLayout}>
<Image source={{ uri: 'https://placehold.co/300x300.png' }} width={300} height={300} />
</View>
</View>
)
}
以剛剛的例子 console.log(width, height, x, y)
輸出 320 320 80 349.3333435058594
300
+ 水平 padding 10*2 = 20
改變下方這些樣式都會影響 onLayout 重新計算 View 的大小和位置
可以將獲取組件大小的邏輯抽象出來成一個 custom hook:
// hooks/useComponentSize.tsx
import { useState, useCallback } from 'react'
import { View, Image, type LayoutChangeEvent } from 'react-native'
export const useComponentSize = () => {
const [size, setSize] = useState({ width: 0, height: 0 })
const onLayout = useCallback((event: LayoutChangeEvent) => {
const { width, height } = event.nativeEvent.layout
setSize({ width, height })
}, [])
return [size, onLayout] as const
}
// App.tsx
export const App = () => {
const [size, onLayout] = useComponentSize()
return (
<View style={{ backgroundColor: 'white', padding: 10 }} onLayout={onLayout}>
<Image source={{ uri: 'https://placehold.co/300x300.png' }} width={300} height={300} />
</View>
)
}
measure 跟 onLayout 一樣可以用於獲取組件尺寸和位置,不同的是 measure 會需要使用 ref 來獲得組件的引用,然後再使用 ref.measure 方法來獲取組件的尺寸和位置。
ref.current.measure((x, y, width, height, pageX, pageY) => { // ... })
width
, height
: 元素的寬高pageX
, pageY
: 相對於整個頁面的位置x
, y
: 相對於父組件的位置import { useRef, useEffect } from "react"
import { View, Image } from "react-native"
export const ViewPage = () => {
const viewRef = useRef(null)
useEffect(() => {
handleMeasure()
}, [viewRef])
const handleMeasure = () => {
if (viewRef.current) {
viewRef.current.measure((x, y, width, height, pageX, pageY) => {
console.log(width, height, pageX, pageY)
})
}
}
return (
<View ref={viewRef} style={{ backgroundColor: 'white', padding: 10 }}>
<Image source={{ uri: 'https://placehold.co/300x300.png' }} width={300} height={300} />
</View>
)
}
如果希望頁面內容高度在小於設備屏幕高度時無需滾動,但在頁面內容高度小於設備屏幕高度時要可以滾動的話,就可以使用 onLayout 來和當前設備屏幕高度做比較:
enabled={contentHeight > height}
import React, { useState } from 'react'
import { Platform, useWindowDimensions } from 'react-native'
import { ScrollView } from 'react-native-gesture-handler'
export const ContentLayout = () => {
const { height } = useWindowDimensions()
const [contentHeight, setContentHeight] = useState(0)
const onLayout = (event: any) => {
const { height } = event.nativeEvent.layout
setContentHeight(height)
}
return (
<ScrollView
contentContainerStyle={{ flexGrow: 1 }}
enabled={contentHeight > height}
onLayout={onLayout}
>
{children}
</ScrollView>
)
}
onLayout
measure