I've created a custom component with custom properties which I later import in the Scene Builder.
I'd like to have the option to disable one or more of those properties based on the value of another, how can I do that?
CustomControl:
public class CustomControl extends TextField {
private BooleanProperty active = new SimpleBooleanProperty();
private ObjectProperty<TypesEnum> selectType = new SimpleObjectProperty<TypesEnum>();
public CustomControl() {
super();
getStyleClass().add("custom-control");
active.addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (newValue) {
// Enable 'selectType'
}
else {
// Disable 'selectType'
}
}
});
}
public boolean isActive() {
return active.get();
}
public void setActive(boolean active) {
this.active.set(active);
}
public BooleanProperty activeProperty() {
return active;
}
public TypesEnum getSelectType() {
return selectType.get();
}
public void setSelectType(TypesEnum selectType) {
this.selectType.set(selectType);
}
public ObjectProperty<TypesEnum> selectTypeProperty() {
return selectType;
}
}
TypesEnum:
public enum TypesEnum {
TYPE_A,
TYPE_B,
TYPE_C
}
In the image below you can see an example of some disabled properties in Scene Builder (default HBox, with default Layout properties).
So when my active and selectType properties are shown in Scene Builder, when I click on active, I'd like selectType to become disable like in the image above.
Related
I'm designing a custom JavaFX node using FXML. This node fires a custom event.
And I would like to know how to add an event handler to the custom event in the FXML of the parent of this node.
I created my handler, passed it to the child as an object property and hooked it into the event system via the setEventHandler method. But it throws me an error when the event is fired.
Custom event code :
public class ValueUpdatedEvent extends Event {
public static final EventType<ActionEvent> VALUE =
new EventType<>(Event.ANY, "VALUE_UPDATED");
private float value;
public ValueUpdatedEvent() {
super(VALUE);
}
public float getValue() {
return value;
}
public void setValue(float value) {
this.value = value;
}
}
Child component controller :
public class CharacteristicBar extends Component {
#FXML
private JFXTextField field;
#FXML
private JFXProgressBar bar;
#FXML
private JFXButton plus;
#FXML
JFXButton minus;
private ObjectProperty<EventHandler<ValueUpdatedEvent>> onValueUpdated = new ObjectPropertyBase<EventHandler<ValueUpdatedEvent>>() {
#Override
public Object getBean() {
return CharacteristicBar.this;
}
#Override protected void invalidated() {
setEventHandler(new EventType<>(Event.ANY, "onValueUpdated"), get());
}
#Override
public String getName() {
return "onValueUpdated";
}
};
private SimpleFloatProperty value = new SimpleFloatProperty();
private boolean readonly = false;
public CharacteristicBar() {
super("CharacteristicBar.fxml");
value.addListener(
newValue -> {
ValueUpdatedEvent event = new ValueUpdatedEvent();
event.setValue(value.get());
fireEvent(event);
}
);
bar.progressProperty().bind(this.value);
if (this.readonly) {
this.field.setEditable(false);
this.minus.setVisible(false);
this.plus.setVisible(false);
}
}
#FXML
private void handleInput(KeyEvent event) {
try {
value.set(Float.parseFloat(field.getText()) / 20f);
} catch (NumberFormatException exception) {
field.setText("");
}
}
public float getValue() {
return value.get() * 20f;
}
#FXML
public void handleClickPlus(ActionEvent event) {
this.value.set((this.value.get() * 20f + 1f) / 20f);
this.field.setText(String.valueOf(this.value.get() * 20));
}
#FXML
public void handleClickMinus(ActionEvent event) {
this.value.set((this.value.get() * 20f - 1f) / 20f);
this.field.setText(String.valueOf(this.value.get() * 20));
}
public boolean isReadonly() {
return readonly;
}
public void setReadonly(boolean readonly) {
this.readonly = readonly;
this.field.setEditable(!readonly);
this.minus.setVisible(!readonly);
this.plus.setVisible(!readonly);
}
public EventHandler<ValueUpdatedEvent> getOnValueUpdated() {
return onValueUpdated.get();
}
public ObjectProperty<EventHandler<ValueUpdatedEvent>> onValueUpdatedProperty() {
return onValueUpdated;
}
public void setOnValueUpdated(EventHandler<ValueUpdatedEvent> onValueUpdated) {
this.onValueUpdated.set(onValueUpdated);
}
}
Parent's FXML :
<CharacteristicBar fx:id="courageBar" onBarValueChanged="#handleChangeCou"
GridPane.columnIndex="1" GridPane.rowIndex="7"/>
Handler in parent's controller:
#FXML
public void handleChangeCou(ValueUpdatedEvent event){
System.out.println(event.getValue());
}
Still, my event handler isn't called.
Do you guys have any clue on how to hook my handler with the event system ?
Thanks in advance
I could not get the custom event to work but instead I used properties to achieve my goal. Maybe it's intendend this way in JavaFX
When the user has reached the end of this Tetris game I want a new alert to be opened.
In the model class I have a boolean that will switch to true if a new Tetris block cannot be spawned.
I'm working with model view presenter, so in the model is the boolean + getter and in the presenter a new alert will be created if the boolean returns true.
The question is how do I add this to the eventHandlers() in the presenter?
public Presenter(Model model, View view) {
this.model = model;
this.view = view;
addEventHandlers();
}
private void addEventHandlers() {
//view.setOnKeyPressed... this is for rotating the blocks to give you an example
}
JavaFX implements observable properties, which are extensions of the Java Bean pattern that support notification for invalidation and for changes to the underlying value. These are fundamental to the JavaFX library: all controls in JavaFX make use of these. So, for example, if you want to respond to changes to the text in a text field, you would do
myTextField.textProperty().addListener((observable, oldText, newText) -> {
// ... do something with newText (and perhaps oldText) here...
});
So you can just achieve this with a BooleanProperty (or similar) in your model class:
private final BooleanProperty gameEnded = new SimpleBooleanProperty();
public BooleanProperty gameEndedProperty() {
return gameEnded ;
}
public final boolean isGameEnded() {
return gameEndedProperty().get();
}
public final void setGameEnded(boolean gameEnded) {
gameEndedProperty().set(gameEnded);
}
Then you can do:
model.gameEndedProperty().addListener((obs, gameWasEnded, gameIsNowEnded) -> {
if (gameIsNowEnded) {
// show alert, etc...
}
});
See "Properties and Bindings" in the Oracle tutorial for more details, including bindings, etc. You might also consider a ReadOnlyBooleanWrapper if you don't want the property to be changed from outside the class it is defined in.
public void setEind() {
boolean oldValue = this.eind;
eind = true;
System.out.println(eind);
firePropertyChange("eind",oldValue,eind);
}
private final List<PropertyChangeListener> listeners = new ArrayList<>();
public void addPropertyChangeListener(PropertyChangeListener listener) {
listeners.add(listener);
}
public void firePropertyChange(String property, Object oldValue, Object newValue) {
for (PropertyChangeListener listener : listeners) {
listener.propertyChange(new PropertyChangeEvent(this,property,oldValue,newValue));
}
}
spel.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
val.stop();
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setHeaderText("Game over!");
alert.setContentText("Enter your name in the next window for the highscores.");
alert.setTitle("End");
alert.show();
}
});
I want to develop a custom textarea in javafx. The first property I want to define is the styleable property missing in the textarea class which is the textFill property so I can programmatically set the color of the text in the textarea. This is important as the textarea is gonna be used in a program where on a click of a button, the color of the text has to be changed.
The problem is that the property value does not affect the text in the textarea. I crossed checked with the javafx api classes and had found that I had followed all the procedure needed to define a styleable property and it's cssMetaData.
Here is the source code of the custom textarea:
public class CustomTextArea extends TextArea {
private ObjectProperty<Paint> textFill;
public void setTextFill(Paint value){
textFillProperty().setValue(value);
}
public ObjectProperty<Paint> textFillProperty(){
if(textFill == null){
textFill = new StyleableObjectProperty<Paint>(Color.BLUE) {
#Override
public Object getBean() {
return CustomTextArea.this;
}
#Override
public String getName() {
return "textFill";
}
#Override
public CssMetaData<? extends Styleable, Paint> getCssMetaData() {
return TEXT_FILL;
}
};
}
return textFill;
}
private static final CssMetaData<CustomTextArea, Paint> TEXT_FILL =
new CssMetaData<CustomTextArea, Paint>("-fx-text-fill", PaintConverter.getInstance(), Color.BLUE) {
#Override
public boolean isSettable(CustomTextArea styleable) {
return styleable.textFill == null || !styleable.textFill.isBound();
}
#Override
public StyleableProperty<Paint> getStyleableProperty(CustomTextArea styleable) {
return (StyleableProperty<Paint>)(WritableValue<Paint>)styleable.textFillProperty();
}
};
private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
static{
final List<CssMetaData<? extends Styleable, ?>> styleables =
new ArrayList<CssMetaData<? extends Styleable, ?>>(Control.getClassCssMetaData());
Collections.addAll(styleables, TEXT_FILL);
STYLEABLES = Collections.unmodifiableList(styleables);
}
public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData(){
return STYLEABLES;
}
#Override
public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData(){
return getClassCssMetaData();
}
}
Here is the main class:
public class CustomTextAreaTest extends Application {
public void start(Stage primaryStage){
AnchorPane pane = new AnchorPane();
CustomTextArea area = new CustomTextArea();
area.setText("Custom text area test");
area.setLayoutX(10);
area.setLayoutY(10);
pane.getChildren().add(area);
Scene scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.sizeToScene();
primaryStage.show();
}
public static void main(String[] args){
launch(args);
}
}
Please can you help me identify where I am making a mistake or missing out something. Thanks in advance and please ask for clarification if needed.
When I use two radio buttons with the same name then they are in a group. If one gets selected the other one gets unselected.
I want to build my own Radio Button Widget which is represented by the following code.
How can I achieve that if more than one of my widgets have the same name only one is selected just like for normal radio buttons that are grouped?
public class MyRadioButton extends Composite implements HasText, HasName, HasValueChangeHandlers<Boolean>, HasValue<Boolean> {
private FlowPanel picker;
private boolean isChecked;
public MyRadioButton() {
picker = new FlowPanel();
initWidget(picker);
addValueChangeHandler(new ValueChangeHandler<Boolean>() {
#Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
ValueChangeEvent.fire(MyRadioButton.this, isChecked);
}
});
}
#Override
public void setValue(Boolean value, boolean fireEvents) {
...
if (fireEvents) {
ValueChangeEvent.fire(MyRadioButton.this, value);
}
}
}
Do you solved your problem? If not this could help
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.RadioButton;
public class MyRadioButton extends Composite implements
DeselectOtherRadioButtonsHandler {
private RadioButton rb;
private String radioButtonGroup;
public MyRadioButton(String radioButtonGroup) {
rb = new RadioButton("Foo Button");
getEventBus().addHandler(DeselectOtherRadioButtonsEvent.getType(), this);
rb.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
#Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
if (event.getValue().booleanValue()) {
// deselect other in same group
getEventBus().fireEvent(new DeselectOtherRadioButtonsEvent(radioButtonGroup);
}
}
});
}
#Override
public void onDeselectOtherRadioButtons(DeselectOtherRadioButtonsEvent event) {
// Same RadioButton Group?
if (radioButtonGroup.equalsIgnoreCase(event.getRadioButtonGroup())) {
rb.setValue(false);
}
}
}
class DeselectOtherRadioButtonsEvent extends
GwtEvent<DeselectOtherRadioButtonsHandler> {
private final static Type<DeselectOtherRadioButtonsHandler> TYPE = new Type<DeselectOtherRadioButtonsHandler>();
private final String radioButtonGroup;
public DeselectOtherRadioButtonsEvent(String radioButtonGroup) {
this.radioButtonGroup = radioButtonGroup;
}
public String getRadioButtonGroup() {
return radioButtonGroup;
}
#Override
public Type<DeselectOtherRadioButtonsHandler> getAssociatedType() {
return TYPE;
}
#Override
protected void dispatch(DeselectOtherRadioButtonsHandler handler) {
handler.onDeselectOtherRadioButtons(this);
}
}
interface DeselectOtherRadioButtonsHandler extends EventHandler {
void onDeselectOtherRadioButtons(DeselectOtherRadioButtonsEvent event);
}
have a look the following working code:
class MyType{
SimpleStringProperty myname;
SimpleObjectProperty<Color> mycolor;
}
TableColumn col;
arr = FXCollections.observableArrayList(new ArrayList<MyType>());
tblColName.setCellValueFactory(new PropertyValueFactory("myname"));
// Use the cell-factory provided by TextFieldTableCell.
tblColName.setCellFactory(TextFieldTableCell.forTableColumn());
tblColName.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent>() {
#Override
public void handle(TableColumn.CellEditEvent cellEditEvent) {
((MyType) cellEditEvent.getRowValue()).myname.set((String) cellEditEvent.getNewValue());
}
});
However, as soon as I am using a custom TableCell, the code in setOnEditCommit is not called anymore:
public class ColorPickerTableCell<S> extends TableCell<S, Color>{
private ColorPicker cp;
public ColorPickerTableCell(){
cp = new ColorPicker(Color.BLACK);
cp.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
commitEdit(cp.getValue());
updateItem(cp.getValue(), isEmpty());
}
});
setGraphic(cp);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setEditable(true);
}
#Override
protected void updateItem(Color item, boolean empty) {
super.updateItem(item, empty);
cp.setVisible(!empty);
this.setItem(item);
}
public static <T> Callback<TableColumn<Color, T>, TableCell<Color, T>> forTableColumn(){
return new Callback<TableColumn<Color, T>, TableCell<Color, T>>() {
#Override
public TableCell<Color, T> call(TableColumn<Color, T> colorTTableColumn) {
return new ColorPickerTableCell();
}
};
}
}
A slight change of the code above...
TableColumn col;
arr = FXCollections.observableArrayList(new ArrayList<MyType>());
tblColName.setCellValueFactory(new PropertyValueFactory("myname"));
// Use the cell-factory provided by TextFieldTableCell.
tblColName.setCellFactory(ColorPickerTableCell.forTableColumn());
tblColName.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent>() {
#Override
public void handle(TableColumn.CellEditEvent cellEditEvent) {
throw new NotImplementedException(); // is never thrown.
}
});
... makes the code not work anymore. The exception is never thrown. I think that I am doing something wrong in the design of ColorPickerTableCell, but I cannot imagine what. How can I make JavaFX call my OnEditCommit ?
You need first to go to edit state with statEdit(); , if you now commit an event will be fired
I've recently had the same problem. Unfortunately, I haven't found any way of trigerring that event from the ColorPicker control. However, I came up with the following workaround.
First of all, I created a Color wrapper class:
public class ColorWrapper {
private Color color;
....
}
I replaced the Color instance with the wrapper instance in my model class M. Next, I implemented setCellFactory method in the following way:
myColumn.setCellFactory(new Callback<TableColumn<M, ColorWrapper>, TableCell<M, ColorWrapper>>() {
#Override
public TableCell<M, ColorWrapper> call(TableColumn<M,ColorWrapper> arg0) {
return new TableCell<M, ColorWrapper>(){
private ColorPicker colorPicker;
private ColorPicker createPicker(){
colorPicker = new ColorPicker();
colorPicker.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent evt) {
ColorPicker cp = (ColorPicker)evt.getSource();
ColorWrapper cw = (ColorWrapper)cp.getUserData();
cw.setColor(cp.getValue());
}
});
return colorPicker;
}
#Override
protected void updateItem(ColorWrapper value, boolean empty) {
super.updateItem(value, empty);
if(empty){
return;
}
if(colorPicker == null){
colorPicker = createPicker();
colorPicker.setUserData(value);
}
colorPicker.setValue(value.getColor());
setGraphic(colorPicker);
}
};
}
});
As you can see, I just made use of setUserData/getUserData methods of the ColorPicker class and that's it. It works.
I had the same problem for CheckBoxTableCell and DatePickerTableCell and ColorPickerTableCells :-(
I deal it like that: on the events of the controls I get back the POJO objects in use by the "((Inputs)getTableView().getItems().get(getTableRow().getIndex()" and I update similary like is it done in the OnEditCommit method...
So for me it's look like this (update the color):
((Inputs) getTableView().getItems().get(
getTableRow().getIndex())
).setColor(cp.getValue());
Here is example with ColorPickerCell
:
public class ColorPickerTableCell<Inputs> extends TableCell<Inputs, Color>{
private ColorPicker cp;
public ColorPickerTableCell(){
cp = new ColorPicker();
cp.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
commitEdit(cp.getValue());
updateItem(cp.getValue(), isEmpty());
((Inputs) getTableView().getItems().get(
getTableRow().getIndex())
).setColor(cp.getValue());
}
});
setGraphic(cp);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setEditable(true);
}
#Override
protected void updateItem(Color item, boolean empty) {
super.updateItem(item, empty);
cp.setVisible(!empty);
this.setItem(item);
cp.setValue(item);
}
}
With this simple JavaFX's POJO:
public ObjectProperty<Color> color = new SimpleObjectProperty<Color>();
this.color = new SimpleObjectProperty(color);
public ObjectProperty<Color> colorProperty() {
return color;
}
public void setColor(Color color2) {
color.set(color2);
}
I do not know if it's a good way to achive that but it worked for me... Note that the JavaFX's POJO is only accessible within an "ActionEvent" request (combobox, datepicker, colorpicker, etc..)
Regards,
To elaborate on justcode's answer, here is my class where I had the problem and solved it:
public class DeleteButtonCell extends TableCell<Menu, Menu> {
private Button deleteButton;
public DeleteButtonCell() {
deleteButton = new Button();
deleteButton.setId("trash-button");
deleteButton.setOnAction((e) -> {
startEdit();
commitEdit((Menu) this.getTableView().getItems().get(this.getIndex()));
});
}
#Override
protected void updateItem(Menu t, boolean empty) {
super.updateItem(t, empty);
if (empty) {
setGraphic(null);
} else {
setGraphic(deleteButton);
}
}
}