You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaFX非托管节点场景下Text组件文本裁剪实现问询

Solution for Truncating & Cropping Non-Managed JavaFX Text Nodes

Since you're avoiding Label and working with non-managed Text nodes, you'll need to handle text truncation manually by calculating how much text fits within your desired bounds. Here's a practical, step-by-step approach:

Key Tools to Use

  • FontMetrics: Get precise measurements of your text's width and line height using the font applied to the Text node.
  • Manual Text Truncation: Use efficient checks (like binary search for single-line) to find the longest substring that fits your bounds, then append ellipsis if needed.
  • Explicit Layout: Since your node is non-managed, you'll set its position and size directly after adjusting the text content.

Example Implementation

First, create a helper class with methods for both single-line and multi-line truncation:

import javafx.scene.text.Font;
import javafx.scene.text.FontMetrics;
import javafx.scene.text.Text;

public class TextTruncationHelper {
    // Single-line truncation (adds ellipsis when text exceeds max width)
    public static String truncateSingleLine(Text textNode, double maxWidth) {
        String originalText = textNode.getText();
        if (originalText == null || originalText.isEmpty()) return "";
        
        Font font = textNode.getFont();
        FontMetrics metrics = Font.font(font.getFamily(), font.getStyle(), font.getSize()).getFontMetrics();
        
        // Return full text if it fits
        if (metrics.computeStringWidth(originalText) <= maxWidth) {
            return originalText;
        }
        
        // Binary search to find the longest fitting substring
        int low = 0;
        int high = originalText.length();
        int bestLength = 0;
        double ellipsisWidth = metrics.computeStringWidth("...");
        
        while (low <= high) {
            int mid = (low + high) / 2;
            String substring = originalText.substring(0, mid);
            double width = metrics.computeStringWidth(substring);
            
            if (width <= maxWidth - ellipsisWidth) {
                bestLength = mid;
                low = mid + 1;
            } else {
                high = mid - 1;
            }
        }
        
        return originalText.substring(0, bestLength) + "...";
    }

    // Multi-line truncation (wraps text and cuts off at max height)
    public static String truncateMultiLine(Text textNode, double maxWidth, double maxHeight) {
        String originalText = textNode.getText();
        if (originalText == null || originalText.isEmpty()) return "";
        
        Font font = textNode.getFont();
        FontMetrics metrics = Font.font(font.getFamily(), font.getStyle(), font.getSize()).getFontMetrics();
        double lineHeight = metrics.getLineHeight();
        int maxLines = (int) Math.floor(maxHeight / lineHeight);
        
        if (maxLines <= 0) return "";
        
        // Set wrapping width to calculate line breaks
        textNode.setWrappingWidth(maxWidth);
        double fullHeight = textNode.getLayoutBounds().getHeight();
        
        // Return full text if it fits within max height
        if (fullHeight <= maxHeight) {
            return originalText;
        }
        
        // Incrementally build text until we exceed max lines
        StringBuilder sb = new StringBuilder();
        int lastSpaceIndex = -1;
        
        for (int i = 0; i < originalText.length(); i++) {
            char c = originalText.charAt(i);
            sb.append(c);
            
            textNode.setText(sb.toString());
            int currentLines = (int) Math.ceil(textNode.getLayoutBounds().getHeight() / lineHeight);
            
            if (currentLines > maxLines) {
                // Roll back to last space to avoid cutting words mid-way
                if (lastSpaceIndex != -1) {
                    sb.setLength(lastSpaceIndex);
                } else {
                    sb.setLength(sb.length() - 1);
                }
                sb.append("...");
                break;
            }
            
            if (c == ' ') {
                lastSpaceIndex = sb.length() - 1;
            }
        }
        
        return sb.toString();
    }
}

Integrate into Your Custom Layout

For Single-Line Text

// Initialize your non-managed Text node
Text textNode = new Text("Your long single-line text that needs truncating here");
textNode.setManaged(false);

// Define your desired bounds and position
double maxWidth = 200;
double xPos = 50;
double yPos = 50;

// Truncate the text
String truncatedText = TextTruncationHelper.truncateSingleLine(textNode, maxWidth);
textNode.setText(truncatedText);

// Set position for the non-managed node
textNode.setLayoutX(xPos);
textNode.setLayoutY(yPos);
// Optional: Set explicit width to match your max bounds
textNode.setPrefWidth(maxWidth);

For Multi-Line Text

// Initialize your non-managed Text node
Text textNode = new Text("Your long multi-line text that needs wrapping and truncating here. This will wrap to multiple lines until it hits the height limit.");
textNode.setManaged(false);

// Define your desired bounds and position
double maxWidth = 200;
double maxHeight = 80;
double xPos = 50;
double yPos = 50;

// Truncate the text
String truncatedText = TextTruncationHelper.truncateMultiLine(textNode, maxWidth, maxHeight);
textNode.setText(truncatedText);

// Set wrapping width and position
textNode.setWrappingWidth(maxWidth);
textNode.setLayoutX(xPos);
textNode.setLayoutY(yPos);
// Clamp height to your max bounds
textNode.setMaxHeight(maxHeight);

Critical Notes for Non-Managed Nodes

  • Update text first: The Text node's bounds depend on its content, so always truncate before setting layout positions or sizes.
  • Dynamic updates: Add listeners to the textProperty() or fontProperty() of your Text node to re-run truncation whenever content or font changes.
  • Avoid layout conflicts: Since the node is non-managed, the parent Pane won't adjust its position, so you're fully responsible for keeping its layout consistent with your custom logic.

内容的提问来源于stack exchange,提问作者O Roundabout

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 07:01:14