ArrayList displayed incorrectly - java

I'm testing to display Array List into JavaFX accordion:
public class Navigation {
// Object for storing conenctions
public static List<dataObj> list = new ArrayList<>();
private ObservableList<dataObj> data;
public class dataObj {
private String conenctionname;
public dataObj(String conenctionname) {
this.conenctionname = conenctionname;
}
public String getConenctionname() {
return conenctionname;
}
public void setConenctionname(String conenctionname) {
this.conenctionname = conenctionname;
}
}
public void initNavigation(Stage primaryStage, Group root, Scene scene) {
VBox stackedTitledPanes = createStackedTitledPanes();
ScrollPane scroll = makeScrollable(stackedTitledPanes);
scroll.getStyleClass().add("stacked-titled-panes-scroll-pane");
scroll.setPrefSize(395, 580);
scroll.setLayoutX(5);
scroll.setLayoutY(32);
root.getChildren().add(scroll);
}
private ScrollPane makeScrollable(final VBox node) {
final ScrollPane scroll = new ScrollPane();
scroll.setContent(node);
scroll.viewportBoundsProperty().addListener(new ChangeListener<Bounds>() {
#Override
public void changed(ObservableValue<? extends Bounds> ov, Bounds oldBounds, Bounds bounds) {
node.setPrefWidth(bounds.getWidth());
}
});
return scroll;
}
/////////////////////////////////////////////////////////////////////////////////////
// Generate accordition with Connections, Tables and Description
private VBox createStackedTitledPanes() {
VBox stackedTitledPanes = new VBox();
stackedTitledPanes.getChildren().setAll(
createConnectionsList("Connections"));
((TitledPane) stackedTitledPanes.getChildren().get(0)).setExpanded(true);
stackedTitledPanes.getStyleClass().add("stacked-titled-panes");
return stackedTitledPanes;
}
//////////////////////////////////////////////////////////////////////////////
// Generate list with Connections
public TitledPane createConnectionsList(String title) {
initObject();
data = FXCollections.observableArrayList(list);
ListView<dataObj> lv = new ListView<>(data);
lv.setCellFactory(new Callback<ListView<dataObj>, ListCell<dataObj>>() {
#Override
public ListCell<dataObj> call(ListView<dataObj> p) {
return new ConnectionsCellFactory();
}
});
AnchorPane content = new AnchorPane();
content.getChildren().add(lv);
// add to TitelPane
TitledPane pane = new TitledPane(title, content);
return pane;
}
static class ConnectionsCellFactory extends ListCell<dataObj> {
#Override
public void updateItem(dataObj item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
for (int i = 0; i < list.size(); i++) {
setText(list.get(i).getConenctionname());
}
}
}
}
// Insert Some test data
public void initObject() {
dataObj test1 = new dataObj("test data 1");
dataObj test2 = new dataObj("test data 2");
list.add(test1);
list.add(test2);
}
}
But for some reason I cannot get proper list of Objects from the Array list and display them. I get this result:
The proper result should be test data 1 and test data 2. How I can fix this?

The problem is in the ConnectionsCellFactory, the method updateItem is called for every item in List. So in you code, for every cell you are setting the text for every item in the list
you should try:
static class ConnectionsCellFactory extends ListCell<dataObj> {
#Override
public void updateItem(dataObj item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText(item.getConenctionname());
}
}
}

Related

JavaFX - How to disable TreeTableView column or Cell children Node on Button Click

I have JFXTreeTableView which consist of 5 columnsx In that first 2 columns have Delete & Edit Buttons for each cell. After populating table
I want first columns should disable on save Button click.
If above case is not possible then delete Buttons inside first column's cells should be disabled on Save button click.
I did like this but dont know how to disable column or buttons inside cells.
Controller Class
public class FinanceActionsController implements Initializable {
#FXML
private JFXTreeTableView<InvoiceItems> tblInvoiceItemsView;
private JFXButton btnSave;
#FXML
private HBox hbBottonBtnBar;
ObservableList<InvoiceItems> invoiceItems = FXCollections.observableArrayList();
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
tableStructure();
btnSave.setOnAction((ActionEvent event) -> {
if (invoiceItems.isEmpty()) {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setHeaderText("Please add Atleast one Invoice Item");
alert.showAndWait();
} else {
onClickBtnSaveInvoice();
disableAndAddControlsOnSave();
//tblInvoiceItemsView.setDisable(true);
}
});
}
private void tableStructure() {
JFXTreeTableColumn<InvoiceItems, Boolean> delItem = new JFXTreeTableColumn<>("Delete");
JFXTreeTableColumn<InvoiceItems, String> editItem = new JFXTreeTableColumn<>("Edit");
JFXTreeTableColumn<InvoiceItems, String> billItem = new JFXTreeTableColumn<>("Billable Head");
delItem.setCellValueFactory((TreeTableColumn.CellDataFeatures<InvoiceItems, Boolean> param) -> param.getValue().getValue().getBtnFlag());
delItem.setCellFactory(new Callback<TreeTableColumn<InvoiceItems, Boolean>, TreeTableCell<InvoiceItems, Boolean>>() {
#Override
public TreeTableCell<InvoiceItems, Boolean> call(TreeTableColumn<InvoiceItems, Boolean> param) {
final TreeTableCell<InvoiceItems, Boolean> cell = new TreeTableCell<InvoiceItems, Boolean>() {
MaterialIconView del = new MaterialIconView(MaterialIcon.DELETE_FOREVER, "1.5em");
final JFXButton btnDel = new JFXButton("", del);
#Override
public void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
setText(null);
} else {
btnDel.disableProperty().bind(txtN.disableProperty());
del.setFill(Color.RED);
btnDel.setButtonType(JFXButton.ButtonType.RAISED);
btnDel.setOnAction(event -> {
});
setGraphic(btnDel);
setText(null);
}
}
};
return cell;
}
});
billItem.setCellValueFactory((TreeTableColumn.CellDataFeatures<InvoiceItems, String> param) -> param.getValue().getValue().getBillItemDesc());
final TreeItem<InvoiceItems> root = new RecursiveTreeItem<>(invoiceItems, RecursiveTreeObject::getChildren);
tblInvoiceItemsView.getColumns().setAll(delItem, editItem, billItem);
tblInvoiceItemsView.setRoot(root);
tblInvoiceItemsView.setShowRoot(false);
}
Class InvoiceItems -
class InvoiceItems extends RecursiveTreeObject<InvoiceItems> {
StringProperty billItemDesc;
BooleanProperty btnFlag;
public InvoiceItems(String billItemDesc) {
this.billItemDesc = new SimpleStringProperty(billItemDesc);
}
public StringProperty getBillItemDesc() {
return billItemDesc;
}
public BooleanProperty getBtnFlag() {
return btnFlag;
}
public void setBtnFlag(Boolean btnFlag) {
this.btnFlag = new SimpleBooleanProperty(btnFlag);
}
}
I have tried to pass InvoiceItems setBtnFlag as True in Observable list to work in setCellFactory's updateItem method but not working. Please help any help will be appreciable, Thank You.

Create a listener logic for a custom combobox

I am trying to create a listener logic for a custom combo box that I have created that contains items with check boxes.
I was not able to proceed as I am not getting an idea on how to do it.
MainApplication.java
public class MainApplication extends Application{
#Override
public void start(Stage stage) {
Scene scene = new Scene(new VBox(), 450, 250);
ComboBox<ComboBoxItemWrap<Person>> cb = new ComboBox<>();
#SuppressWarnings("unchecked")
ObservableList<ComboBoxItemWrap<Person>> options = FXCollections.observableArrayList(
new ComboBoxItemWrap<>(new Person("A", "12-Aug-1994")),
new ComboBoxItemWrap<>(new Person("B", "13-Aug-1994")),
new ComboBoxItemWrap<>(new Person("C", "14-Aug-1994"))
);
cb.setCellFactory( c -> {
ListCell<ComboBoxItemWrap<Person>> cell = new ListCell<ComboBoxItemWrap<Person>>(){
#Override
protected void updateItem(ComboBoxItemWrap<Person> item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
final CheckBox cb = new CheckBox(item.toString());
cb.selectedProperty().bind(item.checkProperty());
setGraphic(cb);
}
}
};
cell.addEventFilter(MouseEvent.MOUSE_RELEASED, event -> {
cell.getItem().checkProperty().set(!cell.getItem().checkProperty().get());
StringBuilder sb = new StringBuilder();
cb.getItems().filtered( f-> f!=null).filtered( f-> f.getCheck()).forEach( p -> {
sb.append("; "+p.getItem());
});
final String string = sb.toString();
cb.setPromptText(string.substring(Integer.min(2, string.length())));
});
return cell;
});
cb.setItems(options);
VBox root = (VBox) scene.getRoot();
Button bt = new Button("test");
bt.setOnAction(event -> {
cb.getItems().filtered( f -> f.getCheck()).forEach( item -> System.out.println(item.getItem()));
});
root.getChildren().addAll(cb, bt);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
ComboBoxItemWrap.java
public class ComboBoxItemWrap<T> {
private BooleanProperty check = new SimpleBooleanProperty(false);
private ObjectProperty<T> item = new SimpleObjectProperty<>();
ComboBoxItemWrap() {
}
ComboBoxItemWrap(T item) {
this.item.set(item);
}
ComboBoxItemWrap(T item, Boolean check) {
this.item.set(item);
this.check.set(check);
}
public BooleanProperty checkProperty() {
return check;
}
public Boolean getCheck() {
return check.getValue();
}
public void setCheck(Boolean value) {
check.set(value);
}
public ObjectProperty<T> itemProperty() {
return item;
}
public T getItem() {
return item.getValue();
}
public void setItem(T value) {
item.setValue(value);
}
#Override
public String toString() {
return item.getValue().toString();
}
}
Person.java
public class Person {
private StringProperty name = new SimpleStringProperty();
private StringProperty birthday = new SimpleStringProperty();
public Person() {
}
public Person(String name, String birthday) {
setNameValue(name);
setBirthdayValue(birthday);
}
public StringProperty getNameProperty() {
return name;
}
public String getNameValue() {
return name.getValue();
}
public void setNameValue(String value) {
name.setValue(value);
}
public StringProperty getBirthdayProperty() {
return birthday;
}
public String getBirthdayValue() {
return birthday.getValue();
}
public void setBirthdayValue(String value) {
birthday.setValue(value);
}
#Override
public String toString() {
return getNameValue()+" ("+getBirthdayValue()+")";
}
}
In the output application, a list of items with check boxes will get populated. On selection of any number of entries in the list, the entry name gets populated on the combo box itself separated by a ';'. Now I want my back end code to listen and identify the entries that have been selected in order to perform further operations.
You may not need to reinvent the wheel. Consider using ControlsFX CheckComboBox.
That being said there are several problems in the code:
You never update the property on a selection of the CheckBox. This can be easily fixed by using bidirectional bindings.
Since the ComboBox popup is closed, the CheckBox is no longer armed at the time the MOUSE_RELEASED event is triggered. this is a prerequesite for the selected state of the CheckBox changing though. Modifying the skin allows you to change this behaviour.
You use ObservableList.filtered to create FilteredLists that you throw away immediately afterwards. You also create a filtered list of a filtered list in the MOUSE_RELEASED event filter. This is not wrong per se, but you're creating an expensive object there without the need to do so: simply get a stream there. This is a much more lightweight way to filter a list, if the result is only needed once. Use filtered/FilteredList only if you need an ObservableList that contains elements from another ObservableList and that is automatically updated.
Also note that there is a way to make an ObservableList trigger update changes on a change of a property: Use the observableArrayList method taking an extractor as parameter.
This is how you could rewrite your code to make it work:
VBox root = new VBox();
Scene scene = new Scene(root, 450, 250);
ComboBox<ComboBoxItemWrap<Person>> cb = new ComboBox<>();
ObservableList<ComboBoxItemWrap<Person>> options = FXCollections.observableArrayList(item -> new Observable[] {item.checkProperty()});
options.addAll(
new ComboBoxItemWrap<>(new Person("A", "12-Aug-1994")),
new ComboBoxItemWrap<>(new Person("B", "13-Aug-1994")),
new ComboBoxItemWrap<>(new Person("C", "14-Aug-1994")));
cb.setCellFactory(c -> new ListCell<ComboBoxItemWrap<Person>>() {
private final CheckBox cb = new CheckBox();
#Override
protected void updateItem(ComboBoxItemWrap<Person> item, boolean empty) {
ComboBoxItemWrap<Person> oldItem = getItem();
if (oldItem != null) {
// remove old binding
cb.selectedProperty().unbindBidirectional(oldItem.checkProperty());
}
super.updateItem(item, empty);
if (empty || item == null) {
setGraphic(null);
} else {
cb.selectedProperty().bindBidirectional(item.checkProperty());
cb.setText(item.toString());
setGraphic(cb);
}
}
});
// make sure popup remains open
ComboBoxListViewSkin<ComboBoxItemWrap<Person>> skin = new ComboBoxListViewSkin<>(cb);
skin.setHideOnClick(false);
cb.setSkin(skin);
cb.setItems(options);
cb.promptTextProperty().bind(Bindings.createStringBinding(() ->
options.stream().filter(ComboBoxItemWrap::getCheck).map(Object::toString).collect(Collectors.joining("; ")), options));
Note that if you want the popup to be closed after (de)selecting a checkbox, you could simply add a event filter for MOUSE_RELEASED for the checkbox that calls cb.arm() instead of modifying the skin.

How to detect a tableview items count resize in JavaFx?

I am trying to filter and show items count with FX Tableview.
There are some buttons and txt field for listing a spesific data
here is
text input: filtering
clear button: gettingenter code here default values
this month button: getting this month values
data picker: for getting any day values
There is a label for showing table items size
public Label lblRecordSize;
My problem;
i don't want to lblRecordSize.settext() under the every action for filtering.
Is there a listener like tableview.onItemSizeChangeListener() etc.
UPDATED:
public class CenterDbController implements Initializable {
public AnchorPane centerDbPanelPane;
/** */
public TextField txtSerialNo;
public ComboBox<EModemModel> cbxModemModel;
public Label lblRecordSize;
public DatePicker dpStartDate;
public DatePicker dpEndDate;
public Button btnShow;
public Button btnClear;
/** */
public TableView<RecordedTest> tblvRecords;
public TableColumn colRecordId;
public TableColumn colOfficeId;
public TableColumn colCompany;
public TableColumn colModemSerialNumber;
public TableColumn colModemBrand;
public TableColumn colModemModel;
public TableColumn colSoftwareVersion;
public TableColumn colAccessTest;
public TableColumn colSoftwareTest;
public TableColumn colDhcpTest;
public TableColumn colWifiTest;
public TableColumn colInternetTest;
public TableColumn colResetTest;
public TableColumn colTestResult;
public TableColumn colSendStatus;
public TableColumn colRecordDate;
public TableColumn colTestDetails;
/** */
private IRecordedTestService recordedTestService;
private FilteredList<RecordedTest> filteredList;
#Override
public void initialize(URL location, ResourceBundle resources) {
initCellFactories();
recordedTestService = UtilsForSpring.getSingleBeanOfType(IRecordedTestService.class);
filteredList = new FilteredList<>(FXCollections.observableList(recordedTestService.getThisMonthRecords()), s -> true);
tblvRecords.setItems(filteredList);
lblRecordSize.textProperty().bind(Bindings.size(tblvRecords.getItems()).asString("%s"));
// lblRecordSize.setText(filteredList.size() + "");
filteredList.addListener(new ListChangeListener<RecordedTest>() {
#Override
public void onChanged(Change<? extends RecordedTest> c) {
// lblRecordSize.setText(filteredList.size() + "");
}
});
cbxModemModel.getItems().addAll(EModemModel.values());
}
private void initCellFactories() {
colRecordId.setCellValueFactory(new PropertyValueFactory<RecordedTest, String>("recordId"));
colOfficeId.setCellValueFactory(new PropertyValueFactory<>("officeId"));
colCompany.setCellValueFactory(new PropertyValueFactory<>("company"));
colModemSerialNumber.setCellValueFactory(new PropertyValueFactory<>("modemSerialNumber"));
colModemBrand.setCellValueFactory(new PropertyValueFactory<>("modemBrand"));
colModemModel.setCellValueFactory(new PropertyValueFactory<>("modemModel"));
colSoftwareVersion.setCellValueFactory(new PropertyValueFactory<>("softwareVersion"));
colAccessTest.setCellValueFactory(new PropertyValueFactory<>("accessTest"));
colSoftwareTest.setCellValueFactory(new PropertyValueFactory<>("softwareTest"));
colDhcpTest.setCellValueFactory(new PropertyValueFactory<>("dhcpTest"));
colWifiTest.setCellValueFactory(new PropertyValueFactory<>("wifiTest"));
colInternetTest.setCellValueFactory(new PropertyValueFactory<>("internetTest"));
colResetTest.setCellValueFactory(new PropertyValueFactory<>("resetTest"));
colTestResult.setCellValueFactory(new PropertyValueFactory<>("testResult"));
colSendStatus.setCellValueFactory(new PropertyValueFactory<>("sendStatus"));
colRecordDate.setCellValueFactory(new PropertyValueFactory<>("recordDate"));
colTestDetails.setCellValueFactory(new PropertyValueFactory<>("testDetails"));
}
public void btnClearOnClickAction(ActionEvent e) {
txtSerialNo.clear();
cbxModemModel.getSelectionModel().clearSelection();
dpEndDate.setValue(null);
dpStartDate.setValue(null);
filteredList = new FilteredList<>(FXCollections.observableList(recordedTestService.getThisMonthRecords()), s -> true);
tblvRecords.setItems(filteredList);
// lblRecordSize.setText(filteredList.size() + "");
}
public void btnShowOnClickAction(ActionEvent e) {
if (dpStartDate.getValue() != null && dpEndDate != null) {
filteredList = new FilteredList<>(FXCollections.observableList(recordedTestService.getBetweenRecords(dpStartDate.getValue(), dpEndDate.getValue())));
tblvRecords.setItems(filteredList);
}
}
public void tableOnSortListener() {
// lblRecordSize.setText(tblvRecords.getItems().size() + "");
}
public void txtSerialNoOnKeyPress() {
txtSerialNo.textProperty().addListener(observable -> {
String filter = txtSerialNo.getText();
if (filter == null || filter.length() == 0) {
filteredList.setPredicate(s -> true);
} else {
filteredList.setPredicate(s -> s.getModemSerialNumber().contains(filter));
}
});
}
public void cbxModemModelOnValueChange() {
String filter = cbxModemModel.getSelectionModel().getSelectedItem().toString();
if (filter == null || filter.length() == 0) {
filteredList.setPredicate(s -> true);
} else {
filteredList.setPredicate(s -> s.getModemModel().equalsIgnoreCase(filter));
}
}
}
The Bindings class provides a size method that allows you to create a binding for the size of a ObservableList. Assuming you modify the existing list and do not replace it with a new one every time you filter (e.g. using FilteredList), you can use this to bind the Label text:
// after assigning the items
lblRecordSize.textProperty().bind(Bindings.size(tableview.getItems()).asString("Record count: %s"));
Edit
In your code you replace the items list. The prequesite of the items not being replaced is not given...
You could add a listener to the item property instead and rebind the Label text every time
// before setting items the first time
tblvRecords.itemsProperty().addListener((observable, oldItems, newItems) ->
lblRecordSize.textProperty().bind(
Bindings.size(newItems).asString()));
However you could also modify a single list to contain the source data instead of recreating the lists every time:
private final ObservableList<RecordedTest> data = FXCollections.observableArrayList();
private final FilteredList<RecordedTest> filteredList = new FilteredList<>(data);
#Override
public void initialize(URL location, ResourceBundle resources) {
initCellFactories();
recordedTestService = UtilsForSpring.getSingleBeanOfType(IRecordedTestService.class);
filteredList.setPredicate(null);
data.setAll(recordedTestService.getThisMonthRecords());
tblvRecords.setItems(filteredList);
lblRecordSize.textProperty().bind(Bindings.size(filteredList).asString());
...
}
...
public void btnClearOnClickAction(ActionEvent e) {
...
filteredList.setPredicate(null);
data.setAll(recordedTestService.getThisMonthRecords()));
}

JavaFX TableView cell set value later

I have a TableView with 4 rows and for each one a custom cell. The first one is just an image, which one I want to update a few times, a string, a string with word wrap and an second image as a button.
// image
tcUrlStatus.setCellValueFactory(new ColumnImageFactory());
tcUrlStatus.setCellFactory(new ColumnCallback());
// one line string
tcUrlName.setCellValueFactory(new ColumnNameFactory());
tcUrlName.setCellFactory(new ColumnCallback());
// two line string
tcUrlDate.setCellValueFactory(new ColumnDateFactory());
tcUrlDate.setCellFactory(new ColumnCallback());
// image as a button
tcDelete.setCellValueFactory(new ColumnDeleteFactory());
tcDelete.setCellFactory(new ColumnCallback());
And here are the custom cells
class ColumnCallback implements Callback<TableColumn<Quartet<Boolean, String, String, String>, Object>, TableCell<Quartet<Boolean, String, String, String>, Object>>{
#Override
public TableCell<Quartet<Boolean, String, String, String>, Object> call(TableColumn<Quartet<Boolean, String, String, String>, Object> column) {
return new ColumnCell();
}
}
class ColumnImageFactory implements Callback<TableColumn.CellDataFeatures<Quartet<Object, String, String, String>, String>, ObservableValue<Object>> {
#Override
public ObservableValue<Object> call(TableColumn.CellDataFeatures<Quartet<Object, String, String, String>, String> data) {
return new ReadOnlyObjectWrapper<>(data.getValue().getValue0());
}
}
class ColumnNameFactory implements Callback<TableColumn.CellDataFeatures<Quartet<Boolean, String, String, String>, String>, ObservableValue<String>> {
#Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<Quartet<Boolean, String, String, String>, String> data) {
return new ReadOnlyObjectWrapper<>(data.getValue().getValue1());
}
}
class ColumnDateFactory implements Callback<TableColumn.CellDataFeatures<Quartet<Boolean, String, String, String>, Object>, ObservableValue<Object>> {
#Override
public ObservableValue<Object> call(TableColumn.CellDataFeatures<Quartet<Boolean, String, String, String>, Object> data) {
return new ReadOnlyObjectWrapper<>(data.getValue().getValue2());
}
}
class ColumnDeleteFactory implements Callback<TableColumn.CellDataFeatures<Quartet<Boolean, String, String, String>, Object>, ObservableValue<Object>> {
#Override
public ObservableValue<Object> call(TableColumn.CellDataFeatures<Quartet<Boolean, String, String, String>, Object> data) {
return new ReadOnlyObjectWrapper<>(data.getValue().getValue3());
}
}
class ColumnCell extends TableCell<Quartet<Boolean, String, String, String>, Object> {
#Override
protected void updateItem(Object item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
if (item instanceof Boolean) {
setText(null);
Image image;
AnchorPane pane = new AnchorPane();
if ((boolean) item) {
image = new Image(Main.class.getResourceAsStream("/hourglass.gif"));
} else {
image = new Image(Main.class.getResourceAsStream("/clean.gif"));
}
ImageView imageView = new ImageView(image);
imageView.setFitWidth(30);
imageView.setY(5);
imageView.setPreserveRatio(true);
imageView.setSmooth(true);
pane.getChildren().add(imageView);
setGraphic(pane);
}else {
if (item instanceof String) {
if (item.equals("delete")) {
AnchorPane pane = new AnchorPane();
Image image = new Image(Main.class.getResourceAsStream("/cross.png"));
ImageView imageView = new ImageView(image);
imageView.setFitWidth(20);
imageView.setY(10);
imageView.setPreserveRatio(true);
imageView.setSmooth(true);
imageView.setCursor(Cursor.HAND);
pane.getChildren().add(imageView);
imageView.setOnMouseClicked((Event event) -> {
Quartet<Boolean, String, String, String> row = this.getTableView().getSelectionModel().getSelectedItem();
Controller.localJson.remove(row.getValue1());
this.getTableView().getItems().remove(row);
});
setGraphic(pane);
} else {
HBox pane = new HBox();
Label label = new Label();
label.setText((String) item);
label.setTextAlignment(TextAlignment.CENTER);
if (((String) item).length() < 20) {
label.setWrapText(true);
label.setAlignment(Pos.CENTER);
}
pane.setPrefHeight(40);
label.setPrefHeight(40);
pane.getChildren().add(label);
setGraphic(pane);
}
setText(null);
}
}
} else {
setText(null);
setGraphic(null);
}
}
}
Now I want to change the Image of the tcUrlStatus column/cell. I already figured out how to get the row or the value of it, but I can't figure out how to set the value or rather change from true to false or false to true for updating the image.
get row and value:
private int getTableRowIndex(String url){ // url is the second column
int counter = 0;
for (Object row:tvUrls.getItems()){
if ((((Quartet) row).getValue1() == url)){
return counter;
}
counter ++;
}
return -1;
}
int rowIndex = getTableRowIndex(url);
Object item = tvUrls.getItems().get(rowIndex);
It would be easiest to do this by using javafx properties in your Quartet class, e.g. assuming the type used for the value0 bean is T:
// TODO: Is using a readonly property really needed here ???
// if not, use SimpleObjectProperty instead
private final ReadOnlyObjectWrapper<T> value0 = new ReadOnlyObjectWrapper<>();
public T getValue0() {
return value0.get();
}
// TODO: should the setter really be public ???
public void setValue0(T newValue) {
value0.set(newValue);
}
public ReadOnlyObjectProperty<T> value0Property() {
return value0.getReadOnlyProperty();
}
Which allows you to use new PropertyValueFactory<>("value0") instead of your custom cell value factory, and, even more important, means the TableView components will be notified of changes in the Quartet instances.
This way you can simply use
quartetInstance.setValue0(newValue0);
and the cell will get updated.
Using the PropertyValueFactory would have the same effect as using the following cellValueFactory in this case:
class ColumnImageFactory implements Callback<TableColumn.CellDataFeatures<Quartet<Object, String, String, String>, String>, ObservableValue<Object>> {
#Override
public ObservableValue<Object> call(TableColumn.CellDataFeatures<Quartet<Object, String, String, String>, String> data) {
return data.getValue().value0Property();
}
}
If you cannot add javaFX properties to the Quartet class you need some other way of updating the TableView after the change. TableView.refresh() would work (provided you use JavaFX version >= 8u60) or writing an adapter in case you have implemented the observer patten in the Quartet class some other way...
I solved it with chaning from the Quartet Class to the SimpleObjectProperty Class.
Init:
tcUrlStatus.setCellValueFactory(new PropertyValueFactory<ColumnCellValue, Boolean>("status"));
tcUrlName.setCellValueFactory(new PropertyValueFactory<ColumnCellValue, String>("url"));
tcUrlDate.setCellValueFactory(new PropertyValueFactory<ColumnCellValue, String>("date"));
tcDelete.setCellValueFactory(new PropertyValueFactory<ColumnCellValue, Boolean>("delete"));
tcUrlStatus.setCellFactory(new ColumnStatusCell());
tcUrlName.setCellFactory(new ColumnStringCell(false));
tcUrlDate.setCellFactory(new ColumnStringCell(true));
tcDelete.setCellFactory(new ColumnDeleteCell());
Adding rows:
tvUrls.getItems().add(new ColumnCellValue(true, url, date));
Updating cells:
ColumnCellValue statusRow = (ColumnCellValue)
tvUrls.getItems().get(rowIndex);
Column classes:
public class ColumnCellValue{
private final ObjectProperty status;
private final ObjectProperty url;
private final ObjectProperty date;
private final ObjectProperty delete = new SimpleObjectProperty<Boolean>(true);
ColumnCellValue(Boolean status, String url, String date) {
this.status = new SimpleObjectProperty<Boolean>(status);
this.url = new SimpleObjectProperty<String>(url);
this.date = new SimpleObjectProperty<String>(date);
}
public Object getDate() {
return date.get();
}
public ObjectProperty dateProperty() {
return date;
}
public void setDate(Object date) {
this.date.set(date);
}
public Object getDelete() {
return delete.get();
}
public ObjectProperty deleteProperty() {
return delete;
}
public void setDelete(Object delete) {
this.delete.set(delete);
}
public Object getStatus() {
return status.get();
}
public ObjectProperty statusProperty() {
return status;
}
public void setStatus(Object status) {
this.status.set(status);
}
public Object getUrl() {
return url.get();
}
public ObjectProperty urlProperty() {
return url;
}
public void setUrl(Object url) {
this.url.set(url);
}
}
class ColumnStatusCell implements Callback<TableColumn<Boolean, Boolean>,TableCell<Boolean, Boolean>>{
#Override
public TableCell<Boolean, Boolean> call(TableColumn<Boolean, Boolean> param) {
AnchorPane pane = new AnchorPane();
ImageView imageView = new ImageView();
imageView.setFitWidth(30);
imageView.setY(5);
imageView.setPreserveRatio(true);
imageView.setSmooth(true);
TableCell<Boolean,Boolean> cell = new TableCell<Boolean,Boolean>(){
public void updateItem(Boolean item, boolean empty) {
if(item!=null){
Image image;
if (item) {
image = new Image(Main.class.getResourceAsStream("/hourglass.gif"));
} else {
image = new Image(Main.class.getResourceAsStream("/clean.gif"));
}
imageView.setImage(image);
}
}
};
pane.getChildren().add(imageView);
cell.setGraphic(pane);
return cell;
}
}
class ColumnStringCell implements Callback<TableColumn<String, String>,TableCell<String, String>>{
private boolean wrap = false;
ColumnStringCell(boolean wrap){
this.wrap = wrap;
}
#Override
public TableCell<String, String> call(TableColumn<String, String> param) {
TableCell<String,String> cell = new TableCell<String,String>(){
public void updateItem(String item, boolean empty) {
if(item!=null){
Label label = new Label();
label.setText(item);
label.setPrefHeight(40);
label.setTextAlignment(TextAlignment.CENTER);
label.setWrapText(wrap);
setGraphic(label);
}
}
};
return cell;
}
}
class ColumnDeleteCell implements Callback<TableColumn<Boolean, Boolean>,TableCell<Boolean, Boolean>>{
#Override
public TableCell<Boolean, Boolean> call(TableColumn<Boolean, Boolean> param) {
AnchorPane pane = new AnchorPane();
ImageView imageView = new ImageView();
imageView.setFitWidth(20);
imageView.setY(10);
imageView.setPreserveRatio(true);
imageView.setSmooth(true);
imageView.setCursor(Cursor.HAND);
TableCell<Boolean,Boolean> cell = new TableCell<Boolean,Boolean>(){
public void updateItem(Boolean item, boolean empty) {
if (item != null) {
Image image = new Image(Main.class.getResourceAsStream("/cross.png"));
imageView.setImage(image);
}
}
};
imageView.setOnMouseClicked((Event event) -> {
TableView table = (TableView) ((ImageView) event.getSource()).getParent().getParent().getParent().getParent().getParent().getParent().getParent();
ColumnCellValue row = (ColumnCellValue) (table).getSelectionModel().getSelectedItem();
Controller.localJson.remove(row.getUrl().toString());
table.getItems().remove(row);
table.refresh();
});
pane.getChildren().add(imageView);
cell.setGraphic(pane);
return cell;
}
}

JFrame - JList specific actions depending on selection

I created a list basically copying the 'ListDemo.java' from the oracle site. I included a picture of what I have.
public static class BackpackList extends JPanel implements ListSelectionListener {
private JList list;
private DefaultListModel listModel;
private static final String useString = "Use";
private JButton useButton;
public BackpackList() {
super(new BorderLayout());
listModel = new DefaultListModel();
listModel.addElement("Flashlight");
listModel.addElement("Health potion");
listModel.addElement("Snacks");
//Create the list and put it in a scroll pane.
list = new JList(listModel);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.setSelectedIndex(0);
list.addListSelectionListener(this);
list.setVisibleRowCount(10);
JScrollPane listScrollPane = new JScrollPane(list);
useButton = new JButton(useString);
useButton.setActionCommand(useString);
useButton.addActionListener(new UseListener());
//Create a panel that uses BoxLayout.
JPanel buttonPane = new JPanel();
buttonPane.setLayout(new BoxLayout(buttonPane,
BoxLayout.LINE_AXIS));
buttonPane.add(useButton);
buttonPane.add(Box.createHorizontalStrut(5));
buttonPane.add(new JSeparator(SwingConstants.VERTICAL));
buttonPane.add(Box.createHorizontalStrut(5));
buttonPane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
add(listScrollPane, BorderLayout.CENTER);
add(buttonPane, BorderLayout.PAGE_END);
}
class UseListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
//This method can be called only if
//there's a valid selection
//so go ahead and remove whatever's selected.
int index = list.getSelectedIndex();
listModel.remove(index);
int size = listModel.getSize();
if (size == 0) { //Nobody's left, disable firing.
useButton.setEnabled(false);
}
else { //Select an index.
if (index == listModel.getSize()) {
//removed item in last position
index--;
}
list.setSelectedIndex(index);
list.ensureIndexIsVisible(index);
}
}
}
#Override
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting() == false) {
if (list.getSelectedIndex() == -1) {
//No selection, disable fire button.
useButton.setEnabled(false);
}
else {
//Selection, enable the fire button.
useButton.setEnabled(true);
}
}
}
}
Question 1: I am setting up a backpack for a basic text based game. I want to set up specific actions depending on what item you have selected in the list. What would be the code to make it so the health potion would do something different than the snacks?
Question 2: How could I make it so it would say something along the lines of "x2 Snacks" if you have 2 snacks or "x3 Snacks" if you have 3 snacks, etc.
The backpack needs to keep track of items that are defined elsewhere. JList can hold a list of any kind of object so what you need to do is create objects for the inventory items. Below shows an example using an enum:
public class InventoryManager {
public enum InventoryItem {
LIGHT("Flashlight") {
boolean isOn;
#Override public void doAction() {
isOn = !isOn;
}
#Override public String toString() {
return name;
}
},
POTION("Health Potions") {
#Override public void doAction() {
Game.getPlayer().setHealth(Game.getPlayer().getHealth() + 25);
remove(1);
}
},
SNACK("Snacks") {
#Override public void doAction() {
Game.getPlayer().setEnergy(Game.getPlayer().getEnergy() + 10);
remove(1);
}
};
private final String name;
private int quantity = 0;
private InventoryItem(String n) {
name = n;
}
public abstract void doAction();
public void add(int q) {
if ((quantity += q) < 0) quantity = 0;
}
public void remove(int q) {
add(-q);
}
#Override public String toString() {
return name + " x" + quantity;
}
}
public static InventoryItem[] getHeldItems() {
EnumSet<InventoryItem> items = EnumSet.allOf(InventoryItem.class);
Iterator<InventoryItem> it = items.iterator();
while (it.hasNext()) {
if (it.next().quantity < 1) {
it.remove();
}
}
return items.toArray(new InventoryItem[items.size()]);
}
}
The enum example is entirely static so there are some problems with actually doing it this way (I chose it primarily because it's the shortest code). But ultimately what you'll have is a superclass Item with abstract methods that the subclasses implement differently. Then you will populate the JList with the items held. When the user selects an item from the list, list.getSelectedValue() returns an Item object you can use in the game.
// or Item can be an interface classes implement
public abstract class Item {
public void doAction() {
Game.updateState();
}
}
public class Light extends InventoryItem {
boolean lighted;
#Override public void doAction() {
lighted = !lighted;
super.doAction();
}
}
public class Potion extends InventoryItem {
#Override public void doAction() {
player.hp++;
super.doAction();
}
}
public class Snack extends InventoryItem {
#Override public void doAction() {
player.energy++;
super.doAction();
}
}
The other way to do this is to use straight program logic, for example:
switch (list.getSelectedItem()) {
case "Flashlight": {
toggleFlashlight();
break;
}
case "Health Potion": {
usePotion();
break;
}
case "Snack": {
useSnack();
break;
}
}
But I have a feeling trying to do it all with logic like that will ultimately turn out to be more complicated.

Categories