下拉级,但每个索引选择中的索引本地选择

发布于 2025-02-12 22:13:21 字数 5956 浏览 0 评论 0原文

因此,我想使每个flatlist下拉列表打开每个索引,因此当您选择1节时,它只会打开该部分,以便另一个没有打开,但是可悲的是,我仍然找不到解决

方案

app.tsx app.tsx :

import React, { useRef, useState } from 'react';
import { View, Pressable, StyleSheet, Text, FlatList } from 'react-native';
import { AnimateHeight } from './animate-height';
import { Ionicons } from '@expo/vector-icons';
import { MotiView } from 'moti';
import Constants from 'expo-constants';

export default function App() {
  const ref = useRef()
  const dummyData = [
    {
      id: 1,
      question: "are u a banana?",
      desc: "no i'm not",
    },
     {
      id: 2,
      question: "are u a apple?",
      desc: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.",
    },
  ]
  const [currentIndex, setCurrentIndex] = useState(0);
  const [show, toggle] = React.useReducer((open) => !open, false);

  const renderItem = ({item, index}:any) => {
    return(
    <View style={styles.screen}>
      <View style={itemStyles.container}>
      <Pressable onPress={toggle} style={itemStyles.question}>
        <Text selectable={false} style={itemStyles.questionText}>
          {item?.question}
        </Text>
        <MotiView
          animate={{
            rotateZ: show ? '-89deg' : '90deg',
          }}>
          <Ionicons name="chevron-forward" color="white" size={17} />
        </MotiView>
      </Pressable>
      <AnimateHeight enterFrom="bottom" hide={!show}>
          <View style={itemStyles.answer}>
            <Text style={itemStyles.answerText}>
              {item?.desc}
            </Text>
          </View>
        </AnimateHeight>
    </View>
    </View>
    )
  }

  return (
    <View>
      <FlatList 
        data={dummyData}
        renderItem={renderItem}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  screen: {
    flex: 1,
    backgroundColor: '#161618',
    paddingTop: Constants.statusBarHeight,
  },
});

const itemStyles = StyleSheet.create({
  question: {
    padding: 16,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
  },
  answer: {
    padding: 16,
    marginTop: -16
  },
  answerText: {
    color: '#A09FA5',
    lineHeight: 20
  },
  container: {
    borderBottomWidth: 1,
    borderBottomColor: '#232326',
  },
  questionText: {
    color: '#EDEDEE',
    fontWeight: 'bold',
  },
});

Animate-Height.tsx:

import '@motify/core';
import '@motify/components';

import React from 'react';
import { MotiView, TransitionConfig, useDynamicAnimation } from 'moti';
import { StyleSheet, Platform } from 'react-native';
import { useDerivedValue, useSharedValue } from 'react-native-reanimated';
import { View } from 'react-native';

type Props = {
  children?: React.ReactNode;
  /**
   * If `true`, the height will automatically animate to 0. Default: `false`.
   */
  hide?: boolean; 
  onHeightDidAnimate?: (height: number) => void;
  /**
   * Defines where the expanded view will be anchored.
   *
   * Default: `top`
   *
   * This prop is untested, use with caution
   */
  enterFrom?: 'bottom' | 'top';
  initialHeight?: number;
} & React.ComponentProps<typeof MotiView>;

function AnimateHeight({
  children,
  hide = false,
  style,
  delay = Platform.select({ web: 250, default: 0 }),
  transition = { type: 'timing', delay }, 
  enterFrom = 'top',
  onHeightDidAnimate,
  initialHeight = 0,
  ...motiViewProps
}: Props) {
  const measuredHeight = useSharedValue(initialHeight);
  const state = useDynamicAnimation(() => {
    return {
      height: initialHeight,
      opacity: !initialHeight || hide ? 0 : 1
    }
  })
  if ('state' in motiViewProps) {
    console.warn('[AnimateHeight] state prop not supported')
  }

  const animation = useDerivedValue(() => {
    let height = Math.ceil(measuredHeight.value);
    if (hide) {
      height = 0;
    } 

    const notVisible = !height || hide;
    
    state.animateTo({
      height,
      opacity: !height || hide ? 0 : 1
    });
  }, [hide, measuredHeight]);


  return (
    <MotiView
      {...motiViewProps}
      state={state}
      transition={transition}
      onDidAnimate={
        onHeightDidAnimate &&
        ((key, finished, _, { attemptedValue }) =>
          key == 'height' && onHeightDidAnimate(attemptedValue as number))
      }
      style={[styles.hidden, style]}>
      <View
        style={[
          StyleSheet.absoluteFill,
          styles.autoBottom

          // THIS BREAKS IDK WHY, so ignore that prop
          // enterFrom === 'top' ? styles.autoBottom : styles.autoTop, 
        ]}
        onLayout={({ nativeEvent }) => {
          measuredHeight.value = nativeEvent.layout.height;
        }}>
        {children}
      </View>
    </MotiView>
  );
}

const styles = StyleSheet.create({
  autoBottom: {
    bottom: 'auto',
  },
  autoTop: {
    top: 'auto',
  },
  hidden: {
    overflow: 'hidden',
  },
});

export { AnimateHeight };

如果您想实时尝试,这是Expo小吃: https://snack.expo.expo.expo.dev/@mikess/moti-animimimated-heigt-heigt <-heigt <-animimated-heigt <-heigt <-heigt <-heigt < /a>

如果有人可以提供帮助或找到解决方案,这将是一项帮助,非常感谢您

so i want to make that each of the flatlist dropdown open per index, so when you choose 1 section it will only open that section so the other doesn't get open, but sadly i still can not find the solution

The Code

App.tsx:

import React, { useRef, useState } from 'react';
import { View, Pressable, StyleSheet, Text, FlatList } from 'react-native';
import { AnimateHeight } from './animate-height';
import { Ionicons } from '@expo/vector-icons';
import { MotiView } from 'moti';
import Constants from 'expo-constants';

export default function App() {
  const ref = useRef()
  const dummyData = [
    {
      id: 1,
      question: "are u a banana?",
      desc: "no i'm not",
    },
     {
      id: 2,
      question: "are u a apple?",
      desc: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.",
    },
  ]
  const [currentIndex, setCurrentIndex] = useState(0);
  const [show, toggle] = React.useReducer((open) => !open, false);

  const renderItem = ({item, index}:any) => {
    return(
    <View style={styles.screen}>
      <View style={itemStyles.container}>
      <Pressable onPress={toggle} style={itemStyles.question}>
        <Text selectable={false} style={itemStyles.questionText}>
          {item?.question}
        </Text>
        <MotiView
          animate={{
            rotateZ: show ? '-89deg' : '90deg',
          }}>
          <Ionicons name="chevron-forward" color="white" size={17} />
        </MotiView>
      </Pressable>
      <AnimateHeight enterFrom="bottom" hide={!show}>
          <View style={itemStyles.answer}>
            <Text style={itemStyles.answerText}>
              {item?.desc}
            </Text>
          </View>
        </AnimateHeight>
    </View>
    </View>
    )
  }

  return (
    <View>
      <FlatList 
        data={dummyData}
        renderItem={renderItem}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  screen: {
    flex: 1,
    backgroundColor: '#161618',
    paddingTop: Constants.statusBarHeight,
  },
});

const itemStyles = StyleSheet.create({
  question: {
    padding: 16,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
  },
  answer: {
    padding: 16,
    marginTop: -16
  },
  answerText: {
    color: '#A09FA5',
    lineHeight: 20
  },
  container: {
    borderBottomWidth: 1,
    borderBottomColor: '#232326',
  },
  questionText: {
    color: '#EDEDEE',
    fontWeight: 'bold',
  },
});

animate-height.tsx:

import '@motify/core';
import '@motify/components';

import React from 'react';
import { MotiView, TransitionConfig, useDynamicAnimation } from 'moti';
import { StyleSheet, Platform } from 'react-native';
import { useDerivedValue, useSharedValue } from 'react-native-reanimated';
import { View } from 'react-native';

type Props = {
  children?: React.ReactNode;
  /**
   * If `true`, the height will automatically animate to 0. Default: `false`.
   */
  hide?: boolean; 
  onHeightDidAnimate?: (height: number) => void;
  /**
   * Defines where the expanded view will be anchored.
   *
   * Default: `top`
   *
   * This prop is untested, use with caution
   */
  enterFrom?: 'bottom' | 'top';
  initialHeight?: number;
} & React.ComponentProps<typeof MotiView>;

function AnimateHeight({
  children,
  hide = false,
  style,
  delay = Platform.select({ web: 250, default: 0 }),
  transition = { type: 'timing', delay }, 
  enterFrom = 'top',
  onHeightDidAnimate,
  initialHeight = 0,
  ...motiViewProps
}: Props) {
  const measuredHeight = useSharedValue(initialHeight);
  const state = useDynamicAnimation(() => {
    return {
      height: initialHeight,
      opacity: !initialHeight || hide ? 0 : 1
    }
  })
  if ('state' in motiViewProps) {
    console.warn('[AnimateHeight] state prop not supported')
  }

  const animation = useDerivedValue(() => {
    let height = Math.ceil(measuredHeight.value);
    if (hide) {
      height = 0;
    } 

    const notVisible = !height || hide;
    
    state.animateTo({
      height,
      opacity: !height || hide ? 0 : 1
    });
  }, [hide, measuredHeight]);


  return (
    <MotiView
      {...motiViewProps}
      state={state}
      transition={transition}
      onDidAnimate={
        onHeightDidAnimate &&
        ((key, finished, _, { attemptedValue }) =>
          key == 'height' && onHeightDidAnimate(attemptedValue as number))
      }
      style={[styles.hidden, style]}>
      <View
        style={[
          StyleSheet.absoluteFill,
          styles.autoBottom

          // THIS BREAKS IDK WHY, so ignore that prop
          // enterFrom === 'top' ? styles.autoBottom : styles.autoTop, 
        ]}
        onLayout={({ nativeEvent }) => {
          measuredHeight.value = nativeEvent.layout.height;
        }}>
        {children}
      </View>
    </MotiView>
  );
}

const styles = StyleSheet.create({
  autoBottom: {
    bottom: 'auto',
  },
  autoTop: {
    top: 'auto',
  },
  hidden: {
    overflow: 'hidden',
  },
});

export { AnimateHeight };

and this is the Expo Snacks if you want to try it live:
https://snack.expo.dev/@mikess/moti-animated-heigt

if anyone can help or find the solution it will be a help Thank you very much

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

弄潮 2025-02-19 22:13:22

尝试这个,

app.tsx

import React, { useState } from 'react';
import { View, Pressable, StyleSheet, Text, FlatList } from 'react-native';
import { AnimateHeight } from './animated-height';
import { Ionicons } from '@expo/vector-icons';
import { MotiView } from 'moti';
import Constants from 'expo-constants';

export default function App() {
  const dummyData = [
    {
      id: 1,
      question: "are u a banana?",
      desc: "no i'm not",
    },
     {
      id: 2,
      question: "are u a apple?",
      desc: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.",
    },
  ]
  const [currentIndex, setCurrentIndex] = useState(0);
  const [show, toggle] = React.useReducer((open) => !open, false);

  const renderItem = ({item, index}:any) => {
    return(
    <View style={styles.screen}>
      <View style={itemStyles.container}>
      <Pressable onPress={()=> setCurrentIndex(index)} style={itemStyles.question}>
        <Text selectable={false} style={itemStyles.questionText}>
          {item?.question}
        </Text>
        <MotiView
          animate={{
            rotateZ: currentIndex == index ? '-90deg' : '0deg',}}>
          <Ionicons name="chevron-forward" color="white" size={17} />
        </MotiView>
      </Pressable>
      <AnimateHeight enterFrom="bottom" hide={currentIndex == index ? show : !show}>
          <View style={itemStyles.answer}>
            <Text style={itemStyles.answerText}>
              {item?.desc}
            </Text>
          </View>
        </AnimateHeight>
    </View>
    </View>
    )
  }

  return (
    <View>
      <FlatList 
        data={dummyData}
        renderItem={renderItem}
        keyExtractor={(index)=> index.toString()}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  screen: {
    flex: 1,
    backgroundColor: '#161618',
    paddingTop: Constants.statusBarHeight,
  },
});

const itemStyles = StyleSheet.create({
  question: {
    padding: 16,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
  },
  answer: {
    padding: 16,
    marginTop: -16
  },
  answerText: {
    color: '#A09FA5',
    lineHeight: 20
  },
  container: {
    borderBottomWidth: 1,
    borderBottomColor: '#232326',
  },
  questionText: {
    color: '#EDEDEE',
    fontWeight: 'bold',
  },
});

try this,

App.tsx

import React, { useState } from 'react';
import { View, Pressable, StyleSheet, Text, FlatList } from 'react-native';
import { AnimateHeight } from './animated-height';
import { Ionicons } from '@expo/vector-icons';
import { MotiView } from 'moti';
import Constants from 'expo-constants';

export default function App() {
  const dummyData = [
    {
      id: 1,
      question: "are u a banana?",
      desc: "no i'm not",
    },
     {
      id: 2,
      question: "are u a apple?",
      desc: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.",
    },
  ]
  const [currentIndex, setCurrentIndex] = useState(0);
  const [show, toggle] = React.useReducer((open) => !open, false);

  const renderItem = ({item, index}:any) => {
    return(
    <View style={styles.screen}>
      <View style={itemStyles.container}>
      <Pressable onPress={()=> setCurrentIndex(index)} style={itemStyles.question}>
        <Text selectable={false} style={itemStyles.questionText}>
          {item?.question}
        </Text>
        <MotiView
          animate={{
            rotateZ: currentIndex == index ? '-90deg' : '0deg',}}>
          <Ionicons name="chevron-forward" color="white" size={17} />
        </MotiView>
      </Pressable>
      <AnimateHeight enterFrom="bottom" hide={currentIndex == index ? show : !show}>
          <View style={itemStyles.answer}>
            <Text style={itemStyles.answerText}>
              {item?.desc}
            </Text>
          </View>
        </AnimateHeight>
    </View>
    </View>
    )
  }

  return (
    <View>
      <FlatList 
        data={dummyData}
        renderItem={renderItem}
        keyExtractor={(index)=> index.toString()}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  screen: {
    flex: 1,
    backgroundColor: '#161618',
    paddingTop: Constants.statusBarHeight,
  },
});

const itemStyles = StyleSheet.create({
  question: {
    padding: 16,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
  },
  answer: {
    padding: 16,
    marginTop: -16
  },
  answerText: {
    color: '#A09FA5',
    lineHeight: 20
  },
  container: {
    borderBottomWidth: 1,
    borderBottomColor: '#232326',
  },
  questionText: {
    color: '#EDEDEE',
    fontWeight: 'bold',
  },
});
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文