React Native:react-native-phone-number-input无onFocus实现标签动画
问题:react-native-phone-number-input 组件无 onFocus 属性,无法触发标签动画
我用 react-native-phone-number-input 开发手机号输入功能,需要通过聚焦事件触发标签动画,但这个组件本身没有 onFocus 属性,目前无法实现需求。
我的组件代码:
const TestComponent = () => { const [value, setValue] = useState('') const [formattedValue, setFormattedValue] = useState('') const [valid, setValid] = useState(false) const [showMessage, setShowMessage] = useState(false) const phoneInput = useRef<PhoneInput>(null) const [isFocused, setIsFocused] = useState(true) const focusAnimation = useRef(new Animated.Value(0)).current const scale = focusAnimation.interpolate({ inputRange: [0, 1], outputRange: [1, 0.75], }) const translateY = focusAnimation.interpolate({ inputRange: [0, 1], outputRange: [20, 0], }) const translateX = focusAnimation.interpolate({ inputRange: [0, 1], outputRange: [8, -150], }) let borderColor = isFocused ? colors.primary : colors.gray600 let labelColor = isFocused || value ? colors.primary : colors.gray400 return ( <View> {showMessage && ( <View> <Text>Value : {value}</Text> <Text>Formatted Value : {formattedValue}</Text> <Text>Valid : {valid ? 'true' : 'false'}</Text> </View> )} <> <PhoneInput ref={phoneInput} defaultValue={value} defaultCode='TR' layout='first' onChangeText={text => { setValue(text) }} onChangeFormattedText={text => { setFormattedValue(text) }} placeholder='Telefon Numarası' containerStyle={styles.containerStyle} textContainerStyle={styles.textContainerStyle} textInputStyle={styles.textInputStyle} codeTextStyle={styles.codeTextStyle} flagButtonStyle={styles.flagButtonStyle} countryPickerButtonStyle={styles.countryPickerButtonStyle} /> </> </View> ) }
当前效果:
期望效果:
解决方案
可以通过以下两种方式实现聚焦事件监听,进而触发标签动画:
方法1:利用组件内部的 TextInput 引用
react-native-phone-number-input 内部包含原生 TextInput,可以通过组件的 ref 获取内部输入框引用,添加聚焦/失焦监听:
修改后的完整代码:
const TestComponent = () => { const [value, setValue] = useState('') const [formattedValue, setFormattedValue] = useState('') const [valid, setValid] = useState(false) const [showMessage, setShowMessage] = useState(false) const phoneInput = useRef<PhoneInput>(null) const [isFocused, setIsFocused] = useState(false) const focusAnimation = useRef(new Animated.Value(0)).current const scale = focusAnimation.interpolate({ inputRange: [0, 1], outputRange: [1, 0.75], }) const translateY = focusAnimation.interpolate({ inputRange: [0, 1], outputRange: [20, 0], }) const translateX = focusAnimation.interpolate({ inputRange: [0, 1], outputRange: [8, -150], }) let borderColor = isFocused ? colors.primary : colors.gray600 let labelColor = isFocused || value ? colors.primary : colors.gray400 useEffect(() => { if (phoneInput.current) { const textInputRef = phoneInput.current.textInputRef; if (textInputRef) { // 监听聚焦事件 const focusListener = textInputRef.addListener('focus', () => { setIsFocused(true); Animated.timing(focusAnimation, { toValue: 1, duration: 200, useNativeDriver: false, }).start(); }); // 监听失焦事件 const blurListener = textInputRef.addListener('blur', () => { if (!value) { setIsFocused(false); Animated.timing(focusAnimation, { toValue: 0, duration: 200, useNativeDriver: false, }).start(); } }); // 清理监听 return () => { focusListener.remove(); blurListener.remove(); }; } } }, [value, focusAnimation]); return ( <View style={{ position: 'relative' }}> {/* 自定义动画标签 */} <Animated.Text style={{ position: 'absolute', left: 8, top: 0, transform: [{ scale }, { translateY }, { translateX }], color: labelColor, fontSize: 12, }} > Telefon Numarası </Animated.Text> {showMessage && ( <View> <Text>Value : {value}</Text> <Text>Formatted Value : {formattedValue}</Text> <Text>Valid : {valid ? 'true' : 'false'}</Text> </View> )} <PhoneInput ref={phoneInput} defaultValue={value} defaultCode='TR' layout='first' onChangeText={text => { setValue(text); // 输入内容时触发动画 if (!isFocused) { setIsFocused(true); Animated.timing(focusAnimation, { toValue: 1, duration: 200, useNativeDriver: false, }).start(); } }} onChangeFormattedText={text => { setFormattedValue(text) }} placeholder='' containerStyle={[styles.containerStyle, { borderColor, paddingTop: 16 }]} textContainerStyle={styles.textContainerStyle} textInputStyle={styles.textInputStyle} codeTextStyle={styles.codeTextStyle} flagButtonStyle={styles.flagButtonStyle} countryPickerButtonStyle={styles.countryPickerButtonStyle} /> </View> ) }
方法2:包装组件监听触摸事件
如果无法获取内部引用,可给 PhoneInput 外层套 TouchableWithoutFeedback,通过触摸事件模拟聚焦触发(注意:点击国家选择器也会触发此事件):
<TouchableWithoutFeedback onPress={() => { phoneInput.current?.focus(); setIsFocused(true); Animated.timing(focusAnimation, { toValue: 1, duration: 200, useNativeDriver: false, }).start(); }}> <PhoneInput // ...其他属性 /> </TouchableWithoutFeedback>
关键修改说明:
- 移除原组件占位符,改用自定义动画标签
- 通过内部
textInputRef监听focus/blur事件,精准触发动画 - 输入内容时同步触发动画,确保输入后标签保持上浮状态
- 调整输入框容器内边距,避免标签与输入内容重叠
内容的提问来源于stack exchange,提问作者the Great A
相关产品推荐
相关产品推荐

