Подтвердить что ты не робот

Динамическая ширина и высота гибкой коробки

Я пытаюсь создать представление сообщений, используя action-native, например:

enter image description here

Как вы можете видеть:

  • Пузыри имеют динамическую ширину и высоту на основе содержимого
  • Существует максимальная ширина пузырьков, и они растут вниз

Я пытаюсь воссоздать это, используя реакцию native, но я могу только добиться (2) и не уверен, как поступить поближе. Это то, что я до сих пор:

enter image description here

  <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>

Если я увеличиваю оранжевый вид, чтобы представить большой пузырь, тогда он просто отключается от экрана... например.

enter image description here

4b9b3361

Ответ 1

У меня была такая же проблема. Я пробовал много вещей, включая размещение оберток здесь и там (как упоминалось в предыдущих ответах). Ни один из них не работал.

@Ответ André Junges неверен, потому что у меня есть разные требования. @/

Затем я увидел, что 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>
)

Ответ 2

Я придумал несколько придуманный способ сделать это. Сначала рассмотрим проблему.

Мы можем использовать flexbox, чтобы поместить "значок" слева и текст справа, а затем "строки сообщений" спускаются горизонтально. Это легко, но мы хотим, чтобы строка сообщений изменяла ширину в зависимости от ее содержимого, а flexbox не позволит вам сделать это, поскольку она с жадностью расширяет, чтобы заполнить все пространство.

Нам нужен способ проверки ширины текста сообщения, а затем изменение размера представления, заставляя его заданную ширину. Мы не можем использовать measure для получения ширины текста, поскольку на самом деле это дает только ширину базового node, а не самого собственно текста.

Чтобы сделать это, я украл идею здесь и создал мост к 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

Затем я обернул его использование в компонент:

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);

И это дает вам следующее:

enter image description here

Я буду следить за Github issue, поскольку это решение, безусловно, немного неуклюже и, по крайней мере, я ожидаю см. лучший способ измерения текста в RN в какой-то момент.

Ответ 3

Итак, я считаю, что стратегия здесь заключается в том, что вы хотите содержать пузырь сообщения внутри блока полной ширины, а затем иметь два внутренних вида. Один внутренний взгляд пытается быть как можно более тонким, чем тот, в который вы вставляете текст. Другой пытается быть как можно более широким, тем самым уплотняя другой блок, достаточно широкий, чтобы содержать текст.

<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>

Ответ 4

Я знаю, что этот вопрос относится к году назад, но у меня была аналогичная проблема, и я думаю, что это может быть полезно для других разработчиков.

Итак, я пытался создать такие поля, как изображение ниже, где у меня будет текст слева, а стрелка справа.
Обратите внимание, что поля должны иметь высоту 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,
},