【问题标题】:React Native: Determine number of lines of Text componentReact Native:确定Text组件的行数
【发布时间】:2016-11-18 02:36:38
【问题描述】:

正如标题所说,我一直在尝试找到一种方法来确定文本组件在获得文本后的行数。看看我下面的例子。

<Text>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi semper ut ipsum in ultrices. Vivamus fringilla lacinia odio in accumsan. Proin sit amet pellentesque tortor. Nam mollis sit amet ligula id convallis. Etiam in semper diam. Cras id elit consectetur, interdum ante id, tincidunt nisi. Integer non elit placerat, dignissim nibh at, faucibus sem. Curabitur nec posuere turpis. Vivamus rhoncus nulla vitae mi imperdiet, elementum eleifend mi laoreet. Vestibulum molestie turpis non nibh elementum, sed ornare magna tristique. Aliquam erat volutpat. Phasellus volutpat mi vel tempor finibus.
</Text>

在运行时,我如何确定这个 Text 组件已经渲染了多少行。这个数字会因设备而异(例如,与 iPhone 6+ 相比,iPhone 5 需要渲染更多的线条,因为它的屏幕尺寸更小)。我已经检查了 Text 组件的源代码,但似乎没有任何我要查找的内容。

我正在使用 React Native 0.24。

有什么想法吗?

干杯。

【问题讨论】:

  • 也想知道这个问题的答案

标签: android ios text react-native


【解决方案1】:

我已经尝试了上述解决方案。恕我直言,下面的代码块适用于列表。因为它在初始渲染时不会轻弹或显示长文本

export const SomeComponent = ({text}) => {

  const NUM_LINES = 4; //Just define the minimum lines that you want to show, no matter what

  const [hasMore, setHasMore] = useState(false);
  const [showMore, setShowMore] = useState(false);
  const [numOfLines, setNumOfLines] = useState(NUM_LINES);
  const readMoreText = showMore ? 'Show Less' : 'Read More...';

  
  const onLoadMoreToggle = () => {
    setShowMore(!showMore);
  };

  const onTextLayout = e => {
    //we are checking if the original text lines are above our minimum number of lines
    setHasMore(e.nativeEvent.lines.length > NUM_LINES);
    //storing original number of lines
    setNumOfLines(e.nativeEvent.lines.length);
  };

  if (isBlank(post.text)) {
    return null;
  }

  return (
    <Pressable onPress={() => onLoadMoreToggle()}>
      <Text
        onTextLayout={onTextLayout}
        style={{opacity: 0, position: 'absolute', ...styles.text}}>
        {post.text}
      </Text>
      <Text
        style={styles.text}
        numberOfLines={
          !hasMore ? NUM_LINES : showMore ? numOfLines : NUM_LINES
        }>
        {text}
      </Text>
      {hasMore && (
        <View style={styles.readMoreContainer}>
          <Text style={styles.readMore}>{readMoreText}</Text>
        </View>
      )}
    </Pressable>
  );
};

【讨论】:

    【解决方案2】:

    这个适用于IOS和Android

        const [ loadMore, setLoadMore ] = useState(false);
        const [ numOfLines, setNumOfLines ] = useState(0);
    
        const onTextLayout = useCallback(e => {
            if(numOfLines == 0)
                setNumOfLines(e.nativeEvent.lines.length);
        });
    
        const onLoadMoreToggle = () => {
            setLoadMore(!loadMore);
        }
        
        return (
            <View style={styles.container}>
                <Text 
                    numberOfLines={numOfLines == 0 ? null : loadMore ? numOfLines : NUM_OF_LINES} 
                    onTextLayout={onTextLayout} 
                    style={styles.bodyText}
                >
                    {props.children}
                </Text>
                {
                    (numOfLines > NUM_OF_LINES) &&
                    <View style={styles.linkContainer}>
                        <TouchableRipple onPress={onLoadMoreToggle}>
                            <Text style={styles.linkText}>{ loadMore? 'Load Less' :'Load More'}</Text>
                        </TouchableRipple>
                    </View>
                }
            </View>
        )
    
    const styles = StyleSheet.create({
        container: {
            display: 'flex',
            flexDirection:'column',
        },
        bodyText: {
            flex:1,
        },
        linkContainer: {
            flexDirection: 'row',
            justifyContent: 'flex-end'
        },  
        linkText: {
            color: '#2196f3'
        }
    })
    

    【讨论】:

    • 非常感谢!这就像一个魅力!
    【解决方案3】:

    我想提供一个现代解决方案。现在有一个 onTextLayout 事件,其中包括一个 lines 数组,可以确定正在渲染的行数。 lines 数组中还有其他详细信息,例如每行的实际高度和宽度,可进一步用于确定文本是否被截断。

    const NUM_OF_LINES = 5;
    const SOME_LONG_TEXT_BLOCK = 'Lorem ipsum ...';
    
    function SomeComponent () { 
      const [ showMore, setShowMore ] = useState(false);
      const onTextLayout = useCallback(e => {
        setShowMore(e.nativeEvent.lines.length > NUM_OF_LINES);
      }, []);
    
      return (
        <Text numberOfLines={NUM_OF_LINES} onTextLayout={onTextLayout}>
          {SOME_LONG_TEXT_BLOCK}
        </Text>
      );
    }
    

    【讨论】:

    • 谢谢 Amo,您的解决方案是最优雅的。但为了同时适用于 Android 和 iOS,条件必须是:lines.length >= NUM​​_OF_LINES
    • 替代解决方案(尚未测试):github.com/nlt2390/react-native-view-more-text
    • 替代解决方案(尚未测试):github.com/expo/react-native-read-more-text
    • @Amo 答案可以很好地显示阅读更多选项。但在一种情况下,它不起作用。假设我的完整文本适合 5 行,因为存在 '>=' 条件。所以在这种情况下它仍然显示阅读更多按钮,这是不正确的并且没有显示。在这种情况下我们能做什么?
    • @PiyushNaredi 只需将其更改为 > vs >= 并且仅当线条实际上大于显示的内容时才会显示它。我编辑了答案以反映这一点。
    【解决方案4】:

    Garrett McCullough 提供的解决方案似乎对我有用,我只想添加一些代码示例:

    import React from 'react';
    import { StyleSheet, Text, View, TouchableHighlight } from 'react-native';
    
    const styles = StyleSheet.create({
        text: {
          fontSize: 24,
          lineHeight: 30,
        }
    });
    
    export default class App extends React.Component {
    
        onLayout = e => {
            const { height } = e.nativeEvent.layout;
            this.count = Math.floor(height / styles.text.lineHeight)
        }
    
        render() {
            return (
                <View style={styles.page}>
                  <Text onLayout={this.onLayout} style={styles.text}>
                    Random text. Random text. Random text. Random text. Random text. Random text. Random text.
                    Random text. Random text. Random text. Random text. Random text. Random text. Random text.
                    Random text. Random text. Random text. Random text. Random text. Random text. Random text.
                    Random text. Random text. Random text. Random text. Random text. Random text.
                  </Text>
    
                  <TouchableHighlight onPress={() => alert(`text lines count is ${this.count}`)}>
                    <Text style={{ fontSize: 50 }}>touch me!</Text>
                  </TouchableHighlight>
                </View>
            );
        }
    }
    

    https://snack.expo.io/@devastr1/text-lines-count-example

    【讨论】:

      【解决方案5】:

      你可以使用这个公式:

      CPL = 宽度/(字体大小/字体常量)

      font-constant = 为每种字体指定的常量。 CPL = 每行字符数

      这里有一些带有常量的字体:

       - Serif Fonts:
       American Typewriter — 2.14
       Baskerville — 2.14
       Georgia — 1.91
       Times New Roman — 2.21
      
       - Sans-serif Fonts:
       Arial — 1.91
       Calibri — 2.1
       Helvetica Neue — 1.9
       Lucida Grande — 1.91
       Tahoma — 1.91
       Trebuchet MS — 2.11
       Verdana — 1.73
      
       - Monospace Font:
       Courier New — 1.64
      

      例如:

      function getNumberOfLines(text, fontSize, fontConstant, containerWidth){
      
          let cpl = Math.floor(containerWidth / (fontSize / fontConstant) );
          const words = text.split(' ');
          const elements = [];
          let line = '';
      
          while(words.length > 0){
              if(line.length + words[0].length + 1 <= cpl || line.length === 0 && words[0].length + 1 >= cpl){
                  let word = words.splice(0,1);
                  if(line.length === 0){
                      line = word;
                  }else {
                      line = line + " " + word;
                  }
                  if(words.length === 0){
                      elements.push(line);
                  }
              }
              else {
                  elements.push(line);
                  line = "";
              }
          }
          return elements.length;
      }
      

      【讨论】:

      • 你怎么知道字体常量?
      • 有多种字体,大小写不同的字体常量(相同的字体)
      【解决方案6】:

      看起来 React Native 0.24 实现了一个 onLayout 函数
      http://facebook.github.io/react-native/docs/text.html#onlayout

      onLayout 函数
      在安装和布局更改时调用
      {nativeEvent: {layout: {x, y, width, height}}}

      所以看起来你可以传递一个 onLayout 回调函数,获取 Text 组件的高度,然后使用行高进行一些计算以获得行数

      【讨论】:

        猜你喜欢
        • 2020-05-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-21
        • 2020-11-10
        • 1970-01-01
        • 2016-02-02
        • 2015-12-04
        相关资源
        最近更新 更多