【问题标题】:Flex box dynamic width and heightFlex box 动态宽度和高度
【发布时间】:2015-09-22 09:24:01
【问题描述】:

我正在尝试使用 react-native 创建消息视图,例如:

如你所见:

  1. 气泡具有基于内容的动态宽度和高度
  2. 气泡有一个最大宽度,它们会向下增长

我正在尝试使用 react native 重新创建它,但是我只能实现 (2) 并且不知道如何实现这两者.. 这是我迄今为止所拥有的:

  <View style={{flex:1,backgroundColor:'blue',flexDirection:'row'}}>
     <View style={{backgroundColor:'orange'}}>
            <View style={{width:90,flex:1}}>
              <Text>123</Text>
              </View>
     </View>
     <View style={{flex:0.25,backgroundColor:'red'}}>
              <Text>123</Text>
     </View>
  </View>

如果我增加橙色视图来代表一个大气泡,那么它就会离开屏幕......例如:

【问题讨论】:

  • 根据编码可能没有用。但似乎它不适用于动态内容,仅适用于静态内容......

标签: flexbox react-native


【解决方案1】:

所以我认为这里的策略是您希望将消息气泡包含在一个全宽块内,然后有两个内部视图。一个内部视图尝试尽可能地薄,即您将文本放入其中。另一个尝试尽可能宽,从而将另一个块压缩到足以容纳文本的宽度。

<View style={{flex:1,backgroundColor:'blue',flexDirection:'row'}}>
  <View style={{flex:0,backgroundColor:'red'}}>
    <Text>123</Text>
  </View>
  <View style={{backgroundColor:'orange', flex: 1}}/> 
</View>

【讨论】:

  • 嗨 - 这是我在上面的例子中玩的,但是问题是限制我们想要动态宽度的 flex 的大小,然后让它在高度上增长而不是推动其他人不在视野中……这就是现在正在发生的事情……
  • @kingkong 如果您想限制宽度,您可以在红色框中包含的元素上设置 maxWidth。
  • 不要认为 maxWidth 是在 react-native 中实现的... :(
【解决方案2】:

我想出了一个有点做作的方法来做到这一点。我们先来看问题。

我们可以使用 flexbox 将“徽章”放在左侧,将文本放在右侧,然后让“消息行”水平向下。这很简单,但我们希望消息行根据其内容改变宽度,而 flexbox 不会让您这样做,因为它会贪婪地扩展以填满所有空间。

我们需要一种方法来检查消息文本的宽度,然后相应地调整视图的大小,将其强制为指定的宽度。我们不能使用measure 来获取文本宽度,因为它实际上只给了我们底层节点的宽度,而不是实际的文本本身。

为此,我偷了一个想法from here 并创建了一个到 Obj-C 的桥梁,它创建了一个带有文本的 UILabel 并以这种方式获取它的宽度。

//  TextMeasurer.h
#import "RCTBridgeModule.h"
#import <UIKit/UIKit.h>

@interface TextMeasurer : NSObject<RCTBridgeModule>

@end


//  TextMeasurer.m
#import "TextMeasurer.h"

@implementation TextMeasurer

RCT_EXPORT_MODULE();

RCT_EXPORT_METHOD(get:(NSString *)text cb:(RCTResponseSenderBlock)callback)
{
  UILabel *label = [[UILabel alloc]init];
  label.font = [UIFont fontWithName:@"Helvetica" size:14.0];
  label.text = text;

  callback(@[[NSNumber numberWithDouble: label.intrinsicContentSize.width]]);
}

@end

然后我将 this 的用法包装到一个组件中:

var AutosizingText = React.createClass({
    getInitialState: function() {
        return {
            width: null
        }
    },

    componentDidMount() {
        setTimeout(() => {
            this.refs.view.measure((x, y, width, height) => {
                TextMeasurer.get(this.props.children, len => {
                    if(len < width) {
                        this.setState({
                            width: len
                        });
                    }
                })
            });
        });
    },

    render() {
        return <View ref="view" style={{backgroundColor: 'red', width: this.state.width}}><Text ref="text">{this.props.children}</Text></View>
    }
});

如果文本的宽度小于视图的原始宽度 - 这将由 flexbox 设置,所有这些都会调整包含视图的大小。应用程序的其余部分如下所示:

var messages = React.createClass({
    render: function() {
        var rows = [
            'Message Text',
            'Message Text with lots of content ',
            'Message Text with lots of content put in here ok yeah? Keep on talking bla bla bla whatever is needed to stretch this message body out.',
            'Keep on talking bla bla bla whatever is needed to stretch this message body out.'
        ].map((text, idx) => {
            return <View style={styles.messageRow}>
                <View style={styles.badge}><Text>Me</Text></View>
                <View style={styles.messageOuter}><AutosizingText>{text}</AutosizingText></View>
            </View>
        });

        return (
            <View style={styles.container}>
                {rows}
            </View>
        );
    }
});

var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'flex-start',
    alignItems: 'stretch',
    flexDirection: 'column',
    backgroundColor: '#F5FCFF',
  },
  messageRow: {
    flexDirection: 'row',
    margin: 10
  },
  badge: {
    backgroundColor: '#eee',
    width: 80, height: 50
  },
  messageOuter: {
    flex: 1,
    marginLeft: 10
  },
  messageText: {
    backgroundColor: '#E0F6FF'
  }
});

AppRegistry.registerComponent('messages', () => messages);

它给了你这个:

我会密切关注Github issue,因为这个解决方案肯定有点笨拙,至少我希望在某个时候看到一种更好的方法来测量 RN 中的文本。

【讨论】:

    【解决方案3】:

    我知道这个问题是一年前提出的,但我遇到了类似的问题,我认为它对其他开发人员很有用。

    所以我想要实现的是创建如下图所示的框,其中左侧有一个文本,右侧有一个箭头。
    请注意,这些框应该有一个auto 高度 - 因为文本可以是多行..

    JSX

    <View style={styles.container}>
      <View style={styles.textContainer}>
        <Text style={styles.text}>{props.message.text}</Text>
      </View>
      <View style={styles.iconContainer}>
        <Icon name="chevron-right" size={30} color="#999" />
      </View>
    </View>
    

    样式

    container: {
      flex: 1,
      flexDirection: 'row',
      alignItems: 'center',
      backgroundColor: '#FFF',
      marginBottom: Metrics.baseMargin,
      borderRadius: 3,
    },
    textContainer: {
      flex: 1,
      flexDirection: 'column',
      paddingVertical: 30,
      paddingHorizontal: Metrics.doubleBaseMargin,
    },
    text: {
      color: '#333',
    },
    iconContainer: {
      width: 35,
      borderLeftWidth: 1,
      borderLeftColor: '#ddd',
      justifyContent: 'center',
      alignItems: 'center',
      alignSelf: 'stretch',
    },
    

    当然,它没有用。出于某种原因,如果我有一个flexDirection: 'row' 的父母,孩子就不会长大。 文本被包裹在多行中,但父级的高度 (.textContainer) 没有增长 - 在某些情况下,文本甚至显示在容器之外。您可以在下图(第三条消息)。

    解决方法是用一个View 包裹整个组件,如下面的代码:

    <View style={styles.wrapper}>
      <View style={styles.container}>
        <View style={styles.textContainer}>
          <Text style={styles.text}>{props.message.text}</Text>
        </View>
        <View style={styles.iconContainer}>
          <Icon name="chevron-right" size={30} color="#999" />
        </View>
      </View>
    </View>
    

    .wrapper 类只有样式细节(从.container 移出)

    wrapper: {
      backgroundColor: '#FFF',
      marginBottom: Metrics.baseMargin,
      borderRadius: 3,
    },
    

    【讨论】:

      【解决方案4】:

      我遇到了同样的问题。我尝试了很多事情,包括在这里和那里放置包装器(如前面的答案中提到的)。他们都没有工作。

      @André Junges 的回答不正确,因为我和@kingkong 有不同的要求。

      然后,我看到 flex 也可以取值 -1。并设置它解决了问题。

      代码如下:

      const messages = [
                    'hello',
                    'this is supposed to be a bit of a long line.',
                    'bye'
                    ];
      return (
        <View style={{
                  position: 'absolute',
                  top: 0,
                  left: 0,
                  width: 150,
                  alignItems: 'flex-end',
                  justifyContent: 'flex-start',
                  backgroundColor: '#fff',
                  }}>
      
      {messages.map( (message, index) => (
        <View key={index} style={{
                        flexDirection: 'row',
                        marginTop: 10
                      }}>
          <View style={{
                        flex: -1,
                        marginLeft: 5,
                        marginRight: 5,
                        backgroundColor: '#CCC',
                        borderRadius: 10,
                        padding: 5,
                      }}>
            <Text style={{
                          fontSize: 12,
                          }}>
              {message}
            </Text>
          </View>
          <Image source={require('some_path')} style={{width:30,height:30}} />
         </View> 
        ))} 
       </View>
      )

      结果如下:

      【讨论】:

      • 您知道如何设置最大宽度,如 OP 示例中所示?
      猜你喜欢
      • 2011-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-14
      • 2014-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多