React Native:Android 软键盘将 View 向上推

Posted

技术标签:

【中文标题】React Native:Android 软键盘将 View 向上推【英文标题】:React native: Android soft keyboard push the View up 【发布时间】:2021-11-28 10:53:48 【问题描述】:

我制作了一张自定义动画底页。它有两个捕捉点。它从屏幕顶部开始,如果向下滚动动画卡片,它将来到屏幕中间,再次向下滚动卡片,它将来到屏幕底部。如果用户用力向下滚动动画卡片,那么它将一直向下滚动。我把这个组件做成了一个可重用的组件。

我制作了可重复使用的搜索组件

我将这两个组件都导入到 ma​​in-app 组件中。在 Animated-Bottom-sheet 中,我放置了 Search-component

我已经为 keyboardAnimated.View

设置了条件

如果动画视图从屏幕中间移动到底部并搜索 onFocus(出现键盘),则 Keyboard 将关闭。

如果动画视图位于屏幕底部并搜索输入onfocus(出现键盘),那么它会将动画视图移动到屏幕中间。对于这个逻辑,我做了Keyboard.addListener('keyboardDidHide',()=>....)

我的这个逻辑在 IOS 中完美运行,但在 Android 中,它将所有元素推送到屏幕顶部。

有很多关于 KeyboardAvoidingView 的建议,但就我而言,它不起作用。

在 react-native 的 android 的 AndroidManifest.xml 文件中。我制作了android:windowSoftInputMode="adjustPan",它听Keyboard.addListener('keyboardDidHide'并将所有元素推到屏幕顶部,如果我制作android:windowSoftInputMode="adjustPan|adjustResize",那么它不会听Event-lister。结果,我的逻辑不起作用,它隐藏了 Android 键盘下的动画视图。根据RN-documentation,Event-lister 只听adjustResize or adjustPan

问题:

我真的不知道如何解决这个问题

Android 设备行为

DEMO

代码演示

expo-snacks

这是我的动画视图组件

import React,  useState  from "react";
import  StyleSheet, Dimensions, Platform, View, Keyboard  from "react-native";
import 
  PanGestureHandler,
  PanGestureHandlerGestureEvent,
 from "react-native-gesture-handler";

import Animated, 
  useAnimatedGestureHandler,
  useAnimatedStyle,
  useSharedValue,
  withTiming,
  withSpring,
  runOnJS,
  Easing,
 from "react-native-reanimated";
import styled from "styled-components/native";

interface Props 
  children: React.ReactNode;


const  height: SCREEN_HEIGHT  = Dimensions.get("screen");

const IPHONE_DEVICE_MIDDLE_SCREEN = Platform.OS === "ios" ? 4 : 4;
const IPHONE_DEVICE_BOTTOM_SCREEN = Platform.OS === "ios" ? 7.6 : 7.36;
const ANIMATED_DURATION = 300;

const LoadingContainer = styled.View`
  height: $SCREEN_HEIGHT - 300px;
  background-color: #fff;
  justify-content: center;
`;

const ScrollBottomSheet = ( children : Props) => 
  const contentTop = useSharedValue(SCREEN_HEIGHT);
  const [bottomSheetState, setBottomSheetState] = useState("top");
  // Event-listener
  Keyboard.addListener("keyboardDidShow", () => 
    if (bottomSheetState === "bottom") 
      contentTop.value = withSpring(
        SCREEN_HEIGHT * IPHONE_DEVICE_MIDDLE_SCREEN
      );
    
  );
  const animatedStyle = useAnimatedStyle(() => 
    "worklet";
    return 
      top: contentTop.value * 0.1,
      bottom: 0,
    ;
  );

  const gestureHandler = useAnimatedGestureHandler(
    
      onStart(_, context) 
        context.translateY = contentTop.value;
      ,
      onActive(event, context) 
        contentTop.value = context.translateY + event.translationY;
      ,
      onEnd(event, _) 
        if (event.y > 0 && event.y < 200) 
          // MIDDLE SCREEN LOGIC
          contentTop.value = withTiming(
            SCREEN_HEIGHT * IPHONE_DEVICE_MIDDLE_SCREEN,
            
              duration: ANIMATED_DURATION,
              easing: Easing.inOut(Easing.ease),
            
          );
          runOnJS(setBottomSheetState)("middle");
          runOnJS(Keyboard.dismiss)(); // dismiss Keyboard
         else if (event.y > 200) 
          // BOTTOM SCREEN LOGIC
          contentTop.value = withTiming(
            SCREEN_HEIGHT * IPHONE_DEVICE_BOTTOM_SCREEN,
            
              duration: ANIMATED_DURATION,
              easing: Easing.inOut(Easing.ease),
            
          ); 
         runOnJS(Keyboard.dismiss)();
          runOnJS(setBottomSheetState)("bottom");
         else if (event.translationY < 0) 
          contentTop.value = withTiming(SCREEN_HEIGHT, 
            duration: ANIMATED_DURATION,
            easing: Easing.inOut(Easing.ease),
          );
          runOnJS(setBottomSheetState)("top");
        
      ,
    ,
    [contentTop]
  );

  return (
    <PanGestureHandler onGestureEvent=gestureHandler>
      <Animated.View style=[styles.container, animatedStyle]>
        <View style=styles.grbber />
        children
      </Animated.View>
    </PanGestureHandler>
  );
;

export default ScrollBottomSheet;

const styles = StyleSheet.create(
  container: 
    position: "absolute",
    left: 0,
    right: 0,
    top: 0,
    backgroundColor: "#fff",
    shadowOffset: 
      height: -6,
      width: 0,
    ,
    shadowOpacity: 0.1,
    shadowRadius: 5,
    borderTopEndRadius: 15,
    borderTopLeftRadius: 15,
  ,
  grbber: 
    width: 80,
    height: 5,
    marginBottom: 4,
    alignSelf: "center",
    marginTop: 5,
    borderTopWidth: 5,
    borderTopColor: "#aaa",
  ,
);

搜索组件

import React,  useRef, useEffect  from "react";
import 
  Animated,
  KeyboardTypeOptions,
  TextInputProps,
  TextInput,
  Text,
  Keyboard,
 from "react-native";

import styled from "styled-components/native";

type Props = TextInputProps & 
  text: string,
  hint: string,
  onChangeText?: ((text: string) => void) | undefined,
  onClearText: () => void,
  animatedStyle?:  height: Animated.AnimatedInterpolation ,
  keyboardType?: KeyboardTypeOptions,

  autoFocus?: boolean,
;

const Container = styled(Animated.View)`
  flex-direction: row;
  background-color: grey;
  border-radius: 6px;
  align-items: center;
`;

const IconTouchableOpacity = styled.TouchableOpacity`
  padding: 16px;
`;

const SearchInput = styled.TextInput`
  flex: 1;
  padding-right: 16px;
  color: #fff;
  font-size: 16px;
  line-height: 20px;
  height: 44px;
`;

const SearchBar = (
  text,
  hint,
  onChangeText,
  onClearText,
  animatedStyle,
  keyboardType,
  maxLength,
  autoFocus,
: Props) => 
  const searchInput = useRef(null);

  const onSearchPress = () => 
    searchInput?.current?.focus();
  ;

  useEffect(() => 
    if (autoFocus) 
      onSearchPress();
    
  , [autoFocus]);

  return (
    <Container accessible=false style=animatedStyle>
      <SearchInput
        ref=searchInput
        onChangeText=onChangeText
        value=text
        placeholder=hint
        maxLength=maxLength
        underlineColorAndroid="transparent"
        placeholderTextColor="grey"
        keyboardType=keyboardType
        autoFocus=autoFocus
        onKeyPress=() => hideKeyBoard
      />
    </Container>
  );
;

export default SearchBar;

**应用组件

import React,  useState, useEffect  from "react";
import  StyleSheet, Button  from "react-native";
import  TouchableHighlight  from "react-native-gesture-handler";
import MapView from "react-native-maps";
import styled from "styled-components";
import ScrollBottomSheet from "./components/ActionSheet";
import SearchBar from "./components/SearchBar";

const initialRegion = 
  latitudeDelta: 15,
  longitudeDelta: 15,
  latitude: 60.1098678,
  longitude: 24.7385084,
;

export default function App() 
  return (
    <>
      <MapView style=styles.mapStyle initialRegion=initialRegion />
      <ScrollBottomSheet>
        <SearchContainer>
          <SearchBar hint="search" />
        </SearchContainer>
      </ScrollBottomSheet>
    </>
  );


const styles = StyleSheet.create(
  mapStyle: 
    height: "100%",
  ,
);

const SearchContainer = styled.View`
  padding: 10px;
`;

【问题讨论】:

您如何尝试使用KeyboardAvoidingView?你定义了什么行为?我遇到了类似的问题,它帮助我将行为属性定义为高度 - &lt;KeyboardAvoidingView behavior='height' enable=true&gt;/*...*/&lt;/KeyboardAvoidingView&gt; KeyboardTypeOptions from 'react-native' ? 【参考方案1】:

您是否尝试过使用 Animated.ScrollView 代替 Animated.View?

【讨论】:

不,我没有使用Animated.ScrollView。对我有什么帮助

以上是关于React Native:Android 软键盘将 View 向上推的主要内容,如果未能解决你的问题,请参考以下文章

React Native 键盘问题

React-native:打开表情符号键盘

如何在带有 React Native Text 的 Android 中实现软连字符

如何在 react-native 应用程序中更改 android 的键盘主题?

键盘打开时TextInput不可见Expo React Native

react-native解决键盘自适应