wake-up-neo.net

Dynamische Breite und Höhe der Flexbox

Ich versuche, eine Meldungsansicht mithilfe von Reaktiver-Eigenart zu erstellen, z.

enter image description here

Wie du siehst:

  1. Die Blasen haben eine dynamische Breite und Höhe, basierend auf dem Inhalt
  2. Es gibt eine maximale Breite für die Blasen und sie wachsen nach unten

Ich versuche, dies mithilfe von native native zu erstellen. Allerdings kann ich nur 2 erreichen und weiß nicht, wie ich beides erreichen soll ...

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>

Wenn ich die orange Ansicht vergrößere, um eine große Blase darzustellen, wird sie einfach vom Bildschirm genommen ... z. B .: 

enter image description here

14
kingkong

Ich hatte das gleiche Problem. Ich habe viele Dinge ausprobiert, einschließlich Einlegen von Wrappern hier und dort (wie in den vorherigen Antworten erwähnt). Keiner von ihnen hat gearbeitet.

@ André Junges Antwort ist nicht korrekt, weil @kingkong und ich unterschiedliche Anforderungen haben.

Dann habe ich gesehen, dass Flex auch den Wert -1 annehmen kann. Durch das Einstellen wurde das Problem gelöst.

Hier ist der Code:

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

Und hier ist das Ergebnis:

 And here is the result:

15
vin

Ich habe mir dabei eine etwas ausgeklügelte Art ausgedacht. Sehen wir uns zuerst das Problem an.

Wir können flexbox verwenden, um das "Abzeichen" links und den Text rechts zu platzieren. Dann werden "Nachrichtenzeilen" horizontal nach unten verschoben. Das ist einfach, aber wir möchten, dass die Nachrichtenzeile die Breite je nach Inhalt ändert. Flexbox lässt Sie dies nicht zu, da sie gierig erweitert wird, um den gesamten Platz auszufüllen.

Was wir brauchen, ist eine Möglichkeit, die Breite des Nachrichtentextes zu überprüfen und dann die Ansicht entsprechend anzupassen, um sie auf eine bestimmte Breite zu bringen. Wir können nicht measure verwenden, um die Textbreite zu ermitteln, da uns dies eigentlich nur die Breite des zugrunde liegenden Knotens angibt, nicht den eigentlichen Text.

Dafür habe ich eine Idee von hier gestohlen und eine Brücke zu Obj-C erstellt, die ein UILabel mit dem Text erstellt und seine Breite auf diese Weise erhält. 

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

Ich habe dann die Verwendung davon in eine Komponente verpackt:

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

Alles, was Sie tun müssen, ist die Größe der enthaltenden Ansicht zu ändern, wenn die Breite des Texts geringer ist als die ursprüngliche Breite der Ansicht, die von Flexbox festgelegt wurde. Der Rest der App sieht so aus:

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

Und es gibt dir das:

enter image description here

Ich würde das Github-Problem im Auge behalten, da diese Lösung definitiv etwas klobig ist und ich zumindest erwarten würde, irgendwann eine bessere Methode zum Messen von Text in RN zu finden.

4
Colin Ramsay

Ich weiß, dass diese Frage von vor einem Jahr stammt, aber ich hatte ein ähnliches Problem und denke, dass es für andere Entwickler nützlich sein kann.

Was ich also erreichen wollte, war das Erstellen von Kästchen wie dem Bild unten, wo links ein Text und rechts ein Text angezeigt würde.
Beachten Sie, dass die Felder eine auto-Höhe haben sollten, da der Text aus mehreren Zeilen bestehen kann.

 enter image description here

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>

Styles

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',
},

Und natürlich hat es nicht funktioniert. Wenn ich ein Elternteil mit flexDirection: 'row' hätte, würde das Kind aus irgendeinem Grund nicht wachsen. Der Text wurde in mehrere Zeilen umbrochen, aber die Höhe des übergeordneten Elements (.textContainer) wurde nicht größer. In einigen Fällen wurde der Text sogar außerhalb des Containers angezeigt. Sie können es im Bild unten sehen (dritte Nachricht).

 enter image description here

Das Update bestand darin, die gesamte Komponente mit einer weiteren View zu versehen, wie im folgenden Code:

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

Die .wrapper-Klasse enthält nur Stildetails (aus .container verschoben).

wrapper: {
  backgroundColor: '#FFF',
  marginBottom: Metrics.baseMargin,
  borderRadius: 3,
},
2
André Junges

Ich denke, die Strategie hier ist, dass Sie die Nachrichtenblase in einem Block mit voller Breite halten und dann zwei innere Ansichten haben möchten. Eine Innenansicht versucht, so dünn wie möglich zu sein, das ist die, in die Sie Text einfügen. Die andere versucht, so breit wie möglich zu sein, wodurch der andere Block so weit komprimiert wird, dass er den Text enthält.

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

Kann laut Codierung nicht nützlich sein. Aber es scheint nicht für dynamischen Inhalt zu funktionieren, funktioniert nur mit statischem Inhalt ...

0
Sanjeev Rao