Clip an HBox inside a GridPane

喜你入骨 提交于 2019-12-12 14:07:08

问题


I have a JavaFX 2 GUI which includes an HBox filling one of the cells of a GridPane. All of my components are dynamically sized. This displays fine, no problems.

First question: I'd like to clip the contents of the HBox at the edge of the GridPane cell they're in, so that none of the overflow is displayed, but I can't work out how. I've tried setting the clip for my HBox to a rectangle of the same size but this doesn't work. I guess because it's using the dimensions used for layout, rather than dimensions of what's displayed. Should I instead be clipping on the GridPane itself, or at least using its properties?

Second question (for a bonus point): how does clipping interact with translation and scaling of the Node?

Thanks for any help.


回答1:


I tried a couple of short program with attempts to clip the Nodes within a GridPane. None of the clip attempt implementations I really liked all that well, though most of them worked in some fashion or other depending on requirements.

The clip attempt which most closely seemed to fit your description is the last which wraps the clipped node in a region with it's own size specifiers, so that layout of the clipped region matches the size of the clip node applied to the node.

class ClippedNode extends Region {
  private final Node content;
  private final double width;
  private final double height;

  ClippedNode(Node content, double width, double height) {
    this.content = content;
    this.width   = width;
    this.height  = height;

    content.setClip(new Rectangle(width, height));

    getChildren().setAll(content);
  }

  @Override protected double computeMinWidth(double d)   { return width; }
  @Override protected double computeMinHeight(double d)  { return height; }
  @Override protected double computePrefWidth(double d)  { return width; }
  @Override protected double computePrefHeight(double d) { return height; }
  @Override protected double computeMaxWidth(double d)   { return width; }
  @Override protected double computeMaxHeight(double d)  { return height; }
}

A full executable sample demonstrating various clip approaches is provided below:

import java.util.Iterator;
import javafx.application.Application;
import javafx.beans.value.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class GridClipping extends Application {
  String buttonText[] = "The quick brown fox jumped over the lazy dog".split(" ");
  String[] colors = { "aqua", "coral", "cornsilk", "cornflowerblue" };

  @Override public void start(Stage stage) throws Exception {
    final GridPane grid = new GridPane();
    grid.addRow(0, createHBox("aqua"),     createHBox("coral"));
    grid.addRow(1, createHBox("cornsilk"), createHBox("cornflowerblue"));
    grid.setStyle("-fx-border-color: red;");

    final GridPane gridWithClippedBoxes = new GridPane();
    gridWithClippedBoxes.addRow(0, createClipWrappedHBox("aqua"),     createClipWrappedHBox("coral"));
    gridWithClippedBoxes.addRow(1, createClipWrappedHBox("cornsilk"), createClipWrappedHBox("cornflowerblue"));
    gridWithClippedBoxes.setStyle("-fx-border-color: red;");

    final RadioButton noClip            = new RadioButton("No Clip");
    final RadioButton restrictGridSize  = new RadioButton("Restrict Max Grid Size (doesn't work)");
    final RadioButton clipGrid          = new RadioButton("Clip Grid");
    final RadioButton clipHBoxes        = new RadioButton("Clip HBoxes");
    final RadioButton clipWrappedHBoxes = new RadioButton("Clip Wrapped HBoxes");

    final ToggleGroup clipRadios       = new ToggleGroup();
    clipRadios.getToggles().setAll(
     noClip, 
     restrictGridSize, 
     clipGrid, 
     clipHBoxes,
     clipWrappedHBoxes
    );

    final Rectangle gridClip = new Rectangle(0, 0, 100, 25);

    clipGrid.selectedProperty().addListener(new ChangeListener<Boolean>() {
      @Override public void changed(ObservableValue<? extends Boolean> ov, Boolean wasClipped, Boolean clipped) {
        if (clipped != null) {
          if (clipped) {
            grid.setClip(gridClip);
          } else {
            grid.setClip(null);
          }
        } 
      }
    });

    restrictGridSize.selectedProperty().addListener(new ChangeListener<Boolean>() {
      @Override public void changed(ObservableValue<? extends Boolean> ov, Boolean wasClipped, Boolean clipped) {
        if (clipped != null) {
          if (clipped) {
            // this does not work in our case.
            // the minimum size of the grid components > the max size of the grid.
            // so the grid expands in size to fit the minimum size of it's components anyway.
            grid.setMaxSize(100, 25); 
          } else {
            grid.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
          }
        } 
      }
    });

    clipHBoxes.selectedProperty().addListener(new ChangeListener<Boolean>() {
      @Override public void changed(ObservableValue<? extends Boolean> ov, Boolean wasClipped, Boolean clipped) {
        if (clipped != null) {
          if (clipped) {
            for (Node gridCell: grid.getChildren()) {
              Rectangle cellClip = new Rectangle(100, 12);
              gridCell.setClip(cellClip);
            }
          } else {
            for (Node gridCell: grid.getChildren()) {
              gridCell.setClip(null);
            }
          }
        } 
      }
    });

    final VBox layout = new VBox(10);
    clipWrappedHBoxes.selectedProperty().addListener(new ChangeListener<Boolean>() {
      @Override public void changed(ObservableValue<? extends Boolean> ov, Boolean wasClipped, Boolean clipped) {
        if (clipped != null) {
          if (clipped) {
            layout.getChildren().set(0, gridWithClippedBoxes);
          } else {
            layout.getChildren().set(0, grid);
          }
        } 
      }
    });

    noClip.fire();

    layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10;");
    layout.getChildren().setAll(
      grid,
      noClip,
      restrictGridSize,
      clipGrid,
      clipHBoxes,
      clipWrappedHBoxes
    );

    stage.setScene(new Scene(layout));
    stage.show();
  }

  private Region createHBox(String webColor) {
    HBox box = new HBox(5);
    box.setStyle("-fx-background-color: " + webColor + ";");
    for (String text: buttonText) {
      box.getChildren().add(new Text(text));
    }
    box.setOpacity(0.5);

    return box;
  }    

  private Region createClipWrappedHBox(String webColor) {
    return new ClippedNode(createHBox(webColor), 100, 12);
  }

  class ClippedNode extends Region {
    private final Node content;
    private final double width;
    private final double height;

    ClippedNode(Node content, double width, double height) {
      this.content = content;
      this.width   = width;
      this.height  = height;

      content.setClip(new Rectangle(width, height));

      getChildren().setAll(content);
    }

    @Override protected double computeMinWidth(double d)   { return width; }
    @Override protected double computeMinHeight(double d)  { return height; }
    @Override protected double computePrefWidth(double d)  { return width; }
    @Override protected double computePrefHeight(double d) { return height; }
    @Override protected double computeMaxWidth(double d)   { return width; }
    @Override protected double computeMaxHeight(double d)  { return height; }
  }

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


来源:https://stackoverflow.com/questions/15476063/clip-an-hbox-inside-a-gridpane

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!