4

背景

使用 React Native 我能够制作可折叠的卡片组件。在图标上单击卡片会向上滑动隐藏其内容,或展开显示其内容。我认为设置默认值就像设置扩展为 false 或 true 一样简单,但我认为这里的问题是,当它被切换时,会触发一个动画,从而改变卡片的高度。

例子

class CardCollapsible extends Component{
  constructor(props){
    super(props);

    this.state = {
      title: props.title,
      expanded: true,
      animation: new Animated.Value(),
      iconExpand: "keyboard-arrow-down",
    };
  }

  _setMaxHeight(event){
      this.setState({
          maxHeight   : event.nativeEvent.layout.height
      });
  }

  _setMinHeight(event){
      this.setState({
          minHeight   : event.nativeEvent.layout.height
      });

      this.toggle = this.toggle.bind(this);
  }

  toggle(){
    let initialValue    = this.state.expanded? this.state.maxHeight + this.state.minHeight : this.state.minHeight,
        finalValue      = this.state.expanded? this.state.minHeight : this.state.maxHeight + this.state.minHeight;

    this.setState({
      expanded : !this.state.expanded
    });

    if (this.state.iconExpand === "keyboard-arrow-up") {
      this.setState({
        iconExpand : "keyboard-arrow-down"
      })
    } else {
      this.setState({
        iconExpand : "keyboard-arrow-up"
      })
    }
    this.state.animation.setValue(initialValue);
    Animated.spring( this.state.animation, {
        toValue: finalValue
      }
    ).start();
  }

  render(){

    return (
      <Animated.View style={[styles.container,{height: this.state.animation}]}>
          <View style={styles.titleContainer} onLayout={this._setMinHeight.bind(this)}>
            <CardTitle>{this.state.title}</CardTitle>
            <TouchableHighlight
              style={styles.button}
              onPress={this.toggle}
              underlayColor="#f1f1f1">
              <Icon
                name={this.state.iconExpand}
                style={{ fontSize: 30 }}/>
            </TouchableHighlight>
          </View>
          <Separator />
          <View style={styles.card} onLayout={this._setMaxHeight.bind(this)}>
            {this.props.children}
          </View>
      </Animated.View>
    );
  }
}

var styles = StyleSheet.create({
  container: {
    backgroundColor: '#fff',
    margin:10,
    overflow:'hidden'
    },
  titleContainer: {
    flexDirection: 'row'
    },
  card: {
    padding: 10
  }
});

export { CardCollapsible };

打开

在此处输入图像描述

关闭

在此处输入图像描述

问题

我的目标是允许调用组件的人将组件的初始状态设置为展开或打开。但是当我尝试将展开状态更改为false它时不会呈现关闭状态。

我将如何允许调用组件的用户选择在初始组件渲染时是展开还是关闭它?

4

5 回答 5

5

为你做了一个全新的。简单且工作正常。

注意:state此组件不需要。更少的状态,更好的性能。

也许您可以在此基础上修改自己的样式 =)

class Card extends Component {
    anime = {
        height: new Animated.Value(),
        expanded: false,
        contentHeight: 0,
    }

    constructor(props) {
        super(props);

        this._initContentHeight = this._initContentHeight.bind(this);
        this.toggle = this.toggle.bind(this);

        this.anime.expanded = props.expanded;
    }

    _initContentHeight(evt) {
        if (this.anime.contentHeight>0) return;
        this.anime.contentHeight = evt.nativeEvent.layout.height;
        this.anime.height.setValue(this.anime.expanded ? this._getMaxValue() : this._getMinValue() );
    }

    _getMaxValue() { return this.anime.contentHeight };
    _getMinValue() { return 0 };

    toggle() {
        Animated.timing(this.anime.height, {
            toValue: this.anime.expanded ? this._getMinValue() : this._getMaxValue(),
            duration: 300,
        }).start();
        this.anime.expanded = !this.anime.expanded;
    }

    render() {
        return (
            <View style={styles.titleContainer}>
                <View style={styles.title}>
                    <TouchableHighlight underlayColor="transparent" onPress={this.toggle}>
                        <Text>{this.props.title}</Text>
                    </TouchableHighlight>
                </View>

                <Animated.View style={[styles.content, { height: this.anime.height }]} onLayout={this._initContentHeight}>
                    {this.props.children}
                </Animated.View>
            </View>
        );
    }
}

用法:

<Card title='Customized Card 1' expanded={false}>
    <Text>Hello, this is first line.</Text>
    <Text>Hello, this is second line.</Text>
    <Text>Hello, this is third line.</Text>
</Card>

视觉结果:(只有第二张卡片以 开头expanded={true},其他卡片以 开头expanded={false}

在此处输入图像描述

于 2017-08-01T03:12:37.807 回答
0

扩展值“true/false”应从调用组件传递,您需要使用此值来扩展/折叠组件。我看不到您在渲染方法中使用了“扩展”。你应该这样做:

 { this.props.expanded && <View style={styles.card} onLayout={this._setMaxHeight.bind(this)}>
            {this.props.children}
          </View> }
于 2017-08-01T03:13:33.930 回答
0

我按照一些博客上的步骤进行操作,然后我的情况与之相同。所以我在这里从其他人那里得到线索后做出了改变。我的帖子可能来晚了。但我希望能帮助别人。

import React from 'react';
import {
  Text,
  View,
  TouchableOpacity,
  Image,
  Animated,
  StyleSheet
} from 'react-native'
import styles from './styles' //put styles from another file

class PanelExpanding extends Component{
  constructor(props) {
    super(props)
    this.state = {
      expanded: false, //step 1 
      animation: new Animated.Value()
    }

    this.icons = {
      'up': require('../../..assets/images/up.png'),
      'down': require('../../../assets/images/down.png')
    }
  }

  toggle(){
    let finalValue    = this.state.expanded? this.state.maxHeight + this.state.minHeight : this.state.minHeight,
        initialValue  = this.state.expanded? this.state.minHeight : this.state.maxHeight + this.state.minHeight;
    //step 2, this needed, if we use !this.state.expanded i don't know how it wont changes at first
    if(this.state.expanded === true){
      return this.setState({ expanded : false })
    } else {
      return this.setState({ expanded : true })
    }
    this.state.animation.setValue(initialValue);
    Animated.spring(
        this.state.animation,
        { toValue: finalValue }
    ).start();
  }


  _setMinHeight(event){
    this.setState({
        minHeight : event.nativeEvent.layout.height
    });
  }

  _setMaxHeight(event){
    this.setState({
        maxHeight : event.nativeEvent.layout.height
    });
  }


  render(){
    let icon = this.icons['down'],
        textSwap = 'SHOWMORE'

    if(this.state.expanded){
      icon = this.icons['up'],
      textSwap = 'SHOWLESS'
    }

    return (
      <Animated.View
            style={[styles.containerPanel,{height: this.state.animation}]} >
        <View>
          <View style={styles.titleContainer} onLayout={this._setMinHeight.bind(this)}>
              <TouchableOpacity
                  style={styles.button}
                  onPress={this.toggle.bind(this)} >
                  <Text style={styles.textShow}>{textSwap}</Text>
                  <Image
                      style={styles.buttonImage}
                      source={icon} />
              </TouchableOpacity>
          </View>

          //step 3, add this to initial when first render is false
          {this.state.expanded && <View style={styles.body} onLayout={this._setMaxHeight.bind(this)}>
            <Text>
              {this.props.children}
            </Text>
          </View>}
        </View>
      </Animated.View>
    )
  }
}

export default PanelExpanding
于 2018-03-14T02:47:34.190 回答
0
// With hooks for rn


import React, { useState, useEffect } from 'react'
import {
    Text,
    View,
    TouchableOpacity,
    Image,
    StyleSheet
  } from 'react-native'
  import styles from '../../assets/styles' 
  const usePanelExpanding = (props) => {


    let icons = {
        'up': require('../../images/formicons/icons/close.png'),
        'down': require('../../images/formicons/icons/disclosure.png')
      }

      const [expanded, setExpanded] = useState(false);
      const [iconShow, setIconShow] = useState('up');
      const [textSwap, setTextSwap] = useState('Show more...');


      useEffect(
        () => {
          expanded ? setIconShow('up') : setIconShow('down')
          expanded ? setTextSwap('Show less') : setTextSwap('Show more ...')
        },
        [expanded]
      );

      const toggle = () =>{
        setExpanded(!expanded)
      }

      const panel = (<View>
        <View style={styles.titleContainer} >
            <TouchableOpacity
                style={styles.button}
                onPress={toggle} >
                <Image
                    style={styles.buttonImage}
                    source={icons[iconShow]} />
            </TouchableOpacity>
        </View>
        {expanded && <View style={styles.mainPageFull} >
            {props}
        </View>
      }
      </View>)
      return {
        panel,
        toggle
      }
  }
  export default usePanelExpanding


// How to use it from a parent function

 const expandingBioObject = usePanelExpanding(<Text>{profile.bio}</Text>)
<Card title="Biography" containerStyle={styles.CardContainerBio}>
      {expandingBioObject.panel}
      </Card>
于 2019-04-23T23:01:10.170 回答
0

使用react-native-collapsible-view。您可以传递它expanded的属性,该属性允许您完全控制其他组件的可折叠状态。
您也可以让他自行控制,但通过initExpanded它可以让您选择可折叠的初始状态。

于 2020-08-31T19:36:10.843 回答