javafx cannot access button in custom listcell - java

The core of the problem is that I cannot refresh or change the content of a node of a scene (here TablesMain) from another class (here NamePriceCell).
I am building and application with a main StackPane (TableMainController extends StackPane) that contains other nodes, some of which are ListViews. In a specific ListView (say 'readitemslistview') I have created a custom ListCell (public class NamePriceCell extends ListCell) and this NamePriceCell listcell contains a button (plusbtn). When a user clicks on the plusbtn, an arraylist (say 'chosenitemslist') gets filled up with the item displayed in the particular cell and at the same time another listview in the stackpane (say 'ordereditemslist') should be triggered to display the content of the 'chosenitemslist' arraylist.
I haven't found a way to reference the plusbtn of the NamePriceCell from the main controller. Moreover this post
Get ListCell via ListView
Unfortunately right now there is no API to get List Cell by index or
to get All children's(listcells) for ListView.
has turned me away from such an attempt (I haven't been able to understand if the solution provided there is suitable for my needs, as I don't want a refresh of the same listview but from another in the same scene).
So the only way I have found to get a mouse click action from the plusbtn is with an EventHandler in the same class (NamePriceCell). And even though I can get an arraylist filled according to the clicks on the NamePriceCell buttons (the NamePriceCell calls a static method in another class with a static arraylist in it) and I can get the result in my orderedlistview by letting the user click on another button node of the TableMain StackPane, I cannot find a way to do that when the user clicks on the NamePriceCell button.
Among the things I tried is setting a static method in the TablesMainController that I can access it from the NamePriceCell EventHandler (but then I have to make the holder node of the ordereditemslist static, and even so, it hasn't worked),
writing a separate fxml for the VBox containing the ordereditemslist with a corresponding separate controller so that I can have a method there to create the ordereditemslist and set the cellFactory (but even though the fxml file displays correctly and calling the method from the EventHandler of NamePriceCell works - a System.out gets displayed - it won't affect anything on the screen not the ordereditemslist, not even a label I tested.
I am relatively a java newbie and even so more when it comes to javafx. Your help is greatly needed and appreciated.
The code of my last approach is below:
VboxList.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.layout.VBox?>
<VBox>
<Label text="Label" id="label1"/>
<Label text="Label" id="label2"/>
<Label text="sdfsdf" id="label3"/>
<ListView nodeOrientation="LEFT_TO_RIGHT" id="orderedlist" prefHeight="200.0" prefWidth="200.0" />
</VBox>
VboxListController.java
public class VboxListController extends VBox{
FXMLLoader fxmlLoader;
#FXML
private Label label1;
#FXML
private Label label2;
#FXML
private ListView<OrderItem> orderedlist;
public VboxListController() {
fxmlLoader = new FXMLLoader(getClass().getResource("fxml/VboxList.fxml"));
//fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
public void getorderedlist(ArrayList<OrderItem> chosenitems){
ObservableList<OrderItem> myObservableList = FXCollections.observableList(chosenitems);
this.getChildren().remove(orderedlist);
orderedlist = new ListView<>();
orderedlist.setItems(myObservableList);
orderedlist.setCellFactory(new Callback<ListView<OrderItem>, ListCell<OrderItem>>() {
#Override
public ListCell<OrderItem> call(ListView<OrderItem> p) {
ListCell<OrderItem> cell = new ListCell<OrderItem>() {
#Override
protected void updateItem(OrderItem dataobj, boolean bln) {
super.updateItem(dataobj, bln);
if (dataobj != null) {
setText(dataobj.read_item_name);
}
}
};
return cell;
}
});
this.getChildren().add(orderedlist);
orderedlist.setItems(null);
orderedlist.setItems(myObservableList);
label1 = null;
this.getChildren().remove(label1);
label1 = new Label();
label1.setText("oooops!");
System.out.println("sdf");
this.getChildren().add(label1);
}
}
NamePriceCell.java
public class NamePriceCell extends ListCell<ReadItemChoiceObj> {
int count=0;
#FXML
Label namelbl;
#FXML
Label pricelbl;
#FXML
Button plusbtn;
#FXML
Region spacer;
#FXML
HBox cellHbox;
FXMLLoader mLLoader;
ReadItemChoiceObj readitem;
ArrayList<ReadItemChoiceObj> chosenreaditems;
#Override
protected void updateItem(ReadItemChoiceObj readitem, boolean empty) {
super.updateItem(readitem, empty);
chosenreaditems = new ArrayList<>();
if(empty || readitem == null) {
setText(null);
setGraphic(null);
} else {
this.readitem = readitem;
if (mLLoader == null) {
mLLoader = new FXMLLoader(getClass().getResource("fxml/NamePriceCell.fxml"));
mLLoader.setController(this);
try {
mLLoader.load();
} catch (IOException e) {
e.printStackTrace();
}
}
namelbl.setText(readitem.getname());
namelbl.setMaxWidth(500);
pricelbl.setText(String.format("%.2f", readitem.getprice()));
pricelbl.setStyle("-fx-font: 8pt \"Arial\";");
pricelbl.setMaxWidth(40);
spacer.setMaxWidth(10);
spacer.setMinWidth(10);
plusbtn.setMaxWidth(20);
cellHbox.setHgrow(namelbl, Priority.ALWAYS);
cellHbox.setAlignment(Pos.BASELINE_LEFT);
setText(null);
setGraphic(cellHbox);
plusbtn.setOnMouseClicked(whattodohandler);
}
}
EventHandler<MouseEvent> whattodohandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
ChosenItemsStat.getplusbtnclicked(readitem);
ChosenItemsStat chos = new ChosenItemsStat();
count+=1;
plusbtn.setText(String.valueOf(count));
new VboxListController().getorderedlist(chos.sendchosenlist());
}
};
}
and a part of TablesMain.java
#FXML
Label label1;
#FXML
BorderPane borderpane;
#FXML
Pane tablepane;
#FXML
ListView<DataObj> tabcatlist;
#FXML
VBox VboxList;
#FXML
VboxListController vboxListController;
/* #FXML
ListView<OrderItem> orderedlist;*/
#FXML
VBox leftVbox;
public TablesMain(Order order) {
this.order = order;
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("fxml/tablesmain.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
ObservableList<DataObj> myObservableList = FXCollections.observableList(gettable_categories());
tabcatlist.setItems(myObservableList);
tabcatlist.setCellFactory(new Callback<ListView<DataObj>, ListCell<DataObj>>() {
#Override
public ListCell<DataObj> call(ListView<DataObj> p) {
ListCell<DataObj> cell = new ListCell<DataObj>() {
#Override
protected void updateItem(DataObj dataobj, boolean bln) {
super.updateItem(dataobj, bln);
if (dataobj != null) {
setText(dataobj.getname());
}
}
};
return cell;
}
});
if (table_categoryID == 0) table_categoryID = tablecategories.get(0).getid();
gettables(table_categoryID);
}
private void make_readitemlist(int itemcategoryID) {
readitems = new ArrayList<>();
.... the readitems arraylist gets filled up...
ObservableList<ReadItemChoiceObj> myObservableList = FXCollections.observableList(readitems);
readitemlist.setItems(myObservableList);
readitemlist.setCellFactory(new Callback<ListView<ReadItemChoiceObj>,
ListCell<ReadItemChoiceObj>>() {
#Override
public ListCell<ReadItemChoiceObj> call(ListView<ReadItemChoiceObj> list) {
readitemlistcell = new NamePriceCell();
return readitemlistcell;
}
}
);
readitemlist.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
ReadItemChoiceObj dataobj = (ReadItemChoiceObj) readitemlist.getSelectionModel().getSelectedItem();
System.out.println(String.valueOf(dataobj.getid()));
// showorderedlist(new ChosenItemsStat().sendchosenlist());
}
});
System.out.println(readitemlist.cellFactoryProperty().getClass().toString());
}
...
}

Since it's been almost 5 months without another contribution, i would like to post my solution as an answer. It's not mine but inspired (or copied) by the post mentioned in my question (or another one, now I don't remember, please excuse my possible misplacement of credits).
More precisely I created a static StringProperty in TableMain file (the main Controller class). Every time the plusbtn (NamePriceCell class that extends ListCell) is clicked a random number turned to string is forced in the static StringProperty of the TableMain controller class. Inside this class the StringProperty has a ChangeListener added which in its turn triggers (from inside of the main controller now - that was the clue) the refreshing of the orderedlist (the listview that had to be refreshed with the added items).
NamePriceCell.java
public class NamePriceCell extends ListCell<ReadItemChoiceObj> {
#FXML
Label namelbl;
#FXML
Label pricelbl;
#FXML
Button plusbtn;
#FXML
Region spacer;
#FXML
HBox cellHbox;
FXMLLoader mLLoader;
ReadItemChoiceObj readitem;
#Override
protected void updateItem(ReadItemChoiceObj readitem, boolean empty) {
super.updateItem(readitem, empty);
if(empty || readitem == null) {
setText(null);
setGraphic(null);
} else {
this.readitem = readitem;
if (mLLoader == null) {
mLLoader = new FXMLLoader(getClass().getResource("fxml/NamePriceCell.fxml"));
mLLoader.setController(this);
try {
mLLoader.load();
} catch (IOException e) {
e.printStackTrace();
}
}
namelbl.setText(readitem.getname());
namelbl.setMaxWidth(500);
pricelbl.setText(String.format("%.2f", readitem.getprice()));
pricelbl.setStyle("-fx-font: 8pt \"Arial\";");
pricelbl.setMaxWidth(40);
spacer.setMaxWidth(10);
spacer.setMinWidth(10);
plusbtn.setMaxWidth(20);
cellHbox.setHgrow(namelbl, Priority.ALWAYS);
cellHbox.setAlignment(Pos.BASELINE_LEFT);
setText(null);
setGraphic(cellHbox);
plusbtn.setOnMouseClicked(whattodohandler);
}
}
EventHandler<MouseEvent> whattodohandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
OrderItem orderitem = new OrderItem();
orderitem.read_item_name = readitem.getname();
orderitem.read_item_price=readitem.getprice();
orderitem.read_itemID=readitem.getid();
orderitem.choice_name_list = new ArrayList<String>();
orderitem.choice_price_list = new ArrayList<Float>();
orderitem.choice_id_list = new ArrayList<Integer>();
ChosenItemsStat.getplusbtnclicked(orderitem);
Random rand = new Random();
TablesMain.stringProperty.setValue(String.valueOf(rand));
}
};
}
ChosenItemsStat.java (a static class receiving the additions to the list)
public class ChosenItemsStat {
static ArrayList<OrderItem> chosenorderitemsstat = new ArrayList<>();
static void getplusbtnclicked(OrderItem orderitem){
chosenorderitemsstat.add(orderitem);
}
}
I didn't use the VboxList.fxml or the VboxListController.java as I included this in the TablesMain.java (see below)
TablesMain.java (as in the question but with this addition)
stringProperty = new SimpleStringProperty();
stringProperty.setValue("");
tablelbl.setText(stringProperty.getValue());
stringProperty.addListener(new ChangeListener(){
#Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
showselectedlist();
}
});
while the showselectedlist() that is being called by the change of the StringProperty value is the one below (in this method the cell is being contructed as in the removed class (VboxListController)
private void showselectedlist(){
orderitems.addAll(ChosenItemsStat.chosenorderitemsstat);
ChosenItemsStat.chosenorderitemsstat.clear();
ListView<OrderItem> selectedlist = new ListView<>();
ObservableList<OrderItem> myObservableList = FXCollections.observableList(orderitems);
selectedlist.setItems(myObservableList);
selectedlist.setMaxWidth(220);
selectedlist.setCellFactory(new Callback<ListView<OrderItem>, ListCell<OrderItem>>() {
#Override
public ListCell<OrderItem> call(ListView<OrderItem> p) {
ListCell<OrderItem> cell = new ListCell<OrderItem>(){
OrderedCell ordcell= new OrderedCell();
#Override
protected void updateItem(OrderItem orderitem, boolean bln) {
super.updateItem(orderitem, bln);
if (orderitem != null) {
Float price = ordcell.getitemTempPrice(orderitem,orderitem.read_item_price);
HBox namepriceHbox = new HBox();
Label lblprice= new Label(String.format("%.2f",price));
Region betweenregion = new Region();
Label lblname = new Label();
lblname.setText(orderitem.read_item_name );
lblname.setStyle("-fx-font: 10pt \"Arial\";-fx-font-weight:bold");
namepriceHbox.setHgrow(betweenregion, Priority.ALWAYS);
namepriceHbox.getChildren().addAll(lblname,betweenregion,lblprice);
VBox allVbox = new VBox();
Text lblchoices = new Text();
String choices = ordcell.choicestostring(orderitem);
lblchoices.setText(choices);
lblchoices.setWrappingWidth(listpane.getLayoutBounds().getWidth());
if (choices.equals("")) allVbox.getChildren().addAll(namepriceHbox);
else allVbox.getChildren().addAll(namepriceHbox, lblchoices);
double namepricewidth = listpane.getLayoutBounds().getWidth();
System.out.println("namepricewidth is "+String.valueOf(namepricewidth));
//allVbox.setPadding(new Insets(10,0,10,0));
setGraphic(allVbox);
}
}
};
return cell;
}
});
listpane.getChildren().add(selectedlist);
}

Related

CallBack method implementation needed

I have a root screen which generates a popup and in popup I have a listview with button on it and I want to update the texfield in root screen and close the popupwindow when a button is clicked in popup. Code for popup and its controller.
POPUP
public void display() throws IOException {
Stage window =new Stage();
FXMLLoader loader=new FXMLLoader();
Parent root = loader.load(getClass().getResource("/ProfilePopup.fxml"));
window.setTitle("Your profile");
window.setScene(new Scene(root, 400, 500));
window.show();
}
PopUPController
public void initialize() {
listView.setEditable(true);
listView.setVisible(true);
listView.setItems(walletModel.myWallets);
listView.setCellFactory(param -> {
try {
return new EditableCell();
} catch (IOException e) {
e.printStackTrace();
}
return null;
});
listView.layout();
addWalletButton.setOnMouseClicked(event -> {
walletModel.createWallet();
listView.getFixedCellSize();
size.setText("Total Wallets: " + walletModel.walletSize());
});
if (walletModel.myWallets.size() == 0) {
walletModel.initializeWalletData();
walletModel.myWallets.add(walletModel.initializeWalletData());
}
size.setText("Wallet Size " + walletModel.walletSize());
}
static class EditableCell extends ListCell<WalletModel.WalletData> {
private final WalletCellController controller;
EditableCell() throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/selectButton.fxml"));
Node graphic = loader.load();
controller = loader.getController();
setGraphic(graphic);
}
#Override
protected void updateItem(WalletModel.WalletData item, boolean empty) {
if (empty) {
controller.rootView.setVisible(false);
} else {
controller.textField.setText(item.getName());
controller.rootView.setVisible(true);
}
}
}
}
I want the button on listview to update the root screen when its clicked and plus close the popup as well. Each listview is getting graphics from walletcellcontroller code below.
Here is how I am calling from the root screen.
Creating instance in root screen and then calling
(Popup popup=new Popup();)
public void popupOpen() throws IOException {
popup.display();
}
here is the code for listview item
public class WalletCellController implements OnClick {
public Button select;
public TextField textField;
public AnchorPane rootView;
public void initialize(){
onMouseClicked();
}
public void onMouseClicked() {
select.setOnAction(closeEvent -> {
Node source = (Node) closeEvent.getSource();
Stage stage = (Stage) source.getScene().getWindow();
stage.close();
});
}}
Can you tell me how to use the callbacks for actionevents here. I think I need call back from POPUP Controller to POPup and then from POPup to root screen.
I am new in java so I not sure about the implementation of it.
interface Callable {
public void callBackMethod();
}
class Worker {
// Worker gets a handle to the boss object via the Callable interface.
// There's no way this worker class can call any other method other than
// the one in Callable.
public void doSomeWork(Callable myBoss) {
myBoss.callBackMethod();
// ERROR!
//myBoss.directMethod();
}
}
class Boss implements Callable {
public Boss() {
// Boss creates a worker object, and tells it to do some work.
Worker w1 = new Worker();
// Notice, we're passing a reference of the boss to the worker.
w1.doSomeWork(this);
}
//developer that develop library just call controll the place of calling
public void callBackMethod() {
System.out.println("What do you want?");
}
public void directMethod() {
System.out.println("I'm out for coffee.");
}
}
public class Main {
public static void main(String[] args) {
Boss b = new Boss();
b.directMethod();
// write your code here
}
}
this is sample code of call back method
In cases like these I recommend using Dialog, since it allows you to query & wait for user input.
Example
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Dialog?>
<?import javafx.scene.control.DialogPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.VBox?>
<Dialog xmlns:fx="http://javafx.com/fxml/1" fx:id="dialog" fx:controller="fxml.DialogController">
<dialogPane>
<DialogPane headerText="Choose item!">
<content>
<VBox prefWidth="100" spacing="5">
<children>
<Button text="a" onAction="#choice" maxWidth="Infinity" />
<Button text="b" onAction="#choice" maxWidth="Infinity" />
<Button text="c" onAction="#choice" maxWidth="Infinity" />
<Button text="Cancel" onAction="#cancel" maxWidth="Infinity" />
</children>
</VBox>
</content>
</DialogPane>
</dialogPane>
</Dialog>
public class DialogController {
#FXML
private Dialog<String> dialog;
#FXML
private void choice(ActionEvent event) {
Button source = (Button) event.getSource();
dialog.setResult(source.getText());
dialog.close();
}
#FXML
private void cancel() {
dialog.setResult("");
dialog.close();
}
}
#Override
public void start(Stage primaryStage) {
TextField textField = new TextField();
Button btn = new Button("Choose");
btn.setOnAction((ActionEvent event) -> {
Dialog<String> dialog;
try {
dialog = FXMLLoader.load(getClass().getResource("/fxml/Dialog.fxml"));
} catch (IOException ex) {
throw new IllegalStateException(ex);
}
Optional<String> result = dialog.showAndWait();
if (!result.orElse("").isEmpty()) {
textField.setText(s);
}
});
VBox root = new VBox(textField, btn);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}

How to know the row of a dynamic created button of a tableview on javafx

I'm currently working on javafx and I have a serious problem. I can't find a way to get the index of the row of a button that I created dynamically on a table view.
If someone could help me out, that would be very helpful.
this.clmColumn.setCellFactory((TableColumn<?, ?> column) -> {
return new TableCell<?, ?>() {
#Override
protected void updateItem(? item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
final HBox hbox = new HBox(5);
final VBox vbox = new VBox(5);
Label label = new Label(item.toString());
final Button btnMais = new Button("+");
btnMais.setMinSize(25, 25);
final TableCell<?, ?> c = this;
btnMais.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
// At this point i want to select the current ROW of the button that i pressed on the tableview.
}
});
final Button btnMenos = new Button("-");
btnMenos.setMinSize(25, 25);
btnMenos.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
if (getItem() > 1) {
// At this point i want to select the current ROW of the button that i pressed on the tableview.
}
}
});
final Button btnRemover = new Button("Remover");
btnRemover.setFont(new Font(8));
btnRemover.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
// At this point i want to select the current ROW of the button that i pressed on the tableview.
}
});
vbox.getChildren().add(hbox);
vbox.getChildren().add(btnRemover);
hbox.getChildren().add(btnMais);
hbox.getChildren().add(label);
hbox.getChildren().add(btnMenos);
hbox.setAlignment(Pos.CENTER);
vbox.setAlignment(Pos.CENTER);
setGraphic(vbox);
} else {
setGraphic(null);
}
}
};
});
In the handle() method you can do
Object row = getTableView.getItems().get(getIndex());
You can replace Object with a more specific type if you use more specific types throughout in the type parameters.

Implement tags bar in JavaFX

Demonstration of answer:(answered May 29 at 3:10 am)
**10/7/2016** you can find the code on GitHub
Actual Question before answered:(asked May 22 at 19:53)
The title might be not too great but what I want to do is something like this in JavaFX:
Examples
YouTube:
StackOverFlow(which has and autocomplete):
Question:
I don't require to write me the code for that. Instead I want to know how I can achieve that using JavaFX and some ideas.
For the tags you can use a custom styled HBox containing a Text (the tag name) node an a Button (the deletion button (X)). By playing around with the background and the border you can achieve the desired look of the tags.
The onAction handler of the button should remove the tag from it's parent...
For the whole tag bar you can use another HBox. Use the appropriate border for the correct look. In addition to the tags add a TextField with no background as last element and set the Hgrow property of that TextField to Priotity.ALWAYS to cover the rest of the available space.
The onAction handler of this TextField adds new tags and clears the content of the TextField.
You could e.g. use ControlsFX's autocompletion features with the TextField or implement it on your own for a custom look...
public class TagBar extends HBox {
private final ObservableList<String> tags;
private final TextField inputTextField;
public ObservableList<String> getTags() {
return tags;
}
public TagBar() {
getStyleClass().setAll("tag-bar");
getStylesheets().add(getClass().getResource("style.css").toExternalForm());
tags = FXCollections.observableArrayList();
inputTextField = new TextField();
inputTextField.setOnAction(evt -> {
String text = inputTextField.getText();
if (!text.isEmpty() && !tags.contains(text)) {
tags.add(text);
inputTextField.clear();
}
});
inputTextField.prefHeightProperty().bind(this.heightProperty());
HBox.setHgrow(inputTextField, Priority.ALWAYS);
inputTextField.setBackground(null);
tags.addListener((ListChangeListener.Change<? extends String> change) -> {
while (change.next()) {
if (change.wasPermutated()) {
ArrayList<Node> newSublist = new ArrayList<>(change.getTo() - change.getFrom());
for (int i = change.getFrom(), end = change.getTo(); i < end; i++) {
newSublist.add(null);
}
for (int i = change.getFrom(), end = change.getTo(); i < end; i++) {
newSublist.set(change.getPermutation(i), getChildren().get(i));
}
getChildren().subList(change.getFrom(), change.getTo()).clear();
getChildren().addAll(change.getFrom(), newSublist);
} else {
if (change.wasRemoved()) {
getChildren().subList(change.getFrom(), change.getFrom() + change.getRemovedSize()).clear();
}
if (change.wasAdded()) {
getChildren().addAll(change.getFrom(), change.getAddedSubList().stream().map(Tag::new).collect(Collectors.toList()));
}
}
}
});
getChildren().add(inputTextField);
}
private class Tag extends HBox {
public Tag(String tag) {
getStyleClass().setAll("tag");
Button removeButton = new Button("X");
removeButton.setOnAction((evt) -> tags.remove(tag));
Text text = new Text(tag);
HBox.setMargin(text, new Insets(0, 0, 0, 5));
getChildren().addAll(text, removeButton);
}
}
}
style.css
.tag-bar {
-fx-border-color: blue;
-fx-spacing: 3;
-fx-padding: 3;
-fx-max-height: 30;
}
.tag-bar .tag {
-fx-background-color: lightblue;
-fx-alignment: center;
}
.tag-bar .tag .button {
-fx-background-color: transparent;
}
#Override
public void start(Stage primaryStage) {
Button btn = new Button("Sort");
StackPane.setAlignment(btn, Pos.BOTTOM_CENTER);
TagBar tagBar = new TagBar();
btn.setOnAction((ActionEvent event) -> {
FXCollections.sort(tagBar.getTags());
});
Button btn2 = new Button("add \"42\"");
btn2.setOnAction(evt -> {
if (!tagBar.getTags().contains("42")) {
tagBar.getTags().add("42");
}
});
VBox root = new VBox();
root.getChildren().addAll(tagBar, btn, btn2);
root.setPrefSize(300, 400);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
Simple implementation of this code!
import ....
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
BorderPane root = new BorderPane();
HBox tagsPane = new HBox(10);
tagsPane.setStyle("-fx-border-color: #F1F1F1;" +
" -fx-border-width: 1px;" +
" -fx-border-radius: 10;" +
" -fx-border-insets: 5");
root.setBottom(tagsPane);
TextField textField = new TextField();
textField.setPromptText("Tag name - ENTER to add");
textField.setOnKeyPressed(event -> {
if (event.getCode() == KeyCode.ENTER) {
tagButton(tagsPane, textField.getText());
textField.clear();
}
});
root.setTop(textField);
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 450, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
//little image as 15x15 for example
Image toUse = new Image("sample/delete.png");
//box is the pane where this buttons will be placed
public void tagButton(HBox box,String tag){
ImageView closeImg = new ImageView(toUse);
Button result = new Button(tag,closeImg);
result.setPrefHeight(20);
result.setContentDisplay(ContentDisplay.RIGHT);
result.setOnAction(event -> box.getChildren().remove(result));
box.getChildren().add(result);
}
}
Also if u need different events for click on tag and click on "X" you can implement tagButton like this :
public void tagButton(HBox box,String tag){
ImageView closeImg = new ImageView(toUse);
HBox button = new HBox();
button.setStyle("-fx-padding:4;" +
" -fx-border-width: 2;" +
" -fx-border-color: black;" +
" -fx-border-radius: 4;" +
" -fx-background-color: f1f1f1;" +
" -fx-border-insets: 5;");
button.setPrefHeight(20);
button.getChildren().addAll(new Label(tag),closeImg);
closeImg.setOnMouseClicked(event ->
box.getChildren().remove(button)
);
button.setOnMouseClicked(event -> {
//doSomethig
});
box.getChildren().add(button);
}
This is my version too
The whole Main class
its somehow long that's why.
But to sum up. You need a
1: FlowPane for the container, and you do not have to worry about wrapping,it will wrap itself, both vertical or horizontal.
2: Label of course for your Text, which has a GraphicProperty
3: Path - well you could use Button, and add a Shape or Image to it, but that will be a lot of Nodes, so i used Path and i drew a X red button.
The rest is styling to your preferred color
EDIT
something like this?
you can style it to get that output
setFont(Font.font("Serif Regular", FontWeight.SEMI_BOLD,12));
use this line on the TextField
Here is a basic example of a tag bar (I wrote some code, because I think it's easier to follow). For the additional AutoComplete function you could use e.g. ControlsFx, as fabian already mentioned.
public class CloseTag extends HBox implements Comparable<CloseTag> {
private Label label;
private Label closeIcon;
public CloseTag(String text) {
setStyle("-fx-padding:8;");
Text icon = GlyphsDude.createIcon(FontAwesomeIcon.TIMES_CIRCLE);
closeIcon = new Label(null, icon);
label = new Label(text, new StackPane(closeIcon));
label.setContentDisplay(ContentDisplay.RIGHT);
getChildren().add(label);
}
public void setOnCloseAction(EventHandler<? super MouseEvent> action) {
closeIcon.setOnMouseClicked(action);
}
public String getText() {
return label.getText();
}
#Override
public int compareTo(CloseTag other) {
return getText().compareTo(other.getText());
}
}
public class TagPane extends FlowPane {
private TextField textField;
public TagPane() {
setStyle("-fx-padding:8;" + "-fx-hgap:10;");
setOnMouseClicked(evt -> onMouseClickedd(evt));
textField = new TextField();
textField.setOnKeyPressed(evt -> onKeyPressed(evt, textField));
}
private void onMouseClickedd(MouseEvent mouseEvent) {
if (mouseEvent.getTarget() != this || textField.getParent() != null ) {
return;
}
getChildren().add(textField);
textField.requestFocus();
}
private void onKeyPressed(KeyEvent evt, TextField textField) {
if (evt.getCode() == KeyCode.ENTER || evt.getCode() == KeyCode.TAB) {
createTag(textField.getText());
textField.clear();
}
}
private void createTag(String text) {
CloseTag tag = new CloseTag(text);
tag.setOnCloseAction(evt -> removeTag(tag));
getChildren().remove(textField);
getChildren().add(tag);
}
private void removeTag(CloseTag tag) {
getChildren().remove(tag);
}
}

How to download fxml with custom cell in a ListView?

I want to make a custom cell in the ListView. Excuse my bad English!
I want to display the picture, name and status in the ListView.
For this I use a different Fxml that contains Hbox .
public class Controller {
CollectionContactForListCollection contactForList = new CollectionContactForListCollection();
#FXML
private ListView<Contact> listContact ;
#FXML
HBox hbox;
#FXML
ImageView avatar;
#FXML
Label labelName;
#FXML
Label lblStatus;
#FXML
Label lblSense;
#FXML
private void initialize(){
contactForList.fieldData();
// listContact.setItems((ObservableList) contactForList.getContactList());
listContact.setCellFactory(new Callback<ListView<Contact>, ListCell<Contact>>() {
#Override
public ListCell<Contact> call(ListView<Contact> param) {
ListCell<Contact> listCell = new ListCell<Contact>() {
#Override
protected void updateItem(Contact item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {
//This method does not work download
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/view/boxInContact.fxml"));
fxmlLoader.setController(this);
labelName.setText(item.getName());
lblSense.setText(item.getSense());
lblStatus.setText(item.getStatus());
avatar.setImage(item.getImage());
}
}
};
return listCell;
}
});
listContact.setItems((ObservableList) contactForList.getContactList());
}
As a general, rule, you should use a different controller class for each FXML file. With the code you have, all cells are using the same controller instance, so there is only one reference to each control, even though there are many labelNames, etc (one for each cell).
So define a controller for the FXML defined for the list cell, and define the methods you need to update the controls:
public class ContactCellController {
#FXML
private Label labelName ;
#FXML
private Label labelStatus ;
#FXML
private Label labelSense ;
#FXML
private ImageView avatar ;
public void setName(String name) {
labelName.setText(name);
}
public void setStatus(String status) {
labelStatus.setText(status);
}
public void setSense(String sense) {
labelSense.setText(sense);
}
public void setAvatarImage(Image image) {
avatar.setImage(image);
}
}
Update the FXML file to use a controller attribute with fx:controller="my.package.ContactCellController", and then your cell implementation can look like
listContact.setCellFactory(lv -> new ListCell<Contact>() {
private Node graphic ;
private ContactCellController controller ;
{
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/boxInContact.fxml"));
graphic = loader.load();
controller = loader.getController();
} catch (IOException exc) {
throw new RuntimeException(exc);
}
}
#Override
protected void updateItem(Contact contact, boolean empty) {
super.updateItem(contact, empty);
if (empty) {
setGraphic(null);
} else {
controller.setName(contact.getName());
controller.setStatus(contact.getStatus());
controller.setSense(contact.getSense());
controller.setAvatarImage(contact.getImage());
setGraphic(graphic);
}
}
});

JavaFX: ComboBox cells disappear when clicked

I'am working on a java project using javafx multiple input types.but i am having a strangle ComboBox behaviours since i use Labels with images(ImageView) on it.
1- Combobox looks in white! but i need it in black.
2- and every time i choose an item.
3- it disappear!!!
Here is my code:
...
import javafx.scene.control.ComboBox;
import javafx.scene.image.ImageView;
ImageView img_tun = new ImageView("images/icones/flag/Tunisia.png");
Label lbl_tun=new Label("1",img_tun);
ImageView img_fr = new ImageView("images/icones/flag/France.png");
Label lbl_fr=new Label("2",img_fr);
ImageView img_aut = new ImageView("images/icones/flag/World.png");
Label lbl_aut=new Label("3",img_aut);
optionsnat=FXCollections.observableArrayList(lbl_tun,lbl_fr,lbl_aut);
#FXML
ComboBox<Label> cb_nat = new ComboBox<Label>();
private String nat="1";
...
#Override
public void initialize(URL location, ResourceBundle resources) {
...
cb_nat.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observableValue, Number number, Number number2) {
if(cb_nb.getItems().get((Integer) number2)=="1"){setNat("1");}
else if(cb_nb.getItems().get((Integer) number2)=="2"){setNat("2");}
else if(cb_nb.getItems().get((Integer) number2)=="3"){setNat("3");}
else{System.err.println("Erreur lors de changement de nation..");}
}
});
}
...
and code.fxml
<ComboBox fx:id="cb_nat" layoutX="40.0" layoutY="265.0" prefWidth="150.0" />
EDIT:
After reading this Article i know that my approach is tottaly wrong and strongly not recommended.. if anyone have another ideas to put bnation flags in ComboBox please help!!
thanks..(Sorry for my bad english)
What is causing this problem is that when you choose a ListCell, its item (Label in our situation) is being moved by the ComboBox from the ListCell (Items observableList) to the ButtonCell, the ButtonCell is the small box that is empty by default. However, we all know that any Node object cannot be placed twice anywhere inside the same scene, and since there is no clone function for the ListCell class, javafx removes it from its last place to the new place which is the ButtonCell.
The solution is to add strings
items in the list and provide a cell factory to create the label node inside the cell factory. Create a class called "StringImageCell" and do the following:
You need to set the cellFactory property:
cb_nat.setCellFactory(listview -> new StringImageCell());
You need to set the buttonCell property: cb_nat.setButtonCell(new StringImageCell());
Here is an example:
public class ComboBoxCellFactory extends Application {
#Override
public void start(Stage stage) throws Exception {
ComboBox<String> comboBox = new ComboBox<>();
comboBox.getItems().addAll("1", "2", "3");
//Set the cellFactory property
comboBox.setCellFactory(listview -> new StringImageCell());
// Set the buttonCell property
comboBox.setButtonCell(new StringImageCell());
BorderPane root = new BorderPane();
root.setCenter(comboBox);
Scene scene = new Scene(root, 600, 600);
stage.setScene(scene);
stage.show();
}
//A Custom ListCell that displays an image and string
static class StringImageCell extends ListCell<String> {
Label label;
static HashMap<String, Image> pictures = new HashMap<>();
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setItem(null);
setGraphic(null);
} else {
setText(item);
ImageView image = getImageView(item);
label = new Label("",image);
setGraphic(label);
}
}
}
private static ImageView getImageView(String imageName) {
ImageView imageView = null;
switch (imageName) {
case "1":
case "2":
case "3":
if (!pictures.containsKey(imageName)) {
pictures.put(imageName, new Image(imageName + ".png"));
}
imageView = new ImageView(pictures.get(imageName));
break;
default:
imageName = null;
}
return imageView;
}
public static void main(String[] args) {
launch(args);
}
}

Categories

Resources