web-dev-qa-db-ja.com

TextAreaをストレッチしてコンテンツを埋め、プロセスで親を拡張するにはどうすればよいですか?

つまり、TextAreaがあり、ユーザーが段落を貼り付けるか、単に書き込むときに、垂直方向に展開して、使用可能なすべてのテキストを表示したいと思います。つまりテキストフィールド自体にスクロールバーを使用しないでください...多くのWebページで発生するのとよく似ています。私も含めて、多くのユーザーは小さなウィンドウで編集を強いられることを好みません。 Facebookのステータス更新ボックスがどのように機能するか。

私はもう試した

_myTextArea.autoSize()
_

に包まれて

myTextArea.textProperty().addListener(new ChangeListener()....);

しかし、それは機能しません。現在のサイズに自動サイズ変更できて嬉しいと思います。

左、右、上のアンカーは、その親のAnchorPaneに設定されます。底を付けた状態と付けない状態で試してみました。理想的には、textareaが大きくなるにつれてアンカーペインを大きくしたいと思います。

TextPropertyを読んで、自分で設定したトリガーサイズを計算してもかまいません...しかし、すでにベストプラクティスがある場合、これはハッキーなアプローチのようです。 javafxのプロパティとサブオブジェクトの数は十分に気が遠くなるので、フォント/段落などが何ピクセルを占めているかを把握しようとするのではなく、ここで質問するのが良い点のようです。

更新:

だから私は多分それを考えすぎているのではないかと思いました、そして私がする必要があるのはスクロールバーをオフにすることだけでした、そして残りは起こるでしょう。残念ながら、「scroll」、「vertical」、「vbar」で使用可能なフィールドとメソッドを探していると、使用できるものが何も見つかりません。 ScrollTopPropertyは、他の何かのためのもののように見えます。

17
Daniel Gerson

問題; textAreaの高さは、ユーザーの入力またはコピー貼り付けによってテキストが変更されている間、拡大または縮小する必要があります。別のアプローチは次のとおりです。

_public class TextAreaDemo extends Application {

    private Text textHolder = new Text();
    private double oldHeight = 0;

    @Override
    public void start(Stage primaryStage) {
        final TextArea textArea = new TextArea();
        textArea.setPrefSize(200, 40);
        textArea.setWrapText(true);

        textHolder.textProperty().bind(textArea.textProperty());
        textHolder.layoutBoundsProperty().addListener(new ChangeListener<Bounds>() {
            @Override
            public void changed(ObservableValue<? extends Bounds> observable, Bounds oldValue, Bounds newValue) {
                if (oldHeight != newValue.getHeight()) {
                    System.out.println("newValue = " + newValue.getHeight());
                    oldHeight = newValue.getHeight();
                    textArea.setPrefHeight(textHolder.getLayoutBounds().getHeight() + 20); // +20 is for paddings
                }
            }
        });

        Group root = new Group(textArea);
        Scene scene = new Scene(root, 300, 250);
        primaryStage.setScene(scene);
        primaryStage.show();

        //  See the explanation below of the following line. 
        //  textHolder.setWrappingWidth(textArea.getWidth() - 10);  // -10 for left-right padding. Exact value can be obtained from caspian.css
    }

    public static void main(String[] args) {
        launch(args);
    }
}
_

ただし、欠点があります。 textareaの高さは、複数の行の間に改行(つまり、Enterキー)がある場合にのみ変更されます。ユーザーが十分な長さで入力すると、テキストは複数の行に折り返されますが、高さは変更されません。

この欠点を回避するために、この行を追加しました

_textHolder.setWrappingWidth(textArea.getWidth() - 10);
_

primaryStage.show();の後。これは、ユーザーが改行しない長い入力に適しています。ただし、これにより別の問題が発生します。この問題は、ユーザーが「バックスペース」を押してテキストを削除しているときに発生します。この問題は、textHolderの高さが変更され、textAreaの高さが新しい値に設定されたときに正確に発生します。 IMOはバグかもしれませんが、深く観察しませんでした。

どちらの場合も、コピー貼り付けは適切に処理されています。

13
Uluk Biy

より良いものを待って、私はこのハッキーなソリューションを使用します。

  • textareaの垂直スクロールバーを検索します。
  • 透明にする
  • その目に見える特性に耳を傾ける
  • スクロールバーが表示されたら、テキスト領域に行を追加します。

コード:

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.TextArea;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;

public class GrowGrowTextArea extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        AnchorPane root = new AnchorPane();
        root.setStyle("-fx-padding:20;-fx-background-color:dodgerblue;");
        final TextArea textArea = new TextArea();
        AnchorPane.setTopAnchor(textArea, 10.0);
        AnchorPane.setLeftAnchor(textArea, 10.0);
        AnchorPane.setRightAnchor(textArea, 10.0);
        root.getChildren().add(textArea);
        primaryStage.setScene(new Scene(root, 400, 300));
        primaryStage.show();
        ScrollBar scrollBar = lookupVerticalScrollBar(textArea);
        scrollBar.setOpacity(0.0);
        scrollBar.visibleProperty().addListener(new ChangeListener<Boolean>() {

            @Override
            public void changed(ObservableValue<? extends Boolean> source,
                                Boolean wasVisible,
                                Boolean isVisible) {
                if (isVisible) {
                    textArea.setPrefRowCount(textArea.getPrefRowCount() + 1);
                    textArea.requestLayout();
                }
            }
        });

    }

    private ScrollBar lookupVerticalScrollBar(Node node) {
        if (node instanceof ScrollBar && ((ScrollBar)node).getOrientation() == Orientation.VERTICAL) {
            return (ScrollBar) node;
        }
        if (node instanceof Parent) {
            ObservableList<Node> children = ((Parent) node).getChildrenUnmodifiable();
            for (Node child : children) {
                ScrollBar scrollBar = lookupVerticalScrollBar(child);
                if (scrollBar != null) {
                    return scrollBar;
                }
            }
        }
        return null;
    }
}
7
gontard

拡張TextAreaの作成でも同様の問題が発生しました。 TextFieldのように見え、行にスペースがなくなるたびに垂直方向に展開するTextAreaを作成していました。このトピックで見つけたすべてのソリューションを、スタックおよびその他の利用可能なソースでテストしました。良い解決策はほとんど見つかりませんでしたが、どちらも十分ではありませんでした。

何時間も戦った後、私はこのアプローチを理解しました。

TextAreaクラスを拡張し、layoutChildren()メソッドをオーバーライドして、テキストの高さにリスナーを追加しました。

  @Override
  protected void layoutChildren() {
    super.layoutChildren();
    setWrapText(true);
    addListenerToTextHeight();
  }

  private void addListenerToTextHeight() {
    ScrollPane scrollPane = (ScrollPane) lookup(".scroll-pane");
    scrollPane.setHbarPolicy(ScrollBarPolicy.NEVER);
    scrollPane.setVbarPolicy(ScrollBarPolicy.NEVER);

    StackPane viewport = (StackPane) scrollPane.lookup(".viewport");

    Region content = (Region) viewport.lookup(".content");

    Text text = (Text) content.lookup(".text");
    text.textProperty().addListener(textHeightListener(text));
  }

  private InvalidationListener textHeightListener(Text text) {
    return (property) -> {
      // + 1 for little margin
      double textHeight = text.getBoundsInLocal().getHeight() + 1;

      //To prevent that our TextArena will be smaller than our TextField
      //I used DEFAULT_HEIGHT = 18.0
      if (textHeight < DEFAULT_HEIGHT) {
        textHeight = DEFAULT_HEIGHT;
      }

      setMinHeight(textHeight);
      setPrefHeight(textHeight);
      setMaxHeight(textHeight);
    };
  }
2
Jakub Rybiński