Javafx tableview items prevent duplicates - arraylist

I'm using javafx tableview with observableList list, I tried to prevent list from holding duplicate items.
After doing some search, i figure out that an observableSet can do this job by overidding thoes methodes:equals() and hashcode().
But the problem that javaFX tableview can't hold an observable set:
tableView.setItems(FXCollections.observableSet(new hashSet<T>());
I also planned to calculate the some for a columns in my tableview so, i need
// After change in element T the total will change
ObservableList<T> listItems = FXCollections.observableArrayList(
T -> new Observable[]{T.doSomeCalculeProperty});
I really confused about the right way to do this. So, i need your hints

You can create an ObservableSet and then add a listener to it which updates an ObservableList which is used as the items list for the table. As long as modifications are not made directly to the table's items (only to the set, which you can enforce by using an unmodifiable list for the table), then the table will always contain exactly the same items as the set.
To track the total of the values of a property of all the items in the list, you need can register a listener with the list, and recompute the total when it changes. If the property itself may change, you can use an extractor when you create the list, so that the list will fire update notifications if that property changes for any of the list elements.
This example pieces all this together. The modification methods associated with the buttons all operate on the ObservableSet. Notice that if you try to add an item which is equal to an existing item, nothing changes (because adding to the set does nothing, and so no updates are fired to the list).
You can select and modify existing items using the increment and decrement buttons, and you'll see the updates reflected in the total.
import java.util.HashSet;
import java.util.Objects;
import java.util.stream.Collectors;
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.ObservableSet;
import javafx.collections.SetChangeListener.Change;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class UniqueItemTableViewWithTotal extends Application {
// creates a table view which always contains the same items as the provided set
private TableView<Item> createTableView(ObservableSet<Item> items, IntegerProperty total) {
TableView<Item> table = new TableView<>();
table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
// Want the table's items list to fire updates if the value of any item changes
// This allows observing the list for tracking the total of all values
ObservableList<Item> itemList = FXCollections.observableArrayList(
item -> new Observable[] {item.valueProperty()});
// register a listener with the set and update the list if the set changes
// this ensures the list will always contain the same elements as the list,
items.addListener((Change<? extends Item> c) -> {
if (c.wasAdded()) {
itemList.add(c.getElementAdded());
}
if (c.wasRemoved()) {
itemList.remove(c.getElementRemoved());
}
});
// usual column setup
TableColumn<Item, String> nameCol = new TableColumn<>("Item");
nameCol.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
TableColumn<Item, Integer> valueCol = new TableColumn<>("Value");
valueCol.setCellValueFactory(cellData -> cellData.getValue().valueProperty().asObject());
table.getColumns().add(nameCol);
table.getColumns().add(valueCol);
// use an unmodifiable list for the table to prevent any direct updates to the
// table's list (updates must go through the set)
table.setItems(FXCollections.unmodifiableObservableList(itemList));
// update total if the items list changes:
itemList.addListener((ListChangeListener.Change<? extends Item> c) ->
total.set(itemList.stream()
.collect(Collectors.summingInt(Item::getValue))));
// add any existing elements:
itemList.addAll(items);
return table ;
}
#Override
public void start(Stage primaryStage) {
ObservableSet<Item> items = FXCollections.observableSet(new HashSet<>());
IntegerProperty total = new SimpleIntegerProperty();
TableView<Item> table = createTableView(items, total);
for (int i = 1; i <=5 ; i++) {
items.add(new Item("Item "+i, 1+(int)(Math.random()*20)));
}
// label to display the total of all values:
Label totalLabel = new Label();
totalLabel.textProperty().bind(total.asString("Total: %d"));
totalLabel.setStyle("-fx-font-size:24; -fx-padding:10;");
// text fields for new item:
TextField itemField = new TextField();
TextField valueField = new TextField();
// restrict value field to valid integers:
valueField.setTextFormatter(new TextFormatter<Integer>(c ->
c.getControlNewText().matches("-?\\d*") ? c : null));
// button to add new item:
Button addButton = new Button("Add");
addButton.setOnAction(e -> {
Item item = new Item(itemField.getText(), Integer.parseInt(valueField.getText()));
items.add(item);
itemField.clear();
valueField.clear();
});
addButton.disableProperty().bind(itemField.textProperty().isEmpty()
.or(valueField.textProperty().isEmpty()));
ObservableList<Item> selection = table.getSelectionModel().getSelectedItems();
// button to remove selected item(s):
Button removeButton = new Button("Delete");
removeButton.setOnAction(e ->
items.removeIf(new HashSet<Item>(selection)::contains));
removeButton.disableProperty().bind(Bindings.isEmpty(selection));
// button to increment selected item(s):
Button incButton = new Button("Increment");
incButton.setOnAction(e -> selection.forEach(Item::increment));
incButton.disableProperty().bind(Bindings.isEmpty(selection));
// button to decrement selected item(s):
Button decButton = new Button("Decrement");
decButton.setOnAction(e -> selection.forEach(Item::decrement));
decButton.disableProperty().bind(Bindings.isEmpty(selection));
HBox controls = new HBox(5, itemField, valueField, addButton, removeButton, incButton, decButton);
controls.setAlignment(Pos.CENTER);
controls.setPadding(new Insets(5));
BorderPane root = new BorderPane(table);
root.setTop(totalLabel);
root.setBottom(controls);
Scene scene = new Scene(root, 800, 800);
primaryStage.setScene(scene);
primaryStage.show();
}
// model item:
public static class Item {
private final StringProperty name = new SimpleStringProperty();
private final IntegerProperty value = new SimpleIntegerProperty();
public Item(String name, int value) {
setName(name);
setValue(value);
}
public final StringProperty nameProperty() {
return this.name;
}
public final String getName() {
return this.nameProperty().get();
}
public final void setName(final String name) {
this.nameProperty().set(name);
}
public final IntegerProperty valueProperty() {
return this.value;
}
public final int getValue() {
return this.valueProperty().get();
}
public final void setValue(final int value) {
this.valueProperty().set(value);
}
public void increment() {
setValue(getValue()+1);
}
public void decrement() {
setValue(getValue()-1);
}
#Override
public int hashCode() {
return Objects.hash(getName(), getValue());
}
#Override
public boolean equals(Object o) {
if (o.getClass() != Item.class) {
return false ;
}
Item other = (Item) o ;
return Objects.equals(getName(), other.getName())
&& getValue() == other.getValue() ;
}
}
public static void main(String[] args) {
launch(args);
}
}

Related

get selected checkboxs from tableview

I have tableview with checkbox in each row and I have an action button. My problem is that, how can I get selected checkbox from tableview to apply an action when the button is pressed?
This is how I add the checkbox to the tableview
public void addCeckBoxToTableView() {
/** define a simple boolean cell value for the action column so that the column will only be shown for non-empty rows. */
tcCb.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Object, Boolean>,
ObservableValue<Boolean>>() {
#Override
public ObservableValue<Boolean> call(TableColumn.CellDataFeatures<Object, Boolean> p) {
return new SimpleBooleanProperty(p.getValue() != null);
}
});
/** create a cell value factory with an add button for each row in the table. */
tcCb.setCellFactory(new Callback<TableColumn<Object, Boolean>, TableCell<Object, Boolean>>() {
#Override
public TableCell<Object, Boolean> call(TableColumn<Object, Boolean> p) {
return new CheckBoxCell();
}
});
}
private class CheckBoxCell extends TableCell<Object, Boolean> {
CheckBox checkBox = new CheckBox();
HBox hb = new HBox(checkBox);
/**
* places button in the row only if the row is not empty.
*/
#Override
protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setGraphic(hb);
} else {
setGraphic(null);
}
}
}
cordially.
I'm of the same opinion as James: "with that setup you can't"
But you could do it this way
private TableView<Record> table;
private TableColumn<Record, Boolean> tcCb;
private Button actionButton;
public class Record {
private SimpleBooleanProperty selected = new SimpleBooleanProperty();
public SimpleBooleanProperty selectedProperty() {
return selected;
}
}
#Override
public void start(Stage primaryStage) throws Exception {
table = new TableView<Record>(FXCollections.observableArrayList(new Record(), new Record(), new Record()));
table.setEditable(true);
// Create "CheckBox" - Column
tcCb = new TableColumn<Record, Boolean>("Boolean-Column");
tcCb.setCellValueFactory(new PropertyValueFactory<Record, Boolean>("selected"));
tcCb.setCellFactory(CheckBoxTableCell.forTableColumn(tcCb));
tcCb.setEditable(true);
table.getColumns().add(tcCb);
// Create actionButton for retrieving cellData
actionButton = new Button("action");
actionButton.setOnAction(actionEvent -> {
for (int row = 0; row < table.getItems().size(); row++) {
System.out.println(tcCb.getCellData(row));
}
});
// The uninteresting stuff...
primaryStage.setScene(new Scene(new VBox(table, actionButton)));
primaryStage.show();
}
The UI elements should associate them with the object. In your example, you wanted to apply the action on items which are selected(Selected CheckBox).
The Object associated with the table can be like this
public class TableData{
private Boolean selected=Boolean.False;
public void setSelected(Boolean isSelected){
this.isSelected = isSelected;
}
public boolean isSelected(){
return this.selected;
}
}
So In TableCell,
When the checkBox is selected, update 'selected' Boolean value of TableData,by adding a selection action listener to the CheckBox.
then you can iterate through the TableData which you can get it from the TableView, to apply the actions upon Button selection.

GWT - How to display String[][] array in CellTable or DataGrid

I am displaying a large String[][] array in a FlexTable, but it is very slow.
How can I display the array in a CellTable or DataGrid?
Examples show CellTable< some class> but I just want something like CellTable< String> , CellTable< String[]> , CellTable< List< String> > .
I solved the issue by modifying Google's ListDataProviderExample to use CellTable. Since the number of Columns is not known at compile time, I used the IndexedColumn class by Thomas Broyer (Create GWT CellTable Dynamically) as referred to by Juan Pablo Gardella (https://groups.google.com/forum/?fromgroups=#!topic/google-web-toolkit/v6vZT0eUQKU). Here is my test code:
package com.google.gwt.examples.view.client;
import com.google.gwt.cell.client.TextCell;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.TextHeader;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.view.client.ListDataProvider;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ListDataProviderExample implements EntryPoint {
String[][] rowsA = {{"aaaaa","bbbbb","ccccc"},
{"111", "222", "333"}, {"A", "B", "C"}};
public void onModuleLoad() {
// Create a CellTable.
CellTable<List<String>> table = new CellTable<List<String>>();
// Get the rows as List
int nrows = rowsA.length;
int ncols = rowsA[0].length;
ArrayList rowsL = new ArrayList(nrows);
//List rowsL = new ArrayList(nrows);
for (int irow = 0; irow < nrows; irow++) {
List<String> rowL = Arrays.asList(rowsA[irow]);
rowsL.add(rowL);
}
// Create table columns
for (int icol = 0; icol < ncols; icol++) {
table.addColumn(new IndexedColumn(icol),
new TextHeader(rowsA[0][icol]));
}
// Create a list data provider.
final ListDataProvider<List<String>> dataProvider
= new ListDataProvider<List<String>>(rowsL);
// Add the table to the dataProvider.
dataProvider.addDataDisplay(table);
// Add the widgets to the root panel.
VerticalPanel vPanel = new VerticalPanel();
vPanel.add(table);
RootPanel.get().add(vPanel);
}
}
class IndexedColumn extends Column<List<String>, String> {
private final int index;
public IndexedColumn(int index) {
super(new TextCell());
this.index = index;
}
#Override
public String getValue(List<String> object) {
return object.get(this.index);
}
}

JavaFX How to change ProgressBar color dynamically?

I was trying to solve my problem with colored progress bars in this thread. The solution was present, but then I ran into another problem: I can't change color dynamically from my code. I want to do it right from my code, not with pre-defined .css. Generally I can do it, but I run into some difficulties when I try to do it with more than one progess bar.
public class JavaFXApplication36 extends Application {
#Override
public void start(Stage primaryStage) {
AnchorPane root = new AnchorPane();
ProgressBar pbRed = new ProgressBar(0.4);
ProgressBar pbGreen = new ProgressBar(0.6);
pbRed.setLayoutY(10);
pbGreen.setLayoutY(30);
pbRed.setStyle("-fx-accent: red;"); // line (1)
pbGreen.setStyle("-fx-accent: green;"); // line (2)
root.getChildren().addAll(pbRed, pbGreen);
Scene scene = new Scene(root, 150, 50);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
}
I always get 2 red progressbars with it! It seems that code in line (1) changes the style of ProgressBar class, not the instance.
Another strange moment is that deleting line (1) don't result in 2 green progress bars. So I can figure that line (2) is completely useless!! WHY?! That's definitely getting odd.
Is there any way to set different colors for separate progressbars?
See also the StackOverflow JavaFX ProgressBar Community Wiki.
There is a workaround you can use until a bug to fix the sample code in your question is filed and fixed.
The code in this answer does a node lookup on the ProgressBar contents, then dynamically modifies the bar colour of the progress bar to any value you like.
import javafx.application.Application;
import javafx.beans.value.*;
import javafx.geometry.Pos;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class ProgressBarDynamicColor extends Application {
public static void main(String[] args) { launch(args); }
#Override public void start(Stage stage) {
PickedColorBar aquaBar = new PickedColorBar(0.4, Color.AQUA);
PickedColorBar fireBar = new PickedColorBar(0.6, Color.FIREBRICK);
HBox layout = new HBox(20);
layout.getChildren().setAll(aquaBar, fireBar);
layout.setStyle("-fx-background-color: -fx-box-border, cornsilk; -fx-padding: 15;");
stage.setScene(new Scene(layout));
stage.show();
aquaBar.wasShown();
fireBar.wasShown();
}
class PickedColorBar extends VBox {
private final ProgressBar bar;
private final ColorPicker picker;
private boolean wasShownCalled = false;
final ChangeListener<Color> COLOR_LISTENER = new ChangeListener<Color>() {
#Override public void changed(ObservableValue<? extends Color> value, Color oldColor, Color newColor) {
setBarColor(bar, newColor);
}
};
public PickedColorBar(double progress, Color initColor) {
bar = new ProgressBar(progress);
picker = new ColorPicker(initColor);
setSpacing(10);
setAlignment(Pos.CENTER);
getChildren().setAll(bar, picker);
}
// invoke only after the progress bar has been shown on a stage.
public void wasShown() {
if (!wasShownCalled) {
wasShownCalled = true;
setBarColor(bar, picker.getValue());
picker.valueProperty().addListener(COLOR_LISTENER);
}
}
private void setBarColor(ProgressBar bar, Color newColor) {
bar.lookup(".bar").setStyle("-fx-background-color: -fx-box-border, " + createGradientAttributeValue(newColor));
}
private String createGradientAttributeValue(Color newColor) {
String hsbAttribute = createHsbAttributeValue(newColor);
return "linear-gradient(to bottom, derive(" + hsbAttribute+ ",30%) 5%, derive(" + hsbAttribute + ",-17%))";
}
private String createHsbAttributeValue(Color newColor) {
return
"hsb(" +
(int) newColor.getHue() + "," +
(int) (newColor.getSaturation() * 100) + "%," +
(int) (newColor.getBrightness() * 100) + "%)";
}
}
}
The code uses inlined string processing of css attributes to manipulate Region backgrounds. Future JavaFX versions (e.g. JDK8+) will include a public Java API to manipulate background attributes, making obsolete the string processing of attributes from the Java program.
Sample program output:

Cannot successfully add item to beginning of CategoryAxis series for a JavaFX BarChart

I've attempted to implement drag-left and drag-right functionality for a JavaFX BarChart. The drag-left functionality works correct where I add an item at the end of the x-axis data series and remove an item at the beginning.
However, the drag-right functionality does not work as expected. It's supposed to add an item at the beginning of the x-axis series and remove an item at the end. The removal works correctly, but the add operation results in the item being added to the end of the BarChart instead of the beginning.
I suspect it has to do with the use of a CategoryAxis that does not recognize the need to update the BarChart from the ObservableList in a sorted manner (even though I can see that the item gets added to the beginning of the underlying ObservableList).
Also, please let me know if there is a better way of implementing the drag operation.
Below is a full application to reproduce the problem.
import java.text.SimpleDateFormat;
import java.util.Date;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class BarchartProblem extends Application {
private static SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
private double dragStartX = 0;
private long minTime;
private long maxTime;
#FXML
private BarChart<String,Number> histogramBarChart;
#Override
public void start(Stage primaryStage) {
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
xAxis.setLabel("Time");
yAxis.setLabel("Entries");
histogramBarChart = new BarChart<String, Number>(xAxis, yAxis);
final XYChart.Series series1 = new XYChart.Series();
histogramBarChart.setLegendVisible(false);
histogramBarChart.getData().addAll(series1);
histogramBarChart.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
System.out.println(mouseEvent);
dragStartX = mouseEvent.getX();
}
});
histogramBarChart.setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if (dragStartX > mouseEvent.getX()) {
XYChart.Series series = histogramBarChart.getData().get(0);
minTime += 60000;
maxTime += 60000;
series.getData().add(new XYChart.Data(dateFormat.format(new Date(maxTime)), 0));
series.getData().remove(0);
System.out.println("Drag Left");
} else if (dragStartX < mouseEvent.getX()) {
XYChart.Series series = histogramBarChart.getData().get(0);
minTime -= 60000;
maxTime -= 60000;
series.getData().add(0, new XYChart.Data(dateFormat.format(new Date(minTime)), 0));
series.getData().remove(series.getData().size()-1);
System.out.println("Drag Right");
}
}
});
final long currentTime = System.currentTimeMillis();
minTime = currentTime-60000*30;
maxTime = currentTime;
for (long i = minTime; i <= maxTime; i += 60000) {
series1.getData().add(new XYChart.Data(dateFormat.format(new Date(i)), Math.random()*500));
}
StackPane root = new StackPane();
root.getChildren().add(histogramBarChart);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("BarChart Problem");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Android 3.1, trouble displaying two counters side by side

Im new to programming and I am having a hard time trying to figure this one out. I'm trying to create two visible separate counters on each side of the tablet. One is the the left of the tablet, the other on the right of the tablet. When i click the left button it updates the count on the left(e.g., 1+1+1 etc) but when I click on the right counter, it adds an additional value to sum up on the left counter. (e.g., click on right (adds 1, then when i click on the left counter it acts such as add 2, instead of 1.)
here is what my code looks like so far
import android.app.Activity;
import android.os.Bundle;
import android.content.Intent;
import android.widget.Button;
import android.widget.EditText;
import android.view.View;
import android.view.View.OnClickListener;
public class swim2 extends Activity {
// References to UI views
EditText txtCount;
EditText txtCount2;
Button PUp;
Button NUp;
static int Count = 0; // Initial count
static int Count2 = 0;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
setContentView(R.layout.main3);
// TODO Auto-generated method stub
Button previous = (Button) findViewById(R.id.button4);
previous.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Intent myIntent = new Intent(view.getContext(), swim1.class);
startActivityForResult(myIntent, 0);
}
});
// Retrieve references to UI views by their id in XML layout
NUp = (Button)findViewById(R.id.incremintationbutton2);
txtCount2 = (EditText)findViewById(R.id.ni);
txtCount2.setText(String.valueOf(Count2)); //Set initial value
NUp = (Button)findViewById(R.id.incremintationbutton2);
// Process the button on-click event
NUp.setOnClickListener(new OnClickListener() {
public void onClick(View Button) {
Count++;
txtCount2.setText(String.valueOf(Count2));
}
});
PUp = (Button)findViewById(R.id.incremintationbutton1);
txtCount = (EditText)findViewById(R.id.pi);
txtCount.setText(String.valueOf(Count)); // Set initial value
PUp = (Button)findViewById(R.id.incremintationbutton1);
PUp.setOnClickListener(new OnClickListener() {
public void onClick(View Button) {
Count++;
txtCount.setText(String.valueOf(Count));
}
});
}
}
Fixed it, had to change one of the count++, to count2++.. took me a while but i figured it out after a cup of coffee.