React Native事件处理 #
概述 #
React Native 提供了多种处理触摸事件和手势的方式。本章节介绍如何处理用户交互,包括基本触摸事件、手势识别和交互反馈。
基本触摸组件 #
TouchableOpacity #
最常用的可触摸组件,点击时透明度降低:
tsx
import React from 'react';
import {TouchableOpacity, Text, StyleSheet, Alert} from 'react-native';
const TouchableOpacityExample = () => {
const handlePress = () => {
Alert.alert('提示', '按钮被点击了');
};
const handleLongPress = () => {
Alert.alert('提示', '长按触发');
};
return (
<TouchableOpacity
style={styles.button}
onPress={handlePress}
onLongPress={handleLongPress}
activeOpacity={0.7}>
<Text style={styles.text}>Click Me</Text>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
button: {
backgroundColor: '#007AFF',
paddingHorizontal: 24,
paddingVertical: 12,
borderRadius: 8,
alignItems: 'center',
},
text: {
color: '#fff',
fontSize: 16,
fontWeight: '600',
},
});
export default TouchableOpacityExample;
TouchableHighlight #
点击时背景变暗:
tsx
import React from 'react';
import {TouchableHighlight, Text, StyleSheet, View} from 'react-native';
const TouchableHighlightExample = () => {
return (
<TouchableHighlight
style={styles.button}
underlayColor="#0056b3"
onPress={() => console.log('Pressed')}>
<Text style={styles.text}>TouchableHighlight</Text>
</TouchableHighlight>
);
};
const styles = StyleSheet.create({
button: {
backgroundColor: '#007AFF',
paddingHorizontal: 24,
paddingVertical: 12,
borderRadius: 8,
},
text: {
color: '#fff',
fontSize: 16,
fontWeight: '600',
textAlign: 'center',
},
});
TouchableNativeFeedback(Android) #
Android 原生水波纹效果:
tsx
import React from 'react';
import {
TouchableNativeFeedback,
Text,
StyleSheet,
View,
Platform,
} from 'react-native';
const NativeButton = () => {
if (Platform.OS === 'android') {
return (
<TouchableNativeFeedback
onPress={() => console.log('Pressed')}
background={TouchableNativeFeedback.Ripple('#007AFF', false)}
useForeground>
<View style={styles.button}>
<Text style={styles.text}>Native Button</Text>
</View>
</TouchableNativeFeedback>
);
}
return (
<TouchableOpacity style={styles.button} onPress={() => console.log('Pressed')}>
<Text style={styles.text}>iOS Button</Text>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
button: {
backgroundColor: '#007AFF',
paddingHorizontal: 24,
paddingVertical: 12,
borderRadius: 8,
},
text: {
color: '#fff',
fontSize: 16,
fontWeight: '600',
},
});
TouchableWithoutFeedback #
无视觉反馈,用于隐藏键盘等场景:
tsx
import React from 'react';
import {
TouchableWithoutFeedback,
Keyboard,
View,
Text,
TextInput,
StyleSheet,
} from 'react-native';
const HideKeyboardExample = () => {
return (
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={styles.container}>
<TextInput style={styles.input} placeholder="Tap outside to dismiss" />
</View>
</TouchableWithoutFeedback>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
padding: 20,
},
input: {
height: 48,
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 8,
paddingHorizontal: 16,
},
});
Pressable 组件 #
Pressable 是 React Native 0.63 引入的新组件,提供更强大的交互能力:
tsx
import React from 'react';
import {Pressable, Text, StyleSheet} from 'react-native';
const PressableExample = () => {
return (
<Pressable
onPress={() => console.log('Pressed')}
onLongPress={() => console.log('Long pressed')}
onPressIn={() => console.log('Press in')}
onPressOut={() => console.log('Press out')}
delayLongPress={500}
hitSlop={10}
style={({pressed}) => [
styles.button,
pressed && styles.pressed,
]}>
{({pressed}) => (
<Text style={styles.text}>
{pressed ? 'Pressed!' : 'Press Me'}
</Text>
)}
</Pressable>
);
};
const styles = StyleSheet.create({
button: {
backgroundColor: '#007AFF',
paddingHorizontal: 24,
paddingVertical: 12,
borderRadius: 8,
},
pressed: {
backgroundColor: '#0056b3',
},
text: {
color: '#fff',
fontSize: 16,
fontWeight: '600',
},
});
Pressable 属性 #
| 属性 | 类型 | 说明 |
|---|---|---|
| onPress | function | 点击回调 |
| onLongPress | function | 长按回调 |
| onPressIn | function | 按下回调 |
| onPressOut | function | 抬起回调 |
| disabled | boolean | 是否禁用 |
| delayLongPress | number | 长按延迟(毫秒) |
| hitSlop | number/object | 扩大触摸区域 |
| pressRetentionOffset | number/object | 触摸保留区域 |
手势响应系统 #
基本手势事件 #
tsx
import React, {useState} from 'react';
import {View, StyleSheet, GestureResponderEvent} from 'react-native';
const GestureExample = () => {
const [position, setPosition] = useState({x: 0, y: 0});
const handleTouchStart = (e: GestureResponderEvent) => {
const {locationX, locationY} = e.nativeEvent;
setPosition({x: locationX, y: locationY});
};
const handleTouchMove = (e: GestureResponderEvent) => {
const {locationX, locationY} = e.nativeEvent;
setPosition({x: locationX, y: locationY});
};
return (
<View
style={styles.container}
onTouchStart={handleTouchStart}
onTouchMove={handleTouchMove}
onTouchEnd={() => console.log('Touch ended')}>
<View style={[styles.indicator, {left: position.x - 25, top: position.y - 25}]} />
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
},
indicator: {
position: 'absolute',
width: 50,
height: 50,
borderRadius: 25,
backgroundColor: '#007AFF',
},
});
PanResponder #
PanResponder 提供了更高级的手势处理能力:
tsx
import React, {useRef, useState} from 'react';
import {View, PanResponder, StyleSheet, Animated} from 'react-native';
const DraggableBox = () => {
const pan = useRef(new Animated.ValueXY()).current;
const panResponder = useRef(
PanResponder.create({
onMoveShouldSetPanResponder: () => true,
onPanResponderGrant: () => {
pan.setOffset({
x: (pan.x as any)._value,
y: (pan.y as any)._value,
});
pan.setValue({x: 0, y: 0});
},
onPanResponderMove: Animated.event([null, {dx: pan.x, dy: pan.y}], {
useNativeDriver: false,
}),
onPanResponderRelease: () => {
pan.flattenOffset();
},
}),
).current;
return (
<View style={styles.container}>
<Animated.View
style={[
styles.box,
{transform: [{translateX: pan.x}, {translateY: pan.y}]},
]}
{...panResponder.panHandlers}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
box: {
width: 100,
height: 100,
backgroundColor: '#007AFF',
borderRadius: 8,
},
});
export default DraggableBox;
滚动事件 #
ScrollView 滚动事件 #
tsx
import React, {useState, useRef} from 'react';
import {ScrollView, Text, View, StyleSheet} from 'react-native';
const ScrollEventExample = () => {
const [scrollY, setScrollY] = useState(0);
const scrollViewRef = useRef<ScrollView>(null);
const handleScroll = (event: any) => {
const {y} = event.nativeEvent.contentOffset;
setScrollY(y);
};
const handleMomentumScrollEnd = (event: any) => {
console.log('Scroll ended at:', event.nativeEvent.contentOffset.y);
};
return (
<View style={styles.container}>
<View style={styles.header}>
<Text>Scroll Position: {Math.round(scrollY)}</Text>
</View>
<ScrollView
ref={scrollViewRef}
style={styles.scrollView}
onScroll={handleScroll}
onMomentumScrollEnd={handleMomentumScrollEnd}
scrollEventThrottle={16}>
{Array.from({length: 50}).map((_, index) => (
<View key={index} style={styles.item}>
<Text>Item {index + 1}</Text>
</View>
))}
</ScrollView>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
header: {
padding: 16,
backgroundColor: '#007AFF',
},
scrollView: {
flex: 1,
},
item: {
padding: 20,
borderBottomWidth: 1,
borderBottomColor: '#eee',
},
});
键盘事件 #
键盘显示/隐藏监听 #
tsx
import React, {useState, useEffect} from 'react';
import {Keyboard, View, Text, TextInput, StyleSheet} from 'react-native';
const KeyboardExample = () => {
const [keyboardHeight, setKeyboardHeight] = useState(0);
useEffect(() => {
const keyboardWillShow = Keyboard.addListener('keyboardWillShow', e => {
setKeyboardHeight(e.endCoordinates.height);
});
const keyboardWillHide = Keyboard.addListener('keyboardWillHide', () => {
setKeyboardHeight(0);
});
return () => {
keyboardWillShow.remove();
keyboardWillHide.remove();
};
}, []);
return (
<View style={[styles.container, {paddingBottom: keyboardHeight}]}>
<Text>Keyboard Height: {keyboardHeight}</Text>
<TextInput style={styles.input} placeholder="Type something" />
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16,
},
input: {
height: 48,
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 8,
paddingHorizontal: 16,
marginTop: 16,
},
});
KeyboardAvoidingView #
自动避开键盘:
tsx
import React from 'react';
import {
KeyboardAvoidingView,
TextInput,
View,
Text,
StyleSheet,
Platform,
} from 'react-native';
const KeyboardAvoidingExample = () => {
return (
<KeyboardAvoidingView
style={styles.container}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
keyboardVerticalOffset={Platform.OS === 'ios' ? 64 : 0}>
<View style={styles.content}>
<Text>Form Content</Text>
<TextInput style={styles.input} placeholder="Input 1" />
<TextInput style={styles.input} placeholder="Input 2" />
<TextInput style={styles.input} placeholder="Input 3" />
</View>
</KeyboardAvoidingView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
content: {
flex: 1,
padding: 16,
},
input: {
height: 48,
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 8,
paddingHorizontal: 16,
marginBottom: 16,
},
});
扩大触摸区域 #
hitSlop #
tsx
import React from 'react';
import {TouchableOpacity, View, StyleSheet} from 'react-native';
const HitSlopExample = () => {
return (
<View style={styles.container}>
<TouchableOpacity
style={styles.smallButton}
hitSlop={{top: 20, bottom: 20, left: 20, right: 20}}
onPress={() => console.log('Pressed')}>
<View style={styles.innerButton} />
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
smallButton: {
width: 44,
height: 44,
justifyContent: 'center',
alignItems: 'center',
},
innerButton: {
width: 20,
height: 20,
backgroundColor: '#007AFF',
borderRadius: 10,
},
});
禁用交互 #
tsx
import React, {useState} from 'react';
import {TouchableOpacity, Text, StyleSheet} from 'react-native';
const DisabledButton = () => {
const [isLoading, setIsLoading] = useState(false);
const handlePress = async () => {
setIsLoading(true);
await someAsyncOperation();
setIsLoading(false);
};
return (
<TouchableOpacity
style={[styles.button, isLoading && styles.disabled]}
onPress={handlePress}
disabled={isLoading}>
<Text style={styles.text}>
{isLoading ? 'Loading...' : 'Submit'}
</Text>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
button: {
backgroundColor: '#007AFF',
paddingHorizontal: 24,
paddingVertical: 12,
borderRadius: 8,
},
disabled: {
backgroundColor: '#ccc',
},
text: {
color: '#fff',
fontSize: 16,
},
});
实用示例 #
可滑动删除 #
tsx
import React, {useRef} from 'react';
import {
View,
Text,
TouchableOpacity,
Animated,
PanResponder,
StyleSheet,
} from 'react-native';
interface SwipeToDeleteProps {
text: string;
onDelete: () => void;
}
const SwipeToDelete: React.FC<SwipeToDeleteProps> = ({text, onDelete}) => {
const translateX = useRef(new Animated.Value(0)).current;
const panResponder = useRef(
PanResponder.create({
onMoveShouldSetPanResponder: (_, gestureState) => {
return Math.abs(gestureState.dx) > Math.abs(gestureState.dy);
},
onPanResponderMove: (_, gestureState) => {
if (gestureState.dx < 0) {
translateX.setValue(gestureState.dx);
}
},
onPanResponderRelease: (_, gestureState) => {
if (gestureState.dx < -100) {
Animated.timing(translateX, {
toValue: -200,
duration: 200,
useNativeDriver: true,
}).start(() => {
onDelete();
});
} else {
Animated.spring(translateX, {
toValue: 0,
useNativeDriver: true,
}).start();
}
},
}),
).current;
return (
<View style={styles.container}>
<View style={styles.deleteButton}>
<Text style={styles.deleteText}>Delete</Text>
</View>
<Animated.View
style={[styles.content, {transform: [{translateX}]}]}
{...panResponder.panHandlers}>
<Text>{text}</Text>
</Animated.View>
</View>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: '#FF3B30',
marginVertical: 4,
},
deleteButton: {
position: 'absolute',
right: 0,
top: 0,
bottom: 0,
width: 100,
justifyContent: 'center',
alignItems: 'center',
},
deleteText: {
color: '#fff',
fontWeight: 'bold',
},
content: {
backgroundColor: '#fff',
padding: 16,
},
});
export default SwipeToDelete;
总结 #
React Native 提供了丰富的交互处理能力:
- TouchableOpacity:透明度反馈
- TouchableHighlight:背景高亮
- TouchableNativeFeedback:Android 水波纹
- Pressable:新式可按压组件
- PanResponder:高级手势处理
- Keyboard:键盘事件管理
掌握这些交互处理方式,可以创建流畅的用户体验。
继续学习 React Navigation基础,了解导航系统的使用。
最后更新:2026-03-28