這是我在2023第十五屆 iThome 鐵人賽發表的系列文章。
https://ithelp.ithome.com.tw/users/20136637/ironman/6408
React Native 內建的點擊類組件(統稱為Touchables*
)有以下幾種:
- TouchableHighlight
- TouchableOpacity
- TouchableWithoutFeedback
- TouchableNativeFeedback
- Button
- Pressable
雖然點擊類的組件有這麼多個,但官方建議在未來改為使用 Pressable,因為 Pressable 涵蓋了所有 Touchables* 的功能。
![React Native 奇幻之旅(3)-內建組件 Touchables* 24 dwK0Gqt](https://i.imgur.com/dwK0Gqt.png)
Touchables* 和 Pressable 的差異
TouchableWithoutFeedback
和Pressable
預設點擊是沒有回饋效果的。
TouchableOpacity | TouchableHighlight | TouchableNativeFeedback |
---|---|---|
![]() | ![]() | ![]() |
點擊時會變透明 | 點擊時會變色(underlayColor) | 點擊時會有波紋效果(僅限 Android) |
TouchableWithoutFeedback | Pressable |
---|---|
![]() | ![]() |
預設點擊沒有回饋效果 | 預設點擊沒有回饋效果 |
Pressable
可以自定義點擊交互效果(包括單擊、長按…等),Touchables*
是各司其職TouchableNativeFeedback
,TouchableWithoutFeedback
本身是沒有辦法設置樣式的,因為它們是用來處理點擊時的反饋效果的。
import React from "react"
import { StyleSheet, Text, TouchableOpacity, TouchableHighlight, TouchableNativeFeedback, TouchableWithoutFeedback, Pressable } from "react-native"
const App = () => {
return (
<>
{/* TouchableOpacity */}
<TouchableOpacity style={styles.button} onPress={() => console.log('TouchableOpacity')}>
<Text>TouchableOpacity</Text>
</TouchableOpacity>
{/* TouchableHighlight */}
<TouchableHighlight style={styles.button} onPress={() => console.log('TouchableHighlight')}>
<Text>TouchableHighlight</Text>
</TouchableHighlight>
{/* TouchableNativeFeedback */}
<TouchableNativeFeedback style={styles.button} onPress={() => console.log('TouchableNativeFeedback')}>
<Text>TouchableNativeFeedback</Text>
</TouchableNativeFeedback>
{/* TouchableWithoutFeedback */}
<TouchableWithoutFeedback style={styles.button} onPress={() => console.log('TouchableWithoutFeedback')}>
<Text>TouchableWithoutFeedback</Text>
</TouchableWithoutFeedback>
{/* Pressable */}
<Pressable style={styles.button} onPress={() => console.log('Pressable')}>
<Text>Pressable</Text>
</Pressable>
</>
)
}
export default App
const styles = StyleSheet.create({
button: {
padding: 10,
backgroundColor: 'white',
borderRadius: 4,
marginVertical: 10
}
})
需要使用 View
<>
{/* TouchableNativeFeedback */}
<TouchableNativeFeedback onPress={() => console.log('TouchableNativeFeedback')}>
<View style={styles.button}>
<Text>TouchableNativeFeedback</Text>
</View>
</TouchableNativeFeedback>
{/* TouchableWithoutFeedback */}
<TouchableWithoutFeedback onPress={() => console.log('TouchableWithoutFeedback')}>
<View style={styles.button}>
<Text>TouchableWithoutFeedback</Text>
</View>
</TouchableWithoutFeedback>
</>
Pressable 實現所有 Touchables*
TouchableWithoutFeedback
不需要做任何設置,因為 Pressable 本身也沒有點擊回饋效果。
TouchableOpacity
Pressable 的 style 有一個 pressed 的屬性,用於判斷是否被點擊,當點擊時改變透明度即可達到 TouchableOpacity
的效果。
<Pressable
style={({ pressed }) => [
styles.button,
{ opacity: pressed ? 0.5 : 1 }
]}
onPress={() => console.log('Pressable')}
>
<Text>Pressable</Text>
</Pressable>
TouchableHighlight
跟 TouchableOpacity
實現方式一樣
<Pressable
style={({ pressed }) => [
styles.button,
{ backgroundColor: pressed ? 'skyblue' : 'white' }
]}
onPress={() => console.log('Pressable')}
>
<Text>Pressable</Text>
</Pressable>
TouchableNativeFeedback
設置 android_ripple
prop,包含以下屬性:
NAME | TYPE | REQUIRED | DESCRIPTION |
---|---|---|---|
color | color | No | 定義波紋的顏色 |
borderless | boolean | No | 定義波紋效果是否包含邊框 |
radius | number | No | 定義波紋的半徑 |
<Pressable
android_ripple={{
color: '#ddd',
borderless: true,
}}
style={styles.button}
onPress={() => console.log('Pressable')}
>
<Text>Pressable</Text>
</Pressable>
Pressable 點擊動畫
需要搭配 React Native 內置的 Animated API,這邊簡單做一個點擊時放大、釋放時縮小的效果:
import React, { useRef } from "react"
import { StyleSheet, Text, Animated, Pressable, View } from "react-native"
export const TouchablesPage = () => {
const scaleValue = useRef(new Animated.Value(1)).current
const handlePressIn = () => {
Animated.spring(scaleValue, {
toValue: 1.2,
useNativeDriver: false,
}).start()
}
const handlePressOut = () => {
Animated.spring(scaleValue, {
toValue: 1,
useNativeDriver: false,
}).start()
}
return (
<Pressable
onPressIn={handlePressIn}
onPressOut={handlePressOut}
>
<Animated.View
style={[styles.button, {
transform: [{ scale: scaleValue }],
}]}
>
<Text>Pressable</Text>
</Animated.View>
</Pressable>
)
}
const styles = StyleSheet.create({
button: {
padding: 10,
backgroundColor: 'white',
borderRadius: 4,
marginVertical: 10
}
})
總結
根據需求選擇使用 Pressable
和 Touchables*
哪種即可,沒有特別需求的話就用 Pressable 方便後續修改點擊回饋或者在點擊時做一些處理,但是有些特殊情況也會需要用到 TouchableWithoutFeedback
,比如可以用在點擊背景關閉Modal(onBackdropPress),這個等說到 Modal 會再提。