更多分享内容可访问我的个人博客
本文介绍如何在 react native 中自定义一个可以通过点击展开收起的容器,这里将其称为可扩展卡片。
实现方式
说一下实现思路。
首先,这个卡片应当有两个状态,即展开和收起状态,那么一定有一个描述状态的变量,点一下按钮可以切换变量的值。
然后,在收起状态下,子组件应当不显示,展开状态下子组件显示。要达成这样的效果有很多种方法。比如{isHidden ? null : children}
,或者改变组件高度。这样做的效果就是卡片展开与收起时没有动画效果。
那么如何实现动画效果呢?这也有很多种方法。先介绍一种简单的,逐步改变高度。只需要在useEffect
中设置一个定时器,定时增加或者减少子组件的高度,然后令子组件的overflow="hidden"
就可以实现简易的动画效果。具体步骤就不讲了。另一种是利用Animated
组件。
以下为源码,关键步骤看注释。
import { Text, View, TouchableOpacity, Animated } from "react-native";
import { useState, useRef } from "react";
const Header = () => {
return <Text>toggle</Text>;
};
const ExpandableCard = ({ duration = 500, header = <Header />, ...props }) => {
const animation = useRef(new Animated.Value(0)).current;
const [isHidden, setHidden] = useState(true);
const [childHeight, setChildHeight] = useState(0);
const toggleHiddenState = () => {
setHidden(!isHidden);
Animated.timing(animation, {
toValue: isHidden ? 1 : 0,
duration: duration,
useNativeDriver: false,
}).start();
};
return (
<View>
<TouchableOpacity
onPress={() => {
toggleHiddenState();
}}
activeOpacity={1}
>
{
// 这里是卡片的header,也就是点击的地方
// 默认就是前面的Header
header
}
</TouchableOpacity>
{
// 动画效果
}
<Animated.View
style={{
height: animation.interpolate({
// 输入 输出
// 0 1
// 1 子组件高度
// 这里输入的0和1是上面Animated.timing中设置的
// 一开始的高度如果设置为0,则子组件的高度一直为0
// 设置为1,子组件的高度会慢慢增加,最终达到真实高度
// 如果想知道具体过程,可以在下面的onLayout中的函数中打印height
inputRange: [0, 1],
outputRange: [1, childHeight],
}),
overflow: "hidden",
}}
>
<View
onLayout={(event) => {
const { height } = event.nativeEvent.layout;
setChildHeight(height);
}}
>
{
// 这里用一个高度为1的空组件把初始高度填上,防止子组件顶部露出来
// 可能你会想到通过将初始高度设置为0.001等极小值来解决该问题,但实际操作中这样的方案是不稳定的
}
<View style={{ height: 1, width: "100%" }} />
{
// 这里是传入的子组件
}
{props.children}
</View>
</Animated.View>
</View>
);
};
export default ExpandableCard;
以上只是一个简单实现,原理已经完全体现了,想要更多的功能(比如向右侧展开)可以自己修改。
使用
用法很简单,可以传入自定义的头部,也可以设置动画时间。
<ExpandableCard header={CustomHeader} duration={1000}>
childComponent
</ExpandableCard>