- objective-c - iOS 5 : Can you override UIAppearance customisations in specific classes?
- iphone - 如何将 CGFontRef 转换为 UIFont?
- ios - 以编程方式关闭标记的信息窗口 google maps iOS
- ios - Xcode 5 - 尝试验证存档时出现 "No application records were found"
一个 CSS 新手问题。
我在两个相邻的 TableView
中显示大量数据,并双向绑定(bind)它们的 ScrollBar
、FocusModel
和 SelectionModel
使它们保持同步。
我现在正在尝试让两个 TableView
看起来像一个,并且希望:
TableView
具有焦点时,两个 TableView
周围的默认蓝色边框。TableView
周围的默认灰色边框,当两者均未 具有焦点时。TableView
相交处没有边框。我该怎么做呢?
像这样的东西会很棒:
到目前为止,我已经能够通过这样做来移除“相遇”边界:
tvLeft.getStyleClass().add("my-table-view-left");
tvRight.getStyleClass().add("my-table-view-right");
像这样使用 CSS:
.my-table-view-left:focused {
-fx-background-insets: -1.4 0 -1.4 -1.4, -0.3 0 -0.3 -0.3, 1 0 1 1;
}
.my-table-view-right:focused {
-fx-background-insets: -1.4 -1.4 -1.4 0, -0.3 -0.3 -0.3 0, 1 1 1 0;
}
当其中一行被选中时,这也正确地设置了单个 TableView
的边框。
但是,当其中一个 具有焦点时,我不知道如何在两个 TableView
周围设置边框。
这是一个 MVCE。很抱歉它的长度,但我需要包含同步代码才能有一个测试用例。
我正在使用 11.0.2 版本的 OpenJDK 和 OpenJFX,在 Windows 7 上的 Netbeans 10.0 中运行。
MyTableViewCSS.css
/************************************************************************************************************
Trying to set the borders of the synchronised tableviews
*/
.my-table-view-left:focused {
-fx-background-insets: -1.4 0 -1.4 -1.4, -0.3 0 -0.3 -0.3, 1 0 1 1;
-fx-focus-color: red; /* for testing only */
}
.my-table-view-right:focused {
-fx-background-insets: -1.4 -1.4 -1.4 0, -0.3 -0.3 -0.3 0, 1 1 1 0;
-fx-focus-color: red; /* for testing only */
}
/************************************************************************************************************
The following section hides the horizontal and vertical tableview scrollbars.
They are replaced by scrollbars manually added to the form.
Source: https://stackoverflow.com/questions/26713162/javafx-disable-horizontal-scrollbar-of-tableview
*/
.my-table-view *.scroll-bar:horizontal *.increment-button,
.my-table-view *.scroll-bar:horizontal *.decrement-button {
-fx-background-color: null;
-fx-background-radius: 0;
-fx-background-insets: 0;
-fx-padding: 0;
}
.my-table-view *.scroll-bar:horizontal *.increment-arrow,
.my-table-view *.scroll-bar:horizontal *.decrement-arrow {
-fx-background-color: null;
-fx-background-radius: 0;
-fx-background-insets: 0;
-fx-padding: 0;
-fx-shape: null;
}
.my-table-view *.scroll-bar:vertical *.increment-button,
.my-table-view *.scroll-bar:vertical *.decrement-button {
-fx-background-color: null;
-fx-background-radius: 0;
-fx-background-insets: 0;
-fx-padding: 0;
}
.my-table-view *.scroll-bar:vertical *.increment-arrow,
.my-table-view *.scroll-bar:vertical *.decrement-arrow {
-fx-background-color: null;
-fx-background-radius: 0;
-fx-background-insets: 0;
-fx-padding: 0;
-fx-shape: null;
}
Test014.java
package test014;
import java.util.Arrays;
import java.util.function.Function;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.stage.Stage;
import javafx.util.converter.DefaultStringConverter;
public class Test014 extends Application {
private final ObservableList<DataModel> ol = FXCollections.observableArrayList();
private final TableView<DataModel> tvLeft = new TableView();
private final TableView<DataModel> tvRight = new TableView();
//Show a tableview that should continue to use the default Modena style. That way I'll know
//if I've messed anything up!
private final ObservableList<DataModel> olDefaultStyle = FXCollections.observableArrayList();
private final TableView<DataModel> tvDefaultStyle = new TableView();
private final ScrollBar vScroll = new ScrollBar();
private final ScrollBar hScroll = new ScrollBar();
private Parent createContent() {
loadDummyData();
createTableColumns();
tvLeft.setItems(ol);
tvRight.setItems(ol);
tvLeft.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
tvRight.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
//Bi-directionally bind the selection and focus models of the two tables.
tvLeft.selectionModelProperty().bindBidirectional(tvRight.selectionModelProperty());
tvLeft.focusModelProperty().bindBidirectional(tvRight.focusModelProperty());
tvLeft.getSelectionModel().selectFirst();
vScroll.setOrientation(Orientation.VERTICAL);
hScroll.setOrientation(Orientation.HORIZONTAL);
tvLeft.getStyleClass().add("my-table-view");
tvRight.getStyleClass().add("my-table-view");
tvLeft.getStyleClass().add("my-table-view-left");
tvRight.getStyleClass().add("my-table-view-right");
Platform.runLater(() -> {
Scene scene = tvLeft.getScene();
String appStyleSheet = "MyTableViewCSS.css";
scene.getStylesheets().add(this.getClass().getResource(appStyleSheet).toString());
//Do the bindings necesary to synchronise the tableviews
synchroniseTheTableViews();
});
//Load the tableviews in a gridpane so I can control the width of the left-hand tableview
GridPane gp = new GridPane();
ColumnConstraints cc1 = new ColumnConstraints();
cc1.setPrefWidth(130D);
cc1.setMaxWidth(130D);
cc1.setMinWidth(130D);
gp.getColumnConstraints().addAll(Arrays.asList(cc1));
gp.add(tvLeft, 0, 0);
gp.add(tvRight, 1, 0);
GridPane.setValignment(tvLeft, VPos.TOP);
GridPane.setVgrow(tvRight, Priority.ALWAYS);
//Put the gridpane in a borderpane so I can then add vScroll and hScroll
BorderPane content = new BorderPane();
gp.prefHeightProperty().bind(content.heightProperty());
gp.prefWidthProperty().bind(content.widthProperty());
content.setCenter(gp);
content.setRight(vScroll);
content.setBottom(hScroll);
//Add buttons to show and hide the tableview that should continue to have the default Modena style
Button btnShowTvDefaultStyle = new Button("Show TV with default style");
btnShowTvDefaultStyle.setOnAction(event -> {
content.setLeft(tvDefaultStyle);
});
Button btnHideTvDefaultStyle = new Button("Hide TV with default style");
btnHideTvDefaultStyle.setOnAction(event -> {
content.setLeft(null);
});
HBox hb = new HBox();
hb.setSpacing(20D);
hb.setPadding(new Insets(20D));
hb.setAlignment(Pos.CENTER);
hb.getChildren().addAll(Arrays.asList(btnShowTvDefaultStyle, btnHideTvDefaultStyle));
content.setTop(hb);
return content;
}
private void synchroniseTheTableViews() {
//Bind the first table's header row height to that of the second
Pane header1 = (Pane) tvLeft.lookup("TableHeaderRow");
Pane header2 = (Pane) tvRight.lookup("TableHeaderRow");
header1.prefHeightProperty().bind(header2.heightProperty());
//Now synchronise the scrollbars
ScrollBar scrollBarLeftTv;
ScrollBar scrollBarRightTv;
for ( Node node1: tvLeft.lookupAll(".scroll-bar") ) {
if ( node1 instanceof ScrollBar && ((ScrollBar) node1).getOrientation() == Orientation.VERTICAL ) {
scrollBarLeftTv = (ScrollBar) node1;
for ( Node node2: tvRight.lookupAll(".scroll-bar") ) {
if ( node2 instanceof ScrollBar && ((ScrollBar) node2).getOrientation() == Orientation.VERTICAL ) {
scrollBarRightTv = (ScrollBar) node2;
scrollBarRightTv.valueProperty().bindBidirectional(scrollBarLeftTv.valueProperty());
scrollBarRightTv.maxProperty().bindBidirectional(scrollBarLeftTv.maxProperty());
scrollBarRightTv.minProperty().bindBidirectional(scrollBarLeftTv.minProperty());
scrollBarRightTv.unitIncrementProperty().bindBidirectional(scrollBarLeftTv.unitIncrementProperty());
scrollBarRightTv.visibleAmountProperty().bindBidirectional(scrollBarLeftTv.visibleAmountProperty());
vScroll.valueProperty().bindBidirectional(scrollBarLeftTv.valueProperty());
vScroll.maxProperty().bindBidirectional(scrollBarLeftTv.maxProperty());
vScroll.minProperty().bindBidirectional(scrollBarLeftTv.minProperty());
vScroll.unitIncrementProperty().bindBidirectional(scrollBarLeftTv.unitIncrementProperty());
vScroll.visibleAmountProperty().bindBidirectional(scrollBarLeftTv.visibleAmountProperty());
}
}
}
}
for ( Node node: tvRight.lookupAll(".scroll-bar") ) {
if ( node instanceof ScrollBar && ((ScrollBar) node).getOrientation() == Orientation.HORIZONTAL ) {
ScrollBar scrollBar = (ScrollBar) node;
hScroll.valueProperty().bindBidirectional(scrollBar.valueProperty());
hScroll.maxProperty().bindBidirectional(scrollBar.maxProperty());
hScroll.minProperty().bindBidirectional(scrollBar.minProperty());
hScroll.unitIncrementProperty().bindBidirectional(scrollBar.unitIncrementProperty());
hScroll.visibleAmountProperty().bindBidirectional(scrollBar.visibleAmountProperty());
}
}
}
private void createTableColumns() {
for ( int i=0; i<2; i++ ) {
TableColumn<DataModel, String> col = createColumn(i, DataModel::field1Property);
tvLeft.getColumns().add(col);
}
for ( int i=0; i<12; i++ ) {
TableColumn<DataModel, String> col = createColumn(i, DataModel::field2Property);
tvRight.getColumns().add(col);
}
for ( int i=0; i<3; i++ ) {
TableColumn<DataModel, String> col = createColumn(i, DataModel::field1Property);
tvDefaultStyle.getColumns().add(col);
}
tvDefaultStyle.setItems(olDefaultStyle);
tvDefaultStyle.setPrefWidth(100D);
tvDefaultStyle.setMaxWidth(100D);
tvDefaultStyle.setMinWidth(100D);
}
private TableColumn<DataModel, String> createColumn (int colNum, Function<DataModel, StringProperty> property) {
TableColumn<DataModel,String> col = new TableColumn<>("field" + colNum);
col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
col.setCellFactory(TextFieldTableCell.<DataModel, String>forTableColumn(new DefaultStringConverter()));
return col;
}
private void loadDummyData() {
for ( int i=0; i<20; i++ ) {
ol.add(new DataModel(Integer.toString(i), "a"));
}
for ( int i=0; i<30; i++ ) {
olDefaultStyle.add(new DataModel(Integer.toString(i), "a"));
}
}
private class DataModel {
private final StringProperty field1;
private final StringProperty field2;
public DataModel(
String field1,
String field2
) {
this.field1 = new SimpleStringProperty(field1);
this.field2 = new SimpleStringProperty(field2);
}
public String getField1() {return field1.get().trim();}
public void setField1(String field1) {this.field1.set(field1);}
public StringProperty field1Property() {return field1;}
public String getField2() {return field2.get().trim();}
public void setField2(String field2) {this.field2.set(field2);}
public StringProperty field2Property() {return field2;}
}
@Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.setTitle("OpenJFX11 - Synchronise two TableViews");
stage.setWidth(700D);
stage.setHeight(400D);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
最佳答案
根据用户 kleopatra 的建议,我想出了如何使用 PseudoClass
es 来做到这一点。
对于每个同步的 TableView
,我将 CSS 定义为:
在除 TableView
“相遇”的边框外的所有边框上设置 Modena、蓝色、聚焦样式。
在 TableView
“相遇”的边框上设置 Modena、灰色、非聚焦样式。
然后我为每种边框样式声明了一个 PseudoClass
。
最后,我在每个TableView
的focusedProperty()
中添加了一个ChangeListener
来激活PseudoClass
两个 TableView
都在其中一个获得焦点时停用 PseudoClass
并且在两个都失去焦点时停用。
最终结果是这样的:
正如 kleopatra 正确指出的那样,这使用了不受支持的功能,但它对我有用。我也没有尝试使用可编辑的 TableView
,也没有尝试设置单元格(而不是行)选择。无论如何,我发布了代码片段和一个包含所有同步代码的 SSCE,以防它们帮助其他人。
CSS:
/* For the LEFT-hand tableview */
.my-table-view:left_focussed {
/* Apply the standard Modena focussed style to all bar the right-hand border (which is the
border that abuts the right-hand tableview) */
-fx-background-color: -fx-faint-focus-color, -fx-focus-color, -fx-control-inner-background;
-fx-background-insets: -1.4 0 -1.4 -1.4, -0.3 0 -0.3 -0.3, 1 0 1 1;
-fx-background-radius: 2, 0, 0;
/* Set the abutting right-hand border to grey. */
-fx-border-width: 0 0.7 0 0;
-fx-border-color: -fx-selection-bar-non-focused;
-fx-border-radius: 0;
}
/* For the RIGHT-hand tableview */
.my-table-view:right_focussed {
/* Apply the standard Modena focussed style to all bar the left-hand border (which is the
border that abuts the left-hand tableview) */
-fx-background-color: -fx-faint-focus-color, -fx-focus-color, -fx-control-inner-background;
-fx-background-insets: -1.4 -1.4 -1.4 0, -0.3 -0.3 -0.3 0, 1 1 1 0;
-fx-background-radius: 2, 0, 0;
/* Set the abutting left-hand border to grey. */
-fx-border-width: 0 0 0 0.7;
-fx-border-color: -fx-selection-bar-non-focused;
-fx-border-radius: 0;
}
PseudoClass
声明:
private final PseudoClass leftFocussed = PseudoClass.getPseudoClass("left_focussed");
private final PseudoClass rightFocussed = PseudoClass.getPseudoClass("right_focussed");
ChangeListener
:
tvLeft.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if (isNowFocused) {
togglePseudoClassStates(true);
} else {
togglePseudoClassStates(false);
}
});
tvRight.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if (isNowFocused) {
togglePseudoClassStates(true);
} else {
togglePseudoClassStates(false);
}
});
togglePseudoClassStates()
方法:
private void togglePseudoClassStates(boolean requiredState) {
//Set the tableview border style
tvLeft.pseudoClassStateChanged(leftFocussed, requiredState);
tvRight.pseudoClassStateChanged(rightFocussed, requiredState);
}
这是 SSCE。我使用 11.0.2 版本的 OpenJDK 和 OpenJFX,在 Windows 7 上的 Netbeans 10.0 中运行。
MyTableViewCSS.css
/************************************************************************************************************
This section defines the border style to be applied to the synchronised tableviews.
a) For the LEFT-hand tableview that represents the "frozen" columns:
i) Set all bar the RIGHT border to the default Modena focussed style.
ii) Set the RIGHT border to be the default Modena unfocussed style.
b) For the RIGHT-hand tableview that represents the "scrollable" columns:
i) Set all bar the LEFT border to the default Modena focussed style.
ii) Set the LEFT border to be the default Modena unfocussed style.
*/
/* For the LEFT-hand tableview */
.my-table-view:left_focussed {
/* Apply the standard Modena focussed style to all bar the right-hand border (which is the
border that abuts the right-hand tableview) */
-fx-background-color: -fx-faint-focus-color, -fx-focus-color, -fx-control-inner-background;
-fx-background-insets: -1.4 0 -1.4 -1.4, -0.3 0 -0.3 -0.3, 1 0 1 1;
-fx-background-radius: 2, 0, 0;
/* Set the abutting right-hand border to grey. */
-fx-border-width: 0 0.7 0 0;
-fx-border-color: -fx-selection-bar-non-focused;
-fx-border-radius: 0;
}
/* For the RIGHT-hand tableview */
.my-table-view:right_focussed {
/* Apply the standard Modena focussed style to all bar the left-hand border (which is the
border that abuts the left-hand tableview) */
-fx-background-color: -fx-faint-focus-color, -fx-focus-color, -fx-control-inner-background;
-fx-background-insets: -1.4 -1.4 -1.4 0, -0.3 -0.3 -0.3 0, 1 1 1 0;
-fx-background-radius: 2, 0, 0;
/* Set the abutting left-hand border to grey. */
-fx-border-width: 0 0 0 0.7;
-fx-border-color: -fx-selection-bar-non-focused;
-fx-border-radius: 0;
}
/************************************************************************************************************
This section defines the style of the selection bars for selected rows in the synchronised tableviews.
When a row in one tableview is selected or gets focus, the bi-directional bindings select and focus the
corresponding row in other tableview. To make it seem like the two tableviews are one, the default Modena
focussed style has to be set on the selection bars for both:
a) the actual selected/focussed row; and
b) the row in the other tableview that's automatically selected/focussed by the bi-directional bindings.
*/
.my-table-view:selectionbar_focussed > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled:selected,
.my-table-view:selectionbar_focussed > .virtual-flow > .clipped-container > .sheet > .table-row-cell .table-cell:selected
{
-fx-background: -fx-selection-bar;
-fx-table-cell-border-color: derive(-fx-selection-bar, 20%);
}
/************************************************************************************************************
This section section hides the default horizontal and vertical scrollbars in the synchronised tableviews.
*/
.my-table-view:hidden_scrollbars *.scroll-bar *.increment-arrow,
.my-table-view:hidden_scrollbars *.scroll-bar *.decrement-arrow,
.my-table-view:hidden_scrollbars *.scroll-bar *.increment-button,
.my-table-view:hidden_scrollbars *.scroll-bar *.decrement-button {
-fx-background-color: null;
-fx-background-radius: 0;
-fx-background-insets: 0;
-fx-padding: 0;
-fx-shape: null;
}
Test014.java
/*
An example of using two, synchronised TableViews to display a wide set of data where:
a) The left-hand TableView contains the dataset's fixed (or "frozen") columns.
b) The right-hand TableView contains the dataset's scrollable columns.
The TableViews are synchronised by bi-directionally binding their ScrollBars and selection and focus models.
The in-built ScrollBars on both TableViews are hidden with CSS. Simultaneous scrolling of both TableViews is
achieved by manually adding vertical and horizontal ScrollBars and bi-directionally binding them to the
now-hidden TableView ScrollBars.
To then make all the components look like a single TableView:
a) The two TableViews are added to a GridPane, which in turn is added as the centre node of a BorderPane.
b) The manually created vertical ScrollBar is added as the right node of the BorderPane.
c) The manually created horizontal ScrollBar is added as the bottom node of the BorderPane.
CSS is then used to:
a) Set the borders around both TableViews (except for the borders that abut each other) to the default Modena
blue style when either TableView has focus.
b) Set the borders around both TableViews (except for the abutting borders) to the default Modena grey style
when both TableViews lose focus.
c) Set the abutting borders permanently to grey.
d) Set the selection bars on both TableViews to the default Modena blue style when a row in either TableView
is selected or receives focus.
e) Set the selection bars on both TableViews to the default Modena grey style when both TableViews lose focus.
CAVEAT: This has not been tested with editable TableViews nor with cell (rather than row) selection enabled.
*/
package test014;
import java.util.Arrays;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.css.PseudoClass;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.stage.Stage;
import javafx.util.converter.DefaultStringConverter;
public class Test014 extends Application {
private final ObservableList<DataModel> ol = FXCollections.observableArrayList();
private final TableView<DataModel> tvLeft = new TableView();
private final TableView<DataModel> tvRight = new TableView();
//Show a tableview that should continue to use the default Modena style. That way I'll know
//if I've messed anything up!
private final ObservableList<DataModel> olDefaultStyle = FXCollections.observableArrayList();
private final TableView<DataModel> tvDefaultStyle = new TableView();
//Declare scrollbars that will be used to scroll the synchronised tableviews.
private final ScrollBar vScroll = new ScrollBar();
private final ScrollBar hScroll = new ScrollBar();
//Declare the pseudoclasses
private final PseudoClass leftFocussed = PseudoClass.getPseudoClass("left_focussed");
private final PseudoClass rightFocussed = PseudoClass.getPseudoClass("right_focussed");
private final PseudoClass selectionbarFocussed = PseudoClass.getPseudoClass("selectionbar_focussed");
private final PseudoClass hiddenScrollBars = PseudoClass.getPseudoClass("hidden_scrollbars");
private Parent createContent() {
loadDummyData();
createTableColumns();
createTvWithDefaultStyle();
tvLeft.setItems(ol);
tvRight.setItems(ol);
tvLeft.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
tvRight.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
//Bi-directionally bind the selection and focus models of the two tables.
tvLeft.selectionModelProperty().bindBidirectional(tvRight.selectionModelProperty());
tvLeft.focusModelProperty().bindBidirectional(tvRight.focusModelProperty());
tvLeft.getSelectionModel().selectFirst();
//Set the orientation of the scrollbars
vScroll.setOrientation(Orientation.VERTICAL);
hScroll.setOrientation(Orientation.HORIZONTAL);
//Add the "my-table-view" styleclass to both tableviews. This allows me to access the
//custom pseudoclasses needed to style the tableviews.
tvLeft.getStyleClass().add("my-table-view");
tvRight.getStyleClass().add("my-table-view");
//Activate the pseudoclass to hide the default scrollbars of the synchronised tableviews
tvLeft.pseudoClassStateChanged(hiddenScrollBars, true);
tvRight.pseudoClassStateChanged(hiddenScrollBars, true);
//Add listeners to detect and act on focus changes
addListeners();
Platform.runLater(() -> {
Scene scene = tvLeft.getScene();
String appStyleSheet = "MyTableViewCSS.css";
scene.getStylesheets().add(this.getClass().getResource(appStyleSheet).toString());
//Do the bindings necesary to synchronise the tableviews
synchroniseTheTableViews();
});
//Load the tableviews in a gridpane so I can control the width of the left-hand tableview
GridPane gp = new GridPane();
ColumnConstraints cc1 = new ColumnConstraints();
cc1.setPrefWidth(130D);
cc1.setMaxWidth(130D);
cc1.setMinWidth(130D);
gp.getColumnConstraints().addAll(Arrays.asList(cc1));
gp.add(tvLeft, 0, 0);
gp.add(tvRight, 1, 0);
GridPane.setValignment(tvLeft, VPos.TOP);
GridPane.setVgrow(tvRight, Priority.ALWAYS);
//Put the gridpane in a borderpane so I can then add vScroll and hScroll
BorderPane content = new BorderPane();
gp.prefHeightProperty().bind(content.heightProperty());
gp.prefWidthProperty().bind(content.widthProperty());
content.setCenter(gp);
content.setRight(vScroll);
content.setBottom(hScroll);
//Add buttons to show and hide the tableview that should continue to have the default Modena style
Button btnShowTvDefaultStyle = new Button("Show TV with default style");
btnShowTvDefaultStyle.setOnAction(event -> {
content.setLeft(tvDefaultStyle);
tvDefaultStyle.requestFocus();
});
Button btnHideTvDefaultStyle = new Button("Hide TV with default style");
btnHideTvDefaultStyle.setOnAction(event -> {
content.setLeft(null);
});
Button btnRequestFocus = new Button("request focus");
btnRequestFocus.setOnAction(event -> {
tvRight.requestFocus();
});
HBox hb = new HBox();
hb.setSpacing(20D);
hb.setPadding(new Insets(20D));
hb.setAlignment(Pos.CENTER);
hb.getChildren().addAll(Arrays.asList(btnShowTvDefaultStyle, btnHideTvDefaultStyle, btnRequestFocus));
content.setTop(hb);
return content;
}
private void addListeners() {
//Add a focus change listener to each of the tableviews to add or remove the border and
//selection bar styles when focus is gained or lost.
tvLeft.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if ( isNowFocused ) {
togglePseudoClassStates(true);
} else {
togglePseudoClassStates(false);
}
});
tvRight.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if ( isNowFocused ) {
togglePseudoClassStates(true);
} else {
togglePseudoClassStates(false);
}
});
//Add a value change listener to the manually added scrollbars to request focus on one of the
//synchronised tableviews.
hScroll.valueProperty().addListener((obs, oldValue, newValue) -> {
tvLeft.requestFocus();
});
vScroll.valueProperty().addListener((obs, oldValue, newValue) -> {
tvLeft.requestFocus();
});
//Likewise, add a handler to detect any mouse action on the scrollbars and request focus
//on one of the tableviews.
hScroll.setOnMouseReleased(event -> {
tvLeft.requestFocus();
});
vScroll.setOnMouseReleased(event -> {
tvLeft.requestFocus();
});
}
private void togglePseudoClassStates(boolean requiredState) {
//Set the tableview border style
tvLeft.pseudoClassStateChanged(leftFocussed, requiredState);
tvRight.pseudoClassStateChanged(rightFocussed, requiredState);
//Set the selection bar style
tvLeft.pseudoClassStateChanged(selectionbarFocussed, requiredState);
tvRight.pseudoClassStateChanged(selectionbarFocussed, requiredState);
}
private void synchroniseTheTableViews() {
//Bind the first table's header row height to that of the second
Pane header1 = (Pane) tvLeft.lookup("TableHeaderRow");
Pane header2 = (Pane) tvRight.lookup("TableHeaderRow");
header1.prefHeightProperty().bind(header2.heightProperty());
//Now synchronise the scrollbars
ScrollBar scrollBarLeftTv;
ScrollBar scrollBarRightTv;
for ( Node node1: tvLeft.lookupAll(".scroll-bar") ) {
if ( node1 instanceof ScrollBar && ((ScrollBar) node1).getOrientation() == Orientation.VERTICAL ) {
scrollBarLeftTv = (ScrollBar) node1;
for ( Node node2: tvRight.lookupAll(".scroll-bar") ) {
if ( node2 instanceof ScrollBar && ((ScrollBar) node2).getOrientation() == Orientation.VERTICAL ) {
scrollBarRightTv = (ScrollBar) node2;
scrollBarRightTv.valueProperty().bindBidirectional(scrollBarLeftTv.valueProperty());
scrollBarRightTv.maxProperty().bindBidirectional(scrollBarLeftTv.maxProperty());
scrollBarRightTv.minProperty().bindBidirectional(scrollBarLeftTv.minProperty());
scrollBarRightTv.unitIncrementProperty().bindBidirectional(scrollBarLeftTv.unitIncrementProperty());
scrollBarRightTv.visibleAmountProperty().bindBidirectional(scrollBarLeftTv.visibleAmountProperty());
vScroll.valueProperty().bindBidirectional(scrollBarLeftTv.valueProperty());
vScroll.maxProperty().bindBidirectional(scrollBarLeftTv.maxProperty());
vScroll.minProperty().bindBidirectional(scrollBarLeftTv.minProperty());
vScroll.unitIncrementProperty().bindBidirectional(scrollBarLeftTv.unitIncrementProperty());
vScroll.visibleAmountProperty().bindBidirectional(scrollBarLeftTv.visibleAmountProperty());
}
}
}
}
for ( Node node: tvRight.lookupAll(".scroll-bar") ) {
if ( node instanceof ScrollBar && ((ScrollBar) node).getOrientation() == Orientation.HORIZONTAL ) {
ScrollBar scrollBar = (ScrollBar) node;
hScroll.valueProperty().bindBidirectional(scrollBar.valueProperty());
hScroll.maxProperty().bindBidirectional(scrollBar.maxProperty());
hScroll.minProperty().bindBidirectional(scrollBar.minProperty());
hScroll.unitIncrementProperty().bindBidirectional(scrollBar.unitIncrementProperty());
hScroll.visibleAmountProperty().bindBidirectional(scrollBar.visibleAmountProperty());
}
}
}
private void createTableColumns() {
TableColumn<DataModel,String> col1 = new TableColumn<>("field1");
TableColumn<DataModel,String> col2 = new TableColumn<>("field2");
TableColumn<DataModel,String> col3 = new TableColumn<>("field3");
TableColumn<DataModel,String> col4 = new TableColumn<>("field4");
TableColumn<DataModel,String> col5 = new TableColumn<>("field5");
TableColumn<DataModel,String> col6 = new TableColumn<>("field6");
TableColumn<DataModel,String> col7 = new TableColumn<>("field7");
TableColumn<DataModel,String> col8 = new TableColumn<>("field8");
TableColumn<DataModel,String> col9 = new TableColumn<>("field9");
TableColumn<DataModel,String> col10 = new TableColumn<>("field10");
TableColumn<DataModel,String> col11 = new TableColumn<>("field11");
TableColumn<DataModel,String> col12 = new TableColumn<>("field12");
TableColumn<DataModel,?> colsGroup = new TableColumn<>("group of cols");
col1.setCellValueFactory(cellData -> cellData.getValue().field1Property());
col1.setCellFactory(TextFieldTableCell.<DataModel, String>forTableColumn(new DefaultStringConverter()));
col2.setCellValueFactory(cellData -> cellData.getValue().field2Property());
col2.setCellFactory(TextFieldTableCell.<DataModel, String>forTableColumn(new DefaultStringConverter()));
col3.setCellValueFactory(cellData -> cellData.getValue().field3Property());
col3.setCellFactory(TextFieldTableCell.<DataModel, String>forTableColumn(new DefaultStringConverter()));
col4.setCellValueFactory(cellData -> cellData.getValue().field4Property());
col4.setCellFactory(TextFieldTableCell.<DataModel, String>forTableColumn(new DefaultStringConverter()));
col5.setCellValueFactory(cellData -> cellData.getValue().field5Property());
col5.setCellFactory(TextFieldTableCell.<DataModel, String>forTableColumn(new DefaultStringConverter()));
col6.setCellValueFactory(cellData -> cellData.getValue().field4Property());
col6.setCellFactory(TextFieldTableCell.<DataModel, String>forTableColumn(new DefaultStringConverter()));
col7.setCellValueFactory(cellData -> cellData.getValue().field5Property());
col7.setCellFactory(TextFieldTableCell.<DataModel, String>forTableColumn(new DefaultStringConverter()));
col8.setCellValueFactory(cellData -> cellData.getValue().field4Property());
col8.setCellFactory(TextFieldTableCell.<DataModel, String>forTableColumn(new DefaultStringConverter()));
col9.setCellValueFactory(cellData -> cellData.getValue().field4Property());
col9.setCellFactory(TextFieldTableCell.<DataModel, String>forTableColumn(new DefaultStringConverter()));
col10.setCellValueFactory(cellData -> cellData.getValue().field4Property());
col10.setCellFactory(TextFieldTableCell.<DataModel, String>forTableColumn(new DefaultStringConverter()));
col11.setCellValueFactory(cellData -> cellData.getValue().field4Property());
col11.setCellFactory(TextFieldTableCell.<DataModel, String>forTableColumn(new DefaultStringConverter()));
col12.setCellValueFactory(cellData -> cellData.getValue().field4Property());
col12.setCellFactory(TextFieldTableCell.<DataModel, String>forTableColumn(new DefaultStringConverter()));
colsGroup.getColumns().addAll(Arrays.asList(col4, col5, col6, col7, col8));
tvLeft.getColumns().addAll(Arrays.asList(col1, col2));
tvRight.getColumns().addAll(Arrays.asList(col3, colsGroup, col9, col10, col11, col12));
}
private void createTvWithDefaultStyle() {
TableColumn<DataModel,String> colDefaultStyle1 = new TableColumn<>("f1");
TableColumn<DataModel,String> colDefaultStyle2 = new TableColumn<>("f2");
TableColumn<DataModel,String> colDefaultStyle3 = new TableColumn<>("f3");
colDefaultStyle1.setCellValueFactory(cellData -> cellData.getValue().field1Property());
colDefaultStyle1.setCellFactory(TextFieldTableCell.<DataModel, String>forTableColumn(new DefaultStringConverter()));
colDefaultStyle2.setCellValueFactory(cellData -> cellData.getValue().field2Property());
colDefaultStyle2.setCellFactory(TextFieldTableCell.<DataModel, String>forTableColumn(new DefaultStringConverter()));
colDefaultStyle3.setCellValueFactory(cellData -> cellData.getValue().field3Property());
colDefaultStyle3.setCellFactory(TextFieldTableCell.<DataModel, String>forTableColumn(new DefaultStringConverter()));
tvDefaultStyle.getColumns().addAll(Arrays.asList(colDefaultStyle1, colDefaultStyle2, colDefaultStyle3));
tvDefaultStyle.setItems(ol);
tvDefaultStyle.setPrefWidth(80D);
tvDefaultStyle.setMaxWidth(80D);
tvDefaultStyle.setMinWidth(80D);
}
private void loadDummyData() {
ol.add(new DataModel("1", "a", "1", "2", "z"));
ol.add(new DataModel("2", "a", "2", "3", "z"));
ol.add(new DataModel("3", "a", "3", "5", "z"));
ol.add(new DataModel("4", "a", "4", "9", "z"));
ol.add(new DataModel("5", "a", "5", "3", "z"));
ol.add(new DataModel("6", "a", "6", "4", "z"));
ol.add(new DataModel("7", "a", "7", "8", "z"));
ol.add(new DataModel("8", "a", "8", "7", "z"));
ol.add(new DataModel("9", "a", "9", "1", "z"));
ol.add(new DataModel("10", "a", "10", "6", "z"));
ol.add(new DataModel("11", "a", "11", "0", "z"));
ol.add(new DataModel("12", "a", "12", "3", "z"));
ol.add(new DataModel("13", "a", "13", "4", "z"));
ol.add(new DataModel("14", "a", "14", "2", "z"));
ol.add(new DataModel("15", "a", "15", "9", "z"));
ol.add(new DataModel("16", "a", "16", "4", "z"));
ol.add(new DataModel("17", "a", "17", "5", "z"));
ol.add(new DataModel("18", "a", "18", "0", "z"));
ol.add(new DataModel("19", "a", "19", "1", "z"));
ol.add(new DataModel("20", "a", "20", "6", "z"));
olDefaultStyle.add(new DataModel("A", "1", "20", "6", "z"));
olDefaultStyle.add(new DataModel("B", "2", "20", "6", "z"));
olDefaultStyle.add(new DataModel("C", "3", "20", "6", "z"));
}
private class DataModel {
private final StringProperty field1;
private final StringProperty field2;
private final StringProperty field3;
private final StringProperty field4;
private final StringProperty field5;
public DataModel(
String field1,
String field2,
String field3,
String field4,
String field5
) {
this.field1 = new SimpleStringProperty(field1);
this.field2 = new SimpleStringProperty(field2);
this.field3 = new SimpleStringProperty(field3);
this.field4 = new SimpleStringProperty(field4);
this.field5 = new SimpleStringProperty(field5);
}
public String getField1() {return field1.get().trim();}
public void setField1(String field1) {this.field1.set(field1);}
public StringProperty field1Property() {return field1;}
public String getField2() {return field2.get().trim();}
public void setField2(String field2) {this.field2.set(field2);}
public StringProperty field2Property() {return field2;}
public String getField3() {return field3.get().trim();}
public void setField3(String field3) {this.field3.set(field3);}
public StringProperty field3Property() {return field3;}
public String getField4() {return field4.get().trim();}
public void setField4(String field4) {this.field4.set(field4);}
public StringProperty field4Property() {return field4;}
public String getField5() {return field5.get().trim();}
public void setField5(String field5) {this.field5.set(field5);}
public StringProperty field5Property() {return field5;}
}
@Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.setTitle("OpenJFX11 - Synchronise two TableViews");
stage.setWidth(600D);
stage.setHeight(400D);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
关于css - 如何使用 CSS 设置两个相邻 TableView 的边框样式,使它们在 JavaFX 11 (OpenJFX 11) 中看起来像是一个 TableView,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55685496/
我的 JavaFX 程序准备并打印出一组 VBox。 这是 ModPrintCycle。这是提供打印选项的窗口 public PrintCycle data; //PrintCycle is
我正在使用 Linux 并尝试从 Oracle 的 JDK 切换到 OpenJDK 和 OpenJFX,但我在使用 JavaFX 应用程序时遇到了严重的问题。 以这个简单的程序为例: import j
我即将利用 Scala 和 JavaFX/OpenJFX 启动一个新的 GUI 项目。 上次我参与时,Java 8 是最先进的,JavaFX 与 JDK 和 JRE 集成,Scala 使用 JDK 8
我正在尝试安装最新版本的 BlueJ,它需要 JDK 11 和 OpenJFX。我现在已经从 Java 8 更新到 11,但我找不到实际安装 OpenJFX 的方法。我尝试检查 Open JFX 的版
我下载了存档 openjfx-11-ea+13_windows-x64_bin-sdk 和 OpenJDK。然后我创建一个 JavaFX 应用程序项目并在 Intellij IDEA (1.8.3)
我想使用特定版本的 openjfx,所以我使用命令下载它 sudo apt install openjfx=8u161-b12-1ubuntu2 但是 intellij idea 似乎找不到这个文件,
我正在尝试从 openjfx 源构建 JFX 场景生成器,但我找不到有效的操作方法或清晰的文档,我已经阅读了官方的 openjfx 说明,该说明不是针对场景生成器的,而是针对整个 sdk 的, 所以我
我安装了 OpenJDK 11 和 OpenJFX 11,并设法构建并运行了 JavaFX 应用程序。但是,我只能在命令行中添加 --module-path 和 --add-modules 参数后才能
我想知道openjfx是否合并到OpenJDK中,如果没有,我在哪里可以找到openjfx的ppa链接? 谢谢 最佳答案 OpenJFX 和 OpenJDK 是两个独立的项目,因此不会合并。 至于 P
我正在尝试在我的笔记本电脑上构建JavaFX sceneBuilder,我已经从 OpenJFx 项目下载了源代码并成功构建了 SDK,但是在场景构建器构建过程中我遇到了以下错误,我有点困惑,因为我完
我想为 Windows x86 arch 构建我的 JavaFX 应用程序。所以我下载了 Adopt OpenJdk 32-bit build 并用它来创建 Java Runtime Image。当我
我正在尝试在带有 openjdk 11 的 Netbeans 11 上使用 Maven 运行一个简单的 java fx 示例。我遵循了 OpenJfx tutorial直至将 JavaFx 创建为全局
我尝试在 official doc 之后创建一个 JavaFX maven 项目(使用 Maven 的 IntelliJ 章,非模块化项目)。我按照概述将 archtype artifact id 替
我尝试使用 OpenJFX 模块和 Maven 启动一个新项目。我关注的是:https://openjfx.io/openjfx-docs/ (转到 JavaFX with Maven -> Modu
我正在将我的项目转换为使用来自 Adoptopenjdk 的 openjdk 8...我知道 OpenJDK 不再携带 javafx。因此,我将 org.openjfx.javafx 设置为项目的依赖
我目前正在使用 OpenJDK 11 和 OpenJFX 构建应用程序。它编译得很好,可以启动,但是没有标题栏,如果我点击靠近应用程序的边缘,它会注册为点击它后面的任何窗口。 我正在使用 Intell
我正在阅读这个问题:Action Buttons css style in JavaFX ControlFX dialog , 以了解是否可以根据样式更改 JavaFX 警报对话框。 答案很好,但我想
SwingNode 在使用 Spring Boot、OpenJFX 和 Maven 的应用程序中导致链接错误 我通读了有关此问题的帖子,尝试按照他们的建议进行操作(请参阅下面的代码),但无法解决我的问
JavaFXpackager 似乎在 openjdk/openjfx 11 中不可用。 我想用它创建二进制 CSS (BSS)。 https://docs.oracle.com/javase/8/do
有谁知道是否有办法在 Eclipse 工作区中使用 JavaFX 模块?我有一个 OSGi-maven 多模块化应用程序。 到目前为止,我已经尝试了几件事。 下载 JavaFX SDK 并将 jar
我是一名优秀的程序员,十分优秀!