Site icon May's Notes

React Native 奇幻之旅(3)-內建組件 Touchables*

React Native logo

這是我在2023第十五屆 iThome 鐵人賽發表的系列文章。

https://ithelp.ithome.com.tw/users/20136637/ironman/6408

React Native 內建的點擊類組件(統稱為Touchables*)有以下幾種:

雖然點擊類的組件有這麼多個,但官方建議在未來改為使用 Pressable,因為 Pressable 涵蓋了所有 Touchables* 的功能。

Touchables* 和 Pressable 的差異

  1. TouchableWithoutFeedbackPressable 預設點擊是沒有回饋效果的。
TouchableOpacityTouchableHighlightTouchableNativeFeedback
點擊時會變透明點擊時會變色(underlayColor)點擊時會有波紋效果(僅限 Android)
TouchableWithoutFeedbackPressable
預設點擊沒有回饋效果預設點擊沒有回饋效果
  1. Pressable 可以自定義點擊交互效果(包括單擊、長按…等),Touchables* 是各司其職
  2. 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,包含以下屬性:

NAMETYPEREQUIREDDESCRIPTION
colorcolorNo定義波紋的顏色
borderlessbooleanNo定義波紋效果是否包含邊框
radiusnumberNo定義波紋的半徑
<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
  }
})

總結

根據需求選擇使用 PressableTouchables* 哪種即可,沒有特別需求的話就用 Pressable 方便後續修改點擊回饋或者在點擊時做一些處理,但是有些特殊情況也會需要用到 TouchableWithoutFeedback ,比如可以用在點擊背景關閉Modal(onBackdropPress),這個等說到 Modal 會再提。

參考資料

Exit mobile version