通过工具栏按钮收起键盘后,多行文本输入框再次获得焦点

移动开发 2026-07-09

在我的React Native(Expo)应用中,当使用自定义工具栏按钮来隐藏键盘时,遇到了一个奇怪的键盘焦点问题。

如果通过硬件 Esc 键关闭键盘,TextInput 会完全失去焦点。之后,我可以滚动内容,松开手指时,键盘仍然保持开启状态(如预期)。

如果我点击自定义工具栏按钮,它会调用 Keyboard.dismiss(),键盘会隐藏,但 TextInput 仍然保持焦点状态。由于焦点仍然处于活动状态,滚动内容并松手后,键盘会立刻弹回。

下面是按上下文块分隔的函数组件代码,以符合格式规则:

首先,核心组件逻辑和钩子配置:

import { MaterialIcons } from "@expo/vector-icons";
import { useHeaderHeight } from "expo-router/react-navigation";
import { useEffect, useState } from "react";
import {
  Keyboard,
  KeyboardAvoidingView,
  Platform,
  Pressable,
  StyleSheet,
  TextInput,
  View,
} from "react-native";

export default function WriteScreen() {
  const headerHeight = useHeaderHeight();
  const [isKeyboardVisible, setIsKeyboardVisible] = useState(false);

接下来,跟踪软键盘可见性状态的事件监听:

 useEffect(() => {
    const showEvent =
      Platform.OS === "ios" ? "keyboardWillShow" : "keyboardDidShow";
    const hideEvent =
      Platform.OS === "ios" ? "keyboardWillHide" : "keyboardDidHide";

    const show = Keyboard.addListener(showEvent, () =>
      setIsKeyboardVisible(true),
    );
    const hide = Keyboard.addListener(hideEvent, () =>
      setIsKeyboardVisible(false),
    );
    return () => {
      show.remove();
      hide.remove();
    };
  }, []);

  const dismissKeyboard = () => {
    Keyboard.dismiss();
  };

以下是包含 KeyboardAvoidingView 包装器和多行输入的UI布局结构:

  return (
    <KeyboardAvoidingView
      style={styles.container}
      behavior={Platform.OS === "ios" ? "padding" : "height"}
      keyboardVerticalOffset={headerHeight}
    >
      <TextInput
        style={styles.title}
        placeholder="제목"
        placeholderTextColor="#999"
      />

      <TextInput
        style={styles.content}
        placeholder="내용"
        placeholderTextColor="#999"
        multiline
        textAlignVertical="top"
      />

最后,包含条件关闭按钮的工具栏布局:

      <View style={styles.toolbar}>
        {isKeyboardVisible ? (
          <Pressable
            style={styles.keyboardDismissButton}
            onPress={dismissKeyboard}
            hitSlop={8}
            accessibilityRole="button"
          >
            <MaterialIcons name="keyboard-hide" size={22} />
          </Pressable>
        ) : null}
      </View>
    </KeyboardAvoidingView>
  );
}

下面是完整的样式表:

const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingHorizontal: 16,
    backgroundColor: "#fff",
  },
  flex: {
    flex: 1,
  },
  scrollContent: {
    flexGrow: 1,
  },
  title: {
    fontSize: 20,
    fontWeight: "bold",
    paddingVertical: 12,
  },
  content: {
    flexGrow: 1,
    fontSize: 16,
    textAlignVertical: "top",
  },
  toolbar: {
    height: 48,
    flexDirection: "row",
    justifyContent: "flex-end",
    alignItems: "center",
    borderTopWidth: StyleSheet.hairlineWidth,
    borderTopColor: "#ddd",
  },
  toolbarButton: {
    padding: 8,
  },
  keyboardDismissButton: {
    padding: 4,
    borderRadius: 10,
    alignItems: "center",
    justifyContent: "center",
  },
});

如何让自定义工具栏按钮彻底终止/让 TextInput 的焦点生命周期像 Esc 键一样被移除,以便之后滚动不会再次意外触发键盘?

解决方案

问题在于 Keyboard.dismiss() 会在视觉上隐藏键盘,但不会让当前聚焦的 TextInput 失去焦点,因此它仍然保持焦点,在滚动结束时会重新触发键盘。

修复方式是显式地让当前聚焦的输入失去焦点:

const dismissKeyboard = () => {
  const currentInput = TextInput.State.currentlyFocusedInput();
  if (currentInput) {
    currentInput.blur();
  }
  Keyboard.dismiss();
};

TextInput.State.currentlyFocusedInput() 返回当前活动输入的原生引用,对它调用 .blur(),就能实现与原生Esc键相同的效果。Keyboard.dismiss() 作为Android端边缘情况的安全网保留。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章