首页 > 解决方案 > React Native:Android 软键盘将 View 向上推

问题描述

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

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

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

我已经为键盘Animated.View创造了条件

我的这个逻辑在 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 only listen adjustResize or adjustPan

问题:

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

Android 设备行为

演示

代码演示

博览会小吃

这是我的动画视图组件

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 - 300}px;
  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;
`;

标签: androidreact-nativekeyboardreact-native-textinput

解决方案


您是否尝试过使用 Animated.ScrollView 而不是 Animated.View?


推荐阅读