so I have the following problem.
I have a JavaFX TableView, where all the Cells has the same styling but one. For that one cell I would like to remove all the styling, but I guess the RowFactory has some priority or whatever.
I have some kind of code like that:
FXML
<TableView fx:id="tableView">
<columns>
<TableColumn fx:id="tcolNoStyle"/>
<TableColumn fx:id="tcolStyled"/>
</columns>
</TableView>
Controller
public class TableController {
#FXML
TableView<TableData> tableView;
#FXML
TableColumn<TableData, String> tcolNoStyle;
#FXML
TableColumn<TableData, String> tcolStyled;
#FXML
public void initialize(){
tableView.setRowFactory(row -> new RowFactory());
tcolNoStyle.setCellFactory(cell -> new CellFactoryForNoStyling());
}
}
The Data behind the table
public class TableData {
public final SimpleStringProperty noStyleTextProperty;
public final SimpleStringProperty styledTextProperty;
public TableData(){
noStyleTextProperty = new SimpleStringProperty();
styledTextProperty = new SimpleStringProperty();
}
}
RowFactory
public class RowFactory extends TableRow<TableData> {
public RowFactory(){
itemProperty().addListener(((observable, oldValue, newValue) -> {
if (newValue != null){
setStyle("-fx-text-alignment: right");
}
}));
}
}
CellFactory that one no styled Cell
public class CellFactoryForNoStyling extends TableCell<TableData, String> {
public CellFactoryForNoStyling(){
super();
setStyle(null);
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setStyle(null);
}
}
So what I want is, that only that one column should have no style
Thank you in advance!
The problem here is the fact that the -fx-text-alignment property is inherited. setStyle(null) only makes sure there are no additional changes done for the node it's called for.
The probably simplest option would be to simply specify the default value using setStyle:
public CellFactoryForNoStyling() {
setStyle("-fx-text-alignment: left");
}
You could leave the styling to a CSS stylesheet though. This is much more convenient than using inline styles, since it's much easier this way to deal with selection, focus, ect.
You could e.g. add the following stylesheet to the scene; this way you do not to use a custom cellValueFactory/rowFactory:
/* default cell style */
.my-table .table-cell {
-fx-text-alignment: right;
}
/* overwrite above style using selector with higher specifity */
.my-table .table-cell.unstyled {
-fx-text-alignment: inherit;
}
<TableView fx:id="tableView" styleClass="my-table"> <!-- style class added here-->
<columns>
<TableColumn fx:id="tcolNoStyle" styleClass="unstyled"/> <!-- style class added here -->
<TableColumn fx:id="tcolStyled"/>
</columns>
</TableView>
Related
Enironment:
OpenJDK12, JavaFX 11
Context: I'm trying to show the Task progress to a TableView, for that, when my code was less complex, my Task object included the bean properties, and the TableView datamodel was my Task object.
public class MyTask extends Task<Void>{
private String name;
//other properties
public Void call() {
//"progress" property is inherited from Task.
//do something and updateProgress()
}
}
public class MyController {
...
#FXML
private TableView<MyTask> dataTable;
#FXML
private TableColumn<MyTask,Double> progressCol;
...
progressCol.setCellValueFactory(new PropertyValueFactory<MyTask, Double>("progress"));
progressCol.setCellFactory(ProgressCell.<Double>forTableColumn());
...
}
That worked fine. But I wanted to separate the Task from the bean properties, so I decided to make a kind of wrapper, but I'm unable to retrieve the progress property anymore.
EDIT
Sample Code:
MyApp
public class MyApp extends Application {
#Override
public void start(Stage stage) throws IOException {
stage.setMinWidth(800);
stage.setMinHeight(500);
FXMLLoader sceneLoader = new FXMLLoader(MyApp.class.getResource("MyScene.fxml"));
Parent parent = sceneLoader.load();
Scene scene = new Scene(parent);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
MyController
public class MyController implements Initializable{
#FXML
private TableView<MyWrapper> dataTable;
#FXML
private TableColumn<MyWrapper, String> nameColumn;
#FXML
private TableColumn<MyWrapper, Double> progressColumn;
public MyController() {
}
#Override
public void initialize(URL location, ResourceBundle resources) {
nameColumn.setCellValueFactory((TableColumn.CellDataFeatures<MyWrapper, String> download) -> download.getValue()
.getMyBean().nameProperty());
//This line only works when MyWrapper has progressPropery() method
//progressColumn.setCellValueFactory(new PropertyValueFactory<>("progress"));
progressColumn.setCellFactory(ProgressCell.<Double>forTableColumn());
MyWrapper w1 = new MyWrapper("qqqqqqq");
MyWrapper w2 = new MyWrapper("wwwwww");
MyWrapper w3 = new MyWrapper("eeeeeee");
ObservableList<MyWrapper> obsList = FXCollections.observableArrayList();
obsList.addAll(w1,w2,w3);
dataTable.setItems(obsList);
Thread t1 = new Thread(w1.getMyTask());
t1.start();
}
MyWrapper
public class MyWrapper {
private SimpleObjectProperty<MyBean> myBean;
private SimpleObjectProperty<MyTask> myTask;
public MyWrapper(String name) {
myBean = new SimpleObjectProperty<MyBean>();
myBean.setValue(new MyBean());
myBean.getValue().setName(name);
myTask = new SimpleObjectProperty<MyTask>();
myTask.setValue(new MyTask());
}
public MyBean getMyBean() {
return myBean.getValue();
}
public MyTask getMyTask() {
return myTask.getValue();
}
}
MyBean
public class MyBean {
private SimpleStringProperty name;
public MyBean() {
name = new SimpleStringProperty("--");
}
public SimpleStringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.setValue(name);
}
}
MyTask
public class MyTask extends Task<Void>{
#Override
protected Void call() throws Exception {
// Set the total number of steps in our process
double steps = 1000;
// Simulate a long running task
for (int i = 0; i < steps; i++) {
Thread.sleep(10); // Pause briefly
// Update our progress and message properties
updateProgress(i, steps);
updateMessage(String.valueOf(i));
} return null;
}
}
ProgressCell
public class ProgressCell extends TableCell<MyWrapper, Double> {
private ProgressBar bar;
private ObservableValue<Double> observable;
private StringProperty colorProperty = new SimpleStringProperty();
public ProgressCell() {
bar = new ProgressBar();
bar.setMaxWidth(Double.MAX_VALUE);
bar.setProgress(0f);
bar.styleProperty().bind(colorProperty);
}
public static <S> Callback<TableColumn<MyWrapper, Double>, TableCell<MyWrapper, Double>> forTableColumn() {
return param -> new ProgressCell();
}
#Override
protected void updateItem(Double item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setGraphic(null);
} else {
final TableColumn<MyWrapper, Double> column = getTableColumn();
observable = column == null ? null : column.getCellObservableValue(getIndex());
if (observable != null) {
bar.progressProperty().bind(observable);
} else if (item != null) {
bar.setProgress(item);
}
setGraphic(bar);
}
}
}
MyScene.fxml
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.effect.Blend?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.StackPane?>
<AnchorPane xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="main.java.MyController">
<StackPane BorderPane.alignment="CENTER">
<children>
<TableView id="dataTable" fx:id="dataTable" prefHeight="193.0" prefWidth="678.0" snapToPixel="false">
<columns>
<TableColumn fx:id="nameColumn" editable="false" prefWidth="88.0" text="Name" />
<TableColumn fx:id="progressColumn" editable="false" prefWidth="75.0" text="Progress" />
</columns>
<effect>
<Blend />
</effect>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
</children>
</StackPane>
</AnchorPane>
I don't know how to get the progress bar working, without adding the progressProperty() method in MyWrapper. I was expecting to access the progress property like the name property. Is there some way ? How do you think it would be better?
Any help appreciated.
There is no support for nested properties (as you noticed and I confirmed in a comment that mysteriously disappeared .. ) - providing the property in a custom cellValueFactory that walks down the tree is the way to go: just do the same for the progress of the task as you do for the name of the bean.
A working code snippet:
// column setup
nameColumn.setCellValueFactory(cc -> cc.getValue().getMyBean().nameProperty());
progressColumn.setCellValueFactory(cc -> cc.getValue().getMyTask().progressProperty().asObject());
progressColumn.setCellFactory(ProgressBarTableCell.forTableColumn());
new Thread(w1.getMyTask()).start();
Note the conversion of DoubleProperty to ObjectProperty<Double> (as Slaw noted in a comment that disappeared as well ;)
Whether or not such deep diving is a good idea depends on your context: it's okay as long as the data is read-only and doesn't change over its lifetime. Otherwise, you would need to take precautions to guard against such change. Which will require additonal logic in the wrapper anyway, so exposing the properties of interest in that layer probably would be the cleaner approach.
The first error is thrown because your MyObject class doesn't have a progressProperty function.
If you add this function to your wrapper class it will work.
public ReadOnlyDoubleProperty progressProperty() {
return task.progressProperty();
}
.
progressCol.setCellValueFactory(new PropertyValueFactory<>("progress"));
Here is a general structure of a JavaFX android mobile application I am creating.
Using (AppBar or AppBarSearch these interchange dynamically) as nested controllers within Primary Application FXML.
ParentController
- AppBarController
- AppBarSearchController
primary.fxml
- appBar.fxml / appBarSearch.fxml
<AnchorPane fx:id="appBarPane" prefHeight="56.0" prefWidth="350.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0">
<children>
<fx:include source="appbar.fxml" fx:id="appBar" />
<!-- initially shows AppBar but appBarSearch can also be here after clicking search button -->
</children>
</AnchorPane>
A button in each of the child fxml is responsible for changing between the fxml content from appBar/AppBarSearch.
The issue arises when I am dynamically change the content of the appBar to appBarSearch and back. I want the appBar menu button to communicate to the NavigationMenu to slide in and out.
I have been looking into whether I should somehow have an instance of the parentController inside the AppBarController.
I did use the following:
#FXML
private AppBarController appBarController; // injected via <fx:include fx:id="child" ... />
<fx:include source="appbar.fxml" fx:id="appBar" /> <- dynamically changes
//to dynamically change content in Panes
public static void setView(View view, Pane pane) {
try {
pane.getChildren().clear();
pane.getChildren().setAll((Node) FXMLLoader.load(Main.class.getResource(view.getTemplate()), resources));
} catch (IOException e) {
logger.error(e.getMessage());
}
}
#FXML
public void initialize() {
appBarController.setParentController(this);
}
#FXML
private void menuButtonClick (ActionEvent event) {
this.parentController.triggerMenu();
}
Initially the above works but after switching between appBarSearch and appBar it gives me a nullPointer to the parentController instance.
It may be that after switching between controllers dynamically it would not recognise the child controller.
I want the menuBtn in AppBar to open a navigationMenu so it would require to call triggerMenu() from within PrimaryController to start animations of it sliding in and out after the button in AppBar is clicked.
Thanks very much to the comments I was able to fix the issue and improve my understanding of how to use Controllers and included fxml.
Below is the general code I used to connect the child controllers with my primary controller and change views dynamically.
#FXML
private AppBarController appBarController; // injected via <fx:include fx:id="child" ... />
#FXML
private NavMenuController navMenuController; // injected via <fx:include fx:id="child" ... />
#FXML
private MainContentController mainContentController; // injected via <fx:include fx:id="child" ... />
#FXML
public void initialize() {
appBarController.setScreenParent(this);
navMenuController.setScreenParent(this);
mainContentController.setScreenParent(this);
}
/**
* Set View on a Pane Javafx component
*/
public <T extends Pane> boolean setView(View view, T pane) {
try {
FXMLLoader myLoader = new FXMLLoader(getClass().getResource(view.getTemplate()), resources);
pane.getChildren().clear();
pane.getChildren().setAll((Node) myLoader.load());
ControlledScreen childController = myLoader.getController();
childController.setScreenParent(this);
return true;
} catch (IOException e) {
logger.error(e.getMessage());
return false;
}
}
public void setMainContentView(View view) {
setView(view, mainContentPane);
}
public void setAppBarView(View view) {
setView(view, appBarPane);
}
public void setNavMenuView(View view) {
setView(view, navMenuPane);
}
public void triggerNavMenu() {
if (navMenuPane.getTranslateX() != 0) {
openNav.play();
appBarController.setMenuClosedImage();
} else {
closeNav.setToX(-(navMenuPane.getWidth()));
closeNav.play();
appBarController.setMenuClosedImage();
}
}
I've developing JavaFX custom button. Like this.
public class MyButton extends Control{
private static final String DEFAULT_STYLE_CLASS = "satrec-button";
private Button button;
public MyButton(){
getStyleClass().setAll(DEFAULT_STYLE_CLASS);
}
#Override
public String getUserAgentStylesheet(){
return "sample/css/button.css";
}
public void setButton(Button button){
this.button = button;
}
public void setText(String text){
this.button.setText(text);
}
}
I want to use this control in FXML. But original button and label can set text to use " test : " Like this
<Button text="Search" textFill="white" prefWidth="130" GridPane.columnIndex="4" GridPane.rowIndex="1" styleClass="control" fx:id="search_btn" />
But MyControl can not use this function. I looked up Labeled class. I know text is a StringProperty.
But I don't know what is this, and how can I use. How do I do?? Please, help me.
The default interpretation of an attribute or child property element of an instance FXML element is that it maps to a JavaFX property in the instance. (Essentially it is relying on the control using the JavaFX properties pattern.)
In other words, in your FXML snippet:
<MyButton text="Search" ... />
or, equivalently,
<MyButton>
<text>
<String fx:value="Search" />
</text>
</MyButton>
<MyButton> is an instance element, and it causes the FXMLLoader to create a new instance of the MyButton class, calling the default constructor by default. In either version, text (an attribute in the first version, a property element in the second) is going to cause the FXMLLoader to look for a textProperty() method, returning a WritableValue instance (for example a StringProperty). Assuming it finds one, it will call setValue(...) on that WritableValue, passing in the string "Search". (See the FXML documentation for a full description of all this terminology.)
So you just need your MyButton class to define an appropriate textProperty() method. Since you want this to be the text of the included button, you can just delegate to that button's method:
public class MyButton extends Control{
private static final String DEFAULT_STYLE_CLASS = "satrec-button";
private Button button;
public MyButton(){
getStyleClass().setAll(DEFAULT_STYLE_CLASS);
}
#Override
public String getUserAgentStylesheet(){
return "sample/css/button.css";
}
public void setButton(Button button){
this.button = button;
}
public StringProperty textProperty() {
if (button == null) { // might be better to write the class so that this is never true?
button = new Button();
}
return button.textProperty();
}
public final void setText(String text) {
textProperty().set(text);
}
public final String getText() {
return textProperty().get();
}
}
You can use the NamedArg Annotation:
public MyButton(#NamedArg("text") String text) {
this();
this.setText(text);
}
otherwise the fxml doesn't know which constructor argument you mean.
Now can use your Button in your fxml as you asked:
<MyButton text="Search" ....
This question already has answers here:
DatePicker in javafx TableCell
(4 answers)
Closed 5 years ago.
How can I add to the column of a javafx table view a datepicker for an inline edit?
<TableView fx:id="timelineTable" VBox.vgrow="ALWAYS">
<columns>
<TableColumn fx:id="dateColumn" prefWidth="85.0" text="%timeline.date" />
</columns>
</TableView>
You need to implement your custom TableCell where you need to #Override the updateItem method. This method should set the graphic to be a DatePicker.
Alternatively, you will implement an editable TableCell where you only set the
DatePicker as graphic when the cell is actually being edited.
Here is a piece of code that might be helpful to implement your idea:
Something more complete is here.
Callback<TableColumn<String, String>, TableCell<String, String>> cellFactory = new Callback<TableColumn<String, String>, TableCell<String, String>>() {
#Override
public TableCell call(final TableColumn<String, String> param) {
final TableCell<String, String> cell = new TableCell<String, String>() {
final DatePicker datePicker = new DatePicker();
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
setText(null);
} else {
setGraphic(datePicker);
setText(null);
}
}
};
return cell;
}
};
Maybe somebody knows the answer and try help me.
I am creating own button.
<fx:root maxHeight="100.0" maxWidth="100.0" minHeight="50.0" minWidth="50.0" prefHeight="80.0" prefWidth="80.0" style="-fx-background-color: red;" type="StackPane" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" >
<children>
<ImageView fx:id="baseImage" fitHeight="66.0" fitWidth="72.0" pickOnBounds="true" preserveRatio="true" StackPane.alignment="TOP_CENTER" />
<Label fx:id="textBtn" alignment="BOTTOM_LEFT" prefHeight="17.0" prefWidth="75.0" textFill="WHITE" textOverrun="CLIP" StackPane.alignment="BOTTOM_LEFT" />
</children>
</fx:root>
So I need to change my button (Image and Label), when I am creating this in FXML file.
<MyButton layoutX="200.0" layoutY="162.0" />
e.g
<MyButton layoutX="200.0" layoutY="162.0" image="" text="" />
Can somebody help me ?
My Java Code
public class MyButton extends StackPane
{
#FXML
private ImageView baseImage;
#FXML
private Label textBtn;
public MyButton()
{
FXMLLoader fxmlLoader =new FXMLLoader(getClass().getResource("/pl/edu/wat/wcy/pz/icons/MyButtonView.fxml"));
fxmlLoader.setController(this);
fxmlLoader.setRoot(this);
init();
try {
fxmlLoader.load();
}
catch (IOException exception) {
throw new RuntimeException(exception);
}
}
public Label getTextBtn() {
return textBtn;
}
public void setTextBtn(Label textBtn) {
this.textBtn = textBtn;
}
public ImageView getBaseImage() {
return baseImage;
}
public void setBaseImage(Image location) {
this.baseImage.setImage(location);
}
public void setButton(Label textBtn, Image location){
this.baseImage.setImage(location);
this.textBtn = textBtn;
}
But I care about icon are changed in FXML file, not JavaCode
}
If you want to set properties in FXML:
<MyButton layoutX="200.0" layoutY="162.0" image="" text="" />
you must define those properties in the corresponding class. In particular, MyButton must define setImage(...) and setText(...) methods (it already has setLayoutX(...) and setLayoutY(...) which are inherited from StackPane). It's hard to know exactly what functionality you want here, but you probably want to set these up as JavaFX Properties. If the intention is to map these into the Label and ImageView defined in the FXML file, you can just expose the relevant properties from the controls. You might need to work a bit to map the string from the image property into the correct thing.
public class MyButton extends StackPane
{
#FXML
private ImageView baseImage;
#FXML
private Label textBtn;
public MyButton()
{
FXMLLoader fxmlLoader =new FXMLLoader(getClass().getResource("/pl/edu/wat/wcy/pz/icons/MyButtonView.fxml"));
fxmlLoader.setController(this);
fxmlLoader.setRoot(this);
// not sure what this is:
// init();
// note that if you define
// public void initialize() {...}
// it will be called
// automatically during the FXMLLoader.load() method
try {
fxmlLoader.load();
}
catch (IOException exception) {
throw new RuntimeException(exception);
}
}
public StringPropergty textProperty() {
return textBtn.textProperty();
}
public final String getText() {
return textProperty().get();
}
public final void setText(String text) {
textProperty().set(text);
}
// similarly expose a property for image, but you need to be able to coerce it from a String
}
(Incidentally, I assume this is just an example for the purposes of understanding how to do this. Everything you have in the example can be done using a regular button. Just wanted to make that clear for any others reading this post.)