JavaFX TableView - New Values in new Cell in same Row - java

I've got following problem:
I styled a TableViews Cells into Rectangles. Now I want to add Objects with an X and Y Coordinate in it (obj.getAblaufX() and obj.getAblaufY()).
How can I add them correctly in JavaFX TableView?
My Current Code:
ObservableList<Event> eventList = loadEvents();
TableView<Event> tableView = new TableView<Event>();
tableView.getSelectionModel().setCellSelectionEnabled(true);
tableView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
tableView.setItems(eventList);
for(int i = 0; i < 10; i++) {
TableColumn<Event, String> tmpCol = new TableColumn<>();
tmpCol.setId(Integer.toString(i));
tmpCol.setMaxWidth(40);
tmpCol.setCellValueFactory(cellData -> new ObservableValue<String>() {
#Override
public void addListener(InvalidationListener listener) {
// TODO Auto-generated method stub
}
#Override
public void removeListener(InvalidationListener listener) {
// TODO Auto-generated method stub
}
#Override
public void addListener(ChangeListener<? super String> listener) {
// TODO Auto-generated method stub
}
#Override
public void removeListener(ChangeListener<? super String> listener) {
// TODO Auto-generated method stub
}
#Override
public String getValue() {
if(Integer.parseInt(tmpCol.getId()) == cellData.getValue().getAblaufX()) {
return cellData.getValue().getAbteilung();
} else {
return "";
}
}
});
tableView.getColumns().add(tmpCol);
}
Result:
But:
Object SP1 has X = 0 and Y = 0
Object SP2 has X = 1 and Y = 0
So, how to fix this Problem?

By using Event as item type, you pervent the TableView from displaying more than one Event per row. Use Integer as item type instead and store the values in a ObservableMap<TablePos, Event> where TablePos is a class containing the x and y coordinates:
public final class TablePos {
private final int x;
private final int y;
public TablePos(int x, int y) {
this.x = x;
this.y = y;
}
#Override
public int hashCode() {
int hash = 7;
hash = 17 * hash + this.x;
hash = 17 * hash + this.y;
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof TablePos)) {
return false;
}
final TablePos other = (TablePos) obj;
return (this.x == other.x && this.y == other.y);
}
}
public class Event {
private final StringProperty name;
private final IntegerProperty x;
private final IntegerProperty y;
public Event(String name, int x, int y) {
// set beans for properties here to make this instance accessible to listeners
this.y = new SimpleIntegerProperty(this, "y", y);
this.x = new SimpleIntegerProperty(this, "x", x);
this.name = new SimpleStringProperty(this, "name", name);
}
public final String getName() {
return this.name.get();
}
public final void setName(String value) {
this.name.set(value);
}
public final StringProperty nameProperty() {
return this.name;
}
public final int getX() {
return this.x.get();
}
public final void setX(int value) {
this.x.set(value);
}
public final IntegerProperty xProperty() {
return this.x;
}
public final int getY() {
return this.y.get();
}
public final void setY(int value) {
this.y.set(value);
}
public final IntegerProperty yProperty() {
return this.y;
}
}
private static void put(Map<TablePos, Event> map, Event evt) {
map.put(new TablePos(evt.getX(), evt.getY()), evt);
}
#Override
public void start(Stage primaryStage) {
ObservableMap<TablePos, Event> contents = FXCollections.observableHashMap();
TableView<Integer> tableView = new TableView<>(FXCollections.observableArrayList());
TableColumn columnGroup = new TableColumn("Heutige Termine");
ChangeListener<Number> xChangeListener = (observable, oldValue, newValue) -> {
Event evt = (Event) ((Property) observable).getBean();
TablePos oldPos = new TablePos(oldValue.intValue(), evt.getY());
TablePos newPos = new TablePos(newValue.intValue(), evt.getY());
contents.remove(oldPos);
contents.put(newPos, evt);
};
ChangeListener<Number> yChangeListener = (observable, oldValue, newValue) -> {
Event evt = (Event) ((Property) observable).getBean();
TablePos oldPos = new TablePos(evt.getX(), oldValue.intValue());
TablePos newPos = new TablePos(evt.getX(), newValue.intValue());
contents.remove(oldPos);
contents.put(newPos, evt);
};
contents.addListener((MapChangeListener.Change<? extends TablePos, ? extends Event> change) -> {
if (change.wasRemoved()) {
Event evt = change.getValueRemoved();
evt.xProperty().removeListener(xChangeListener);
evt.yProperty().removeListener(yChangeListener);
}
if (change.wasAdded()) {
Event evt = change.getValueAdded();
evt.xProperty().addListener(xChangeListener);
evt.yProperty().addListener(yChangeListener);
}
});
// items denote the y coordinate
for (int i = 0; i < 10; i++) {
tableView.getItems().add(i);
}
// one column per x coordiante
for (int i = 0; i < 10; i++) {
final int index = i;
TableColumn<Integer, String> column = new TableColumn<>();
// take value from Map using x (index) and y (item value)
column.setCellValueFactory(cd
-> Bindings.selectString(
Bindings.valueAt(contents, new TablePos(index, cd.getValue())), "name"));
columnGroup.getColumns().add(column);
}
tableView.getColumns().add(columnGroup);
Event opTarget = new Event("Something", 2, 9);
put(contents, new Event("SP1", 0, 0));
put(contents, new Event("SP2", 1, 0));
put(contents, opTarget);
Button move = new Button("move");
move.setOnAction(evt -> opTarget.setX(9 - opTarget.getX()));
Button rename = new Button("rename");
rename.setOnAction(evt -> opTarget.setName(opTarget.getName().equals("42") ? "Answer" : "42"));
Scene scene = new Scene(new VBox(10, tableView, move, rename));
primaryStage.setScene(scene);
primaryStage.show();
}
Note that this requires you to know the number of rows/columns at the initialisation (I used 10 for both for simplicity). If this is not the case, you could add a listener to the ObservableMap that dynamically adds/removes rows / columns.

Related

JButton ActionListener performs if- and else- statement

I am trying to program a basic Nine Men's Morris game in Java for school.
The GUI consists of 24 JButtons. The first Button, that is clicked, is supposed to set the starting position of a move, the second button the destination. I tried to create a bool and set it to true after a button was clicked once, and check, whether that boolean is true or false to determine, whether the Button should set the start or the destination.
However, when I tried it like that and printed Start and Dest to the console, they both were set to 0,1,0. I am fairly new to Java, so there might be quite some bad practice going on right there.
JButton button010 = new JButton("");
button010.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(!targ) {
setStart(0,1,0);
targ = true;
} else {
setDest(0,1,0);
targ = false;
}
}
});
button010.setOpaque(false);
button010.setContentAreaFilled(false);
button010.setBorderPainted(false);
button010.setBounds(222, 11, 47, 44);
contentPane.add(button010);
This is not an answer to you question, only a suggestion for a better design to remove the hardcoding in your listener code.
Why do you call your Boolean variable "targ". Since you have "start" and "dest" methods why are you creating a third variable name? I would suggest a better name is "start" so you know it is related to your start/dest methods. It would default to true, since that is the first value you are trying to set when you click a button.
So your custom Action might be something like:
class GameAction action = new Action()
{
private int value1;
private int value2;
private int value3;
public GameAction(int value1, int value2, int value3)
{
this.value1 = value1;
this.value2 = value2;
this.value3 = value3;
}
#Override
public void actionPerformed(ActionEvent e)
{
if (start)
{
setStart(value1, value2, value3);
start = false;
}
else
{
setDest(value1, value2, value3);
start = true;
}
}
You would use more appropriate variable names.
Now all your logic is in a single class and if you ever need to change the logic it is contained in one place instead of 24 custom listeners.
Then in your code when you create the button you would do:
JButton button010 = new JButton("");
button010.addActionListener( new GameAction(0, 1, 0) );
Of course it would be even better to create your 24 buttons in a loop so you don't have to hardcode the parameters.
Not a real or full answer since your question is currently incomplete and cannot be answered as currently stated, but I'm testing this code out on how to create a 9 Man's Morris Grid using Swing, using layout managers, and with separation of the model from the GUI. I have created a 7 x 7 grid of JLabel and added it to a JPanel that uses GridLayout(7, 7). If the grid position matches a cell position in the 9 man's morris, then a grid cell is created, with mouse listener and all.
The following code creates this GUI:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.*;
#SuppressWarnings("serial")
public class MorrisPanel extends JPanel {
private static final int ICON_WIDTH = 80;
private static final int SMALL_OVAL = 10;
private static final int LARGE_OVAL = 40;
private static final int GRID_SIDES = 7;
private MorrisGameGrid game = new MorrisGameGrid();
private Map<MorrisCell, JLabel> cellLabelMap = new HashMap<>();
private Map<JLabel, MorrisCell> labelCellMap = new HashMap<>();
private Icon blankIcon;
private Icon cellIcon;
private Icon whiteIcon;
private Icon blackIcon;
public MorrisPanel() {
blankIcon = createIcon(null, 0);
cellIcon = createIcon(Color.DARK_GRAY, SMALL_OVAL);
whiteIcon = createIcon(Color.WHITE, LARGE_OVAL);
blackIcon = createIcon(Color.DARK_GRAY, LARGE_OVAL);
setLayout(new GridLayout(GRID_SIDES, GRID_SIDES));
MyMouse myMouse = new MyMouse();
List<MorrisColumn> columns = Arrays.asList(MorrisColumn.values());
Collections.reverse(columns);
for (MorrisColumn column : columns) {
for (MorrisRow row : MorrisRow.values()) {
MorrisCell cell = new MorrisCell(column, row);
JLabel label = new JLabel(blankIcon);
if (game.contains(cell)) {
label.setIcon(cellIcon);
label.addMouseListener(myMouse);
cellLabelMap.put(cell, label);
labelCellMap.put(label, cell);
}
add(label);
}
}
}
private Icon createIcon(Color color, int size) {
int w = ICON_WIDTH;
int h = ICON_WIDTH;
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
if (color != null) {
Graphics2D g2 = img.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(color);
int x = (ICON_WIDTH - size) / 2;
int y = x;
int width = size;
int height = size;
g2.fillOval(x, y, width, height);
g2.setColor(Color.BLACK);
g2.drawOval(x, y, width, height);
g2.dispose();
}
return new ImageIcon(img);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (MorrisEdge edge : game.getEdges()) {
JLabel l1 = cellLabelMap.get(edge.getCell1());
JLabel l2 = cellLabelMap.get(edge.getCell2());
int x1 = l1.getLocation().x + l1.getWidth() / 2;
int x2 = l2.getLocation().x + l2.getWidth() / 2;
int y1 = l1.getLocation().y + l1.getHeight() / 2;
int y2 = l2.getLocation().y + l2.getHeight() / 2;
g2.drawLine(x1, y1, x2, y2);
}
}
private class MyMouse extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
JLabel source = (JLabel) e.getSource();
MorrisCell cell = labelCellMap.get(source);
// TODO: Begin testing block -- delete this
System.out.println(cell); // !! just for testing purposes
Icon icon = source.getIcon();
if (icon == cellIcon) {
source.setIcon(blackIcon);
} else if (icon == blackIcon) {
source.setIcon(whiteIcon);
} else if (icon == whiteIcon) {
source.setIcon(cellIcon);
}
// TODO: end testing block
// TODO: finish
}
}
private static void createAndShowGui() {
MorrisPanel mainPanel = new MorrisPanel();
JFrame frame = new JFrame("TestButtons");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class MorrisGame {
private MorrisGameGrid grid = new MorrisGameGrid();
// TODO: logic for game goes here
public MorrisGameGrid getGrid() {
return grid;
}
}
class MorrisGameGrid {
Set<MorrisCell> cells = new HashSet<>();
Set<MorrisEdge> edges = new HashSet<>();
public MorrisGameGrid() {
cells.add(new MorrisCell("a1"));
cells.add(new MorrisCell("d1"));
cells.add(new MorrisCell("g1"));
cells.add(new MorrisCell("b2"));
cells.add(new MorrisCell("d2"));
cells.add(new MorrisCell("f2"));
cells.add(new MorrisCell("c3"));
cells.add(new MorrisCell("d3"));
cells.add(new MorrisCell("e3"));
cells.add(new MorrisCell("a4"));
cells.add(new MorrisCell("b4"));
cells.add(new MorrisCell("c4"));
cells.add(new MorrisCell("e4"));
cells.add(new MorrisCell("f4"));
cells.add(new MorrisCell("g4"));
cells.add(new MorrisCell("c5"));
cells.add(new MorrisCell("d5"));
cells.add(new MorrisCell("e5"));
cells.add(new MorrisCell("b6"));
cells.add(new MorrisCell("d6"));
cells.add(new MorrisCell("f6"));
cells.add(new MorrisCell("a7"));
cells.add(new MorrisCell("d7"));
cells.add(new MorrisCell("g7"));
connectLoop(getCell("a1"), getCell("d1"), getCell("g1"),
getCell("g4"), getCell("g7"), getCell("d7"),
getCell("a7"), getCell("a4"));
connectLoop(getCell("b2"), getCell("d2"), getCell("f2"),
getCell("f4"), getCell("f6"), getCell("d6"),
getCell("b6"), getCell("b4"));
connectLoop(getCell("c3"), getCell("d3"), getCell("e3"),
getCell("e4"), getCell("e5"), getCell("d5"),
getCell("c5"), getCell("c4"));
connectEdge(getCell("d1"), getCell("d2"), getCell("d3"));
connectEdge(getCell("d7"), getCell("d6"), getCell("d5"));
connectEdge(getCell("a4"), getCell("b4"), getCell("c4"));
connectEdge(getCell("e4"), getCell("f4"), getCell("g4"));
}
public boolean contains(MorrisCell cell) {
return cells.contains(cell);
}
public Set<MorrisCell> getCells() {
return cells;
}
public Set<MorrisEdge> getEdges() {
return edges;
}
private void connectCells(MorrisCell cell1, MorrisCell cell2) {
cell1.addNeighbors(cell2);
cell2.addNeighbors(cell1);
edges.add(new MorrisEdge(cell1, cell2));
}
private void connectEdge(MorrisCell... cells) {
for (int i = 0; i < cells.length - 1; i++) {
connectCells(cells[i], cells[i + 1]);
}
}
private void connectLoop(MorrisCell... cells) {
connectEdge(cells);
connectCells(cells[0], cells[cells.length - 1]);
}
public MorrisCell getCell(String text) {
if (text == null || text.length() != 2) {
String errorTxt = "For text: " + text;
throw new IllegalArgumentException(errorTxt);
}
MorrisCell temp = new MorrisCell(text);
for (MorrisCell morrisCell : cells) {
if (morrisCell.equals(temp)) {
return morrisCell;
}
}
return null;
}
}
class MorrisEdge {
private MorrisCell cell1;
private MorrisCell cell2;
public MorrisEdge(MorrisCell cell1, MorrisCell cell2) {
if (cell1.compareTo(cell2) < 0) {
this.cell1 = cell1;
this.cell2 = cell2;
} else {
this.cell2 = cell1;
this.cell1 = cell2;
}
}
public MorrisCell getCell1() {
return cell1;
}
public MorrisCell getCell2() {
return cell2;
}
#Override
public int hashCode() {
int result = ((cell1 == null) ? 0 : cell1.hashCode());
result += ((cell2 == null) ? 0 : cell2.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MorrisEdge other = (MorrisEdge) obj;
boolean e1 = compareCells(cell1, other.cell1);
e1 &= compareCells(cell2, other.cell2);
boolean e2 = compareCells(cell1, other.cell2);
e2 &= compareCells(cell2, other.cell1);
return e1 || e2;
}
private static boolean compareCells(MorrisCell c1, MorrisCell c2) {
if (c1 == null) {
if (c2 != null)
return false;
} else if (!c1.equals(c2))
return false;
return true;
}
#Override
public String toString() {
return "Edge [" + cell1 + ", " + cell2 + "]";
}
}
class MorrisCell implements Comparable<MorrisCell> {
private MorrisColumn column;
private MorrisRow row;
private Set<MorrisCell> neighbors = new HashSet<>();
private MorrisPlayer player = null;
public MorrisCell(MorrisColumn column, MorrisRow row) {
this.column = column;
this.row = row;
}
public MorrisCell(String text) {
if (text.length() != 2) {
String errorTxt = "For text: " + text;
throw new IllegalArgumentException(errorTxt);
}
String columnTxt = text.substring(0, 1).toLowerCase();
String rowTxt = text.substring(1, 2);
MorrisColumn column1 = null;
MorrisRow row1 = null;
for (MorrisColumn c : MorrisColumn.values()) {
if (c.getText().equals(columnTxt)) {
column1 = c;
break;
}
}
for (MorrisRow r : MorrisRow.values()) {
if (r.getText().equals(rowTxt)) {
row1 = r;
break;
}
}
if (column1 == null || row1 == null) {
String errorTxt = "For text: " + text;
throw new IllegalArgumentException(errorTxt);
}
this.column = column1;
this.row = row1;
}
public void addNeighbors(MorrisCell neighbor) {
neighbors.add(neighbor);
}
public boolean isNeighbor(MorrisCell possibleNeighbor) {
return neighbors.contains(possibleNeighbor);
}
public MorrisRow getRow() {
return row;
}
public MorrisColumn getColumn() {
return column;
}
public void setPlayer(MorrisPlayer player) {
this.player = player;
}
public MorrisPlayer getPlayer() {
return player;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((column == null) ? 0 : column.hashCode());
result = prime * result + ((row == null) ? 0 : row.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MorrisCell other = (MorrisCell) obj;
if (column != other.column)
return false;
if (row != other.row)
return false;
return true;
}
#Override
public String toString() {
return column.getText() + row.getText();
}
#Override
public int compareTo(MorrisCell o) {
int colCompare = column.compareTo(o.column);
int rowCompare = row.compareTo(o.row);
return colCompare != 0 ? colCompare : rowCompare;
}
}
enum MorrisRow {
ONE("1"), TWO("2"), THREE("3"), FOUR("4"), FIVE("5"), SIX("6"), SEVEN("7");
private String text;
private MorrisRow(String text) {
this.text = text;
}
public String getText() {
return text;
}
}
enum MorrisColumn {
A("a"), B("b"), C("c"), D("d"), E("e"), F("f"), G("g");
private String text;
private MorrisColumn(String text) {
this.text = text;
}
public String getText() {
return text;
}
}
enum MorrisPlayer {
WHITE("White"), BLACK("Black");
private String text;
private MorrisPlayer(String text) {
this.text = text;
}
public String getText() {
return text;
}
}

JavaFX: How to remember scroll location of tabcontent after refreshing content ?

I am implmenting a file browser using treeview in JavaFX. And i set the treeview in Tab as conent. As i refresh elements of treeview using scheduled threads, i am unable to scroll to previously selected tree item. Here is the minimal version of my implmentation.
/**
*
* #author nika
*/
public class TabTreeView extends Application implements Runnable {
private TreeItem<String> root;
private TreeView<String> tv = new TreeView<>();
private static ArrayList<NumberPair> rememberExpanded = new ArrayList<>();
private TreeItem<String> previouslySelectedTreeItem;
#Override
public void run() {
root = new TreeItem<>("Root");
root.setExpanded(true);
for (int i = 0; i < 10; i++) {
TreeItem<String> sublevel = new TreeItem<>("Level : " + i + " TS: " + System.currentTimeMillis());
final int level = i;
if (rememberExpanded.contains(new NumberPair(level, -9999))) {
sublevel.setExpanded(true);
}
sublevel.expandedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (newValue) {
rememberExpanded.add(new NumberPair(level, -9999));
} else {
rememberExpanded.remove(new NumberPair(level, -9999));
}
}
});
for (int j = 0; j < 5; j++) {
TreeItem<String> subsublevel = new TreeItem<>("SubLevel : " + j + " # " + i + " TS:" + System.currentTimeMillis());
final int level2 = j;
if (rememberExpanded.contains(new NumberPair(level, level2))) {
sublevel.setExpanded(true);
}
subsublevel.expandedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (newValue) {
rememberExpanded.add(new NumberPair(level, level2));
} else {
rememberExpanded.remove(new NumberPair(level, level2));
}
}
});
sublevel.getChildren().add(subsublevel);
}
root.getChildren().add(sublevel);
}
Platform.runLater(new Runnable() {
#Override
public void run() {
tv.setRoot(root);
//scroll to previously selected item
//i know this one is wrong 1. "as it is not working", 2. "because it will only look into children of root not grandchildren".
tv.scrollTo(root.getChildren().indexOf(previouslySelectedTreeItem));
}
});
}
#Override
public void start(Stage primaryStage) throws Exception {
TabPane tabpane = new TabPane();
tv.selectionModelProperty().addListener(new ChangeListener<MultipleSelectionModel<TreeItem<String>>>() {
#Override
public void changed(ObservableValue<? extends MultipleSelectionModel<TreeItem<String>>> observable, MultipleSelectionModel<TreeItem<String>> oldValue, MultipleSelectionModel<TreeItem<String>> newValue) {
previouslySelectedTreeItem = newValue.getSelectedItem();
}
});
Tab t = new Tab("Demo", tv);
tabpane.getTabs().add(t);
BorderPane bp = new BorderPane(tabpane);
primaryStage.setScene(new Scene(bp));
primaryStage.show();
ScheduledExecutorService exc = Executors.newScheduledThreadPool(1);
exc.scheduleAtFixedRate(this, 0, 2, TimeUnit.SECONDS);
}
public static void main(String[] args) throws Exception {
launch(TabTreeView.class, args);
}
private class NumberPair {
int first, second;
public NumberPair(int first, int second) {
this.first = first;
this.second = second;
}
public int getFirst() {
return first;
}
public void setFirst(int first) {
this.first = first;
}
public int getSecond() {
return second;
}
public void setSecond(int second) {
this.second = second;
}
#Override
public int hashCode() {
int hash = 3;
hash = 37 * hash + this.first;
hash = 37 * hash + this.second;
return hash;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final NumberPair other = (NumberPair) obj;
if (this.first != other.first) {
return false;
}
if (this.second != other.second) {
return false;
}
return true;
}
#Override
public String toString() {
return "NumberPair{" + "first=" + first + ", second=" + second + '}';
}
}
}
Is there anyway to remember to scroll location of tabcontent or treeview? So that i can restore the scroll position after refreshing the view.
In this implementation i have added a NumberPair class to remember expanded items.
You can find ScrollBar reference of TreeView by styleclass. Try this.
Platform.runLater(new Runnable() {
#Override
public void run() {
double position[] = new double[]{0.0d};
Optional<ScrollBar> scroll = findScrollBar(tv, Orientation.VERTICAL);
scroll.ifPresent(s -> position[0] = s.getValue());
tv.setRoot(root);
scroll.ifPresent(s -> s.setValue(position[0]));
}
});
private Optional<ScrollBar> findScrollBar(TreeView<String> from, Orientation orientation) {
Set<Node> nodes = from.lookupAll(".scroll-bar");
return nodes.stream()
.filter(node -> node instanceof ScrollBar && ((ScrollBar)node).getOrientation() == orientation)
.map(node -> ((ScrollBar)node))
.findFirst();
}
But note that, trying to keep same items in TreeItem might be good approach. TreeView is desiened to keep selections expansions and scroll position as long as you add and remove items from unique root.

Making the EventHandler perform two different actions

I have this assignment where we must create a game similar to Candy Crush using model view separation. I have a 5x5 GridLayout with JButtons in each position on the grid. Each JButton has a random color icon called with a method from the model. Clicking on one of the JButtons must change the border of the button red and then clicking an adjacent button must swap the contents of the buttons, or the colors. I have a representation of a 5x5 grid in my model using ArrayList<ArrayList<String>> with the string being the file name used for the ImageIcon. However, I'm having trouble with the swapping portion. When I click on the first JButton, I can make the border red but clicking on another adjacent JButton highlights that button red as well and doesn't swap the colors. I'm not sure why my EventHandler isn't working. Could anyone help me?
This is my Model class:
private ArrayList<String> _imageFileNames;
private ArrayList<String> _store;
private ArrayList<ArrayList<String>> _currentValues;
private int _firstX;
private int _firstY;
public Model() {
_imageFileNames = new ArrayList<String>();
_imageFileNames.add("Tile-0.png");
_imageFileNames.add("Tile-1.png");
_imageFileNames.add("Tile-2.png");
_imageFileNames.add("Tile-3.png");
_imageFileNames.add("Tile-4.png");
_imageFileNames.add("Tile-5.png");
_currentValues = new ArrayList<ArrayList<String>>();
for (int x=0; x<5; ++x) {
_store= new ArrayList<String>();
for (int y=0; y<5; ++y) {
Collections.shuffle(_imageFileNames);
_store.add(_imageFileNames.get(0));
}
_currentValues.add(_store);
}
}
public String setIcon(int x, int y) {
return _currentValues.get(x).get(y);
}
public void firstClick(int x, int y) {
_firstX = x;
_firstY = y;
}
public void secondClick(int x, int y) {
if (checkXAdjacent(x) || checkYAdjacent(y)) {
String first = _currentValues.get(x).get(y);
String second = _currentValues.get(_firstX).get(_firstY);
String temp = _currentValues.get(x).get(y);
first = _currentValues.get(_firstX).get(_firstY);
second = temp;
}
}
public boolean checkXAdjacent(int x) {
if (_firstX != x) {
if (x == _firstX + 1) {
return true;
}
else if (x == _firstX - 1) {
return true;
}
else {
return false;
}
}
else {
return false;
}
}
public boolean checkYAdjacent(int y) {
if (_firstY != y) {
if (y == _firstY + 1) {
return true;
}
if (y == _firstY - 1) {
return true;
}
else {
return false;
}
}
else {
return false;
}
}
This is my View class:
private JFrame _frame;
private Model _model;
public ArrayList<JButton> _buttons;
public UI() {
_model = new Model();
_frame = new JFrame("James Kang's Lab 10");
_frame.getContentPane().setLayout(new GridLayout(5, 5));
_buttons = new ArrayList<JButton>();
for (int x=0; x<5; ++x) {
for (int y=0; y<5; ++y) {
JButton button = new JButton();
button.setIcon(new ImageIcon("Images/" + _model.setIcon(x,y)));
button.addActionListener(new EventHandler(_model, null, button, x, y));
button.setOpaque(true);
_buttons.add(button);
_frame.add(button);
}
}
_frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
_frame.pack();
_frame.setVisible(true);
}
public void update() {
for (int x=0; x<5; ++x) {
for (int y=0; y<5; ++y) {
_buttons.get(y).setIcon(new ImageIcon(_model.setIcon(x, y)));
}
}
}
And this is my EventHandler:
private Model _model;
private JButton _b;
private int _x;
private int _y;
private int _clicked;
private UI _ui;
public EventHandler(Model m, UI ui, JButton b, int x, int y) {
_model = m;
_b = b;
_x = x;
_y = y;
_clicked = 0;
_ui = ui;
}
#Override public void actionPerformed(ActionEvent e) {
if (_clicked == 0) {
_b.setBackground(Color.RED);
_model.firstClick(_x, _y);
_clicked = 1;
}
else if (_clicked == 1) {
_b.setBackground(null);
_model.secondClick(_x, _y);
_ui.update();
_clicked = 0;
}
}

javafx-2 tableview implementing custom column resize policy

In my experience CONSTRAINED_RESIZE_POLICY is broken if the TableView contains some fixed width columns unless I am doing something wrong here:
public class JavaFX2Sandbox extends Application
{
public static void main(String[] args)
{
Application.launch(args);
}
#Override
public void start(Stage stage) throws Exception
{
StackPane root = new StackPane();
root.autosize();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
configureTable(root);
}
public static class Person
{
public Person(String firstName, String lastName)
{
this.fixedValue = "Dr.";
this.firstName = firstName;
this.lastName = lastName;
}
public String fixedValue;
public String firstName;
public String lastName;
}
private static class CellValueFactory<T> implements Callback<TableColumn.CellDataFeatures<T, Object>, ObservableValue<Object>>
{
private String mFieldName;
public CellValueFactory(String fieldName)
{
mFieldName = fieldName;
}
#Override
public ObservableValue call(TableColumn.CellDataFeatures<T, Object> p)
{
try
{
if (p == null)
{
return new SimpleObjectProperty(null);
}
Object o = p.getValue();
if (o == null)
{
return new SimpleObjectProperty(null);
}
Object fieldVal = o.getClass().getField(mFieldName).get(o);
if (fieldVal == null)
{
return new SimpleObjectProperty(null);
}
return new SimpleObjectProperty(fieldVal);
}
catch (Exception ex)
{
return new SimpleObjectProperty(ex);
}
}
}
private void configureTable(StackPane root)
{
TableView<Person> table = new TableView<>();
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
ArrayList<Person> teamMembers = new ArrayList<>();
teamMembers.add(new Person("John", "Big"));
teamMembers.add(new Person("Frank", "Small"));
TableColumn col0 = new TableColumn("fixed-width");
col0.setResizable(false);
col0.setCellValueFactory(new CellValueFactory("fixedValue"));
TableColumn col1 = new TableColumn("First Name");
col1.setCellValueFactory(new CellValueFactory("firstName"));
TableColumn col2 = new TableColumn("Last Name");
col2.setCellValueFactory(new CellValueFactory("lastName"));
table.getColumns().setAll(col0, col1, col2);
table.setItems(FXCollections.observableList(teamMembers));
root.getChildren().add(table);
}
}
So I started to implement my own resize policy which behaves slightly differently from CONSTRAINED_RESIZE_POLICY but I like mine better :-). It is all okay except for the sum of the widths of the columns do not add up to the width of the TableView.
Here is the implementation of my resize policy class:
static class ColumnResizePolicy implements Callback<TableView.ResizeFeatures, Boolean>
{
double mTVWidth;
#Override
public Boolean call(ResizeFeatures arg0)
{
TableView tv = arg0.getTable();
Double tvWidth = tv.widthProperty().getValue();
if (tvWidth == null || tvWidth <= 0.0)
{
return false;
}
if (mTVWidth != tvWidth && arg0.getColumn() == null)
{
mTVWidth = tvWidth;
int numColsToSize = 0;
double fixedColumnsWidths = 0;
for (TableColumn col : new ArrayList<TableColumn>(tv.getColumns()))
{
if (col.isResizable() && col.isVisible())
{
++numColsToSize;
}
else if (col.isVisible())
{
fixedColumnsWidths += col.getWidth();
}
}
if (numColsToSize == 0)
return TableView.UNCONSTRAINED_RESIZE_POLICY.call(arg0);
TableColumn lastCol = null;
for (TableColumn col : new ArrayList<TableColumn>(tv.getColumns()))
{
if (col.isResizable() && col.isVisible())
{
double newWidth = (tvWidth - fixedColumnsWidths) / numColsToSize;
col.setPrefWidth(newWidth);
lastCol = col;
}
}
if (lastCol != null)
{
lastCol.setPrefWidth(lastCol.getPrefWidth()-2);
}
return true;
}
else
{
return TableView.UNCONSTRAINED_RESIZE_POLICY.call(arg0);
}
}
}
And my question (ironically after this long post) is about number 2 in this line:
lastCol.setPrefWidth(lastCol.getPrefWidth()-2);
I assume that the table width gives back the outer width including the border, hence the difference, but how do I get the inner width?
Try next:
double borderWidth = table.getBoundsInLocal().getWidth() - table.getWidth()
Have you tried getting the width of the place holder node?
I have a problem same https://community.oracle.com/message/12334734#12334734
And this implementing custom column resize policy of you was help me.
Thanks you!
Your code.
if (col.isResizable() && col.isVisible()) {
double newWidth = (tvWidth - fixedColumnsWidths) / numColsToSize;
col.setPrefWidth(newWidth);
lastCol = col;
}
I change to
if (col.isResizable() && col.isVisible()) {
double newWidth = (tvWidth - fixedColumnsWidths)*col.getPercentWidth();
col.setPrefWidth(newWidth);
lastCol = col;
}
With col is
public class PTableColumn<S, T> extends javafx.scene.control.TableColumn<S, T>
And getPercentWidth is
private DoubleProperty percentWidth = new SimpleDoubleProperty(1);

GWT editors and get/set value

I have following editor class, and I'm curious what's wrong with it. When running, it does correctly set the right radio button as selected. However, when flushing the top level editor, getValue is never called, and my object's property never get updated. Here's the code (hint - modified ValueListBox):
public class ValueRadioList<T> extends FlowPanel implements
HasConstrainedValue<T>, LeafValueEditor<T>, ValueChangeHandler<Boolean> {
private final List<T> values = new ArrayList<T>();
private final Map<Object, Integer> valueKeyToIndex =
new HashMap<Object, Integer>();
private final String name;
private final Renderer<T> renderer;
private final ProvidesKey<T> keyProvider;
private T value;
public ValueRadioList(Renderer<T> renderer) {
this(renderer, new SimpleKeyProvider<T>());
}
public ValueRadioList(Renderer<T> renderer, ProvidesKey<T> keyProvider) {
super();
this.name = DOM.createUniqueId();
this.keyProvider = keyProvider;
this.renderer = renderer;
}
private void addValue(T value) {
Object key = keyProvider.getKey(value);
if (valueKeyToIndex.containsKey(key)) {
throw new IllegalArgumentException("Duplicate value: " + value);
}
valueKeyToIndex.put(key, values.size());
values.add(value);
RadioButton radio = new RadioButton(name, renderer.render(value));
radio.addValueChangeHandler(this);
add(radio);
assert values.size() == getWidgetCount();
}
#Override public HandlerRegistration addValueChangeHandler(
ValueChangeHandler<T> handler) {
return addHandler(handler, ValueChangeEvent.getType());
}
#Override public T getValue() {
return value;
}
#Override public void onValueChange(ValueChangeEvent<Boolean> event) {
int selectedIndex = -1;
for (int i = 0, l = getWidgetCount(); i < l; i++) {
if (((RadioButton) getWidget(i)).getValue()) {
selectedIndex = i;
break;
}
}
if (selectedIndex < 0) {
return; // Not sure why this happens during addValue
}
T newValue = values.get(selectedIndex);
setValue(newValue, true);
}
#Override public void setAcceptableValues(Collection<T> newValues) {
values.clear();
valueKeyToIndex.clear();
clear();
for (T nextNewValue : newValues) {
addValue(nextNewValue);
}
updateRadioList();
}
#Override public void setValue(T value) {
setValue(value, false);
}
#Override public void setValue(T value, boolean fireEvents) {
if (value == this.value
|| (this.value != null && this.value.equals(value))) {
return;
}
T before = this.value;
this.value = value;
updateRadioList();
if (fireEvents) {
ValueChangeEvent.fireIfNotEqual(this, before, value);
}
}
private void updateRadioList() {
Object key = keyProvider.getKey(value);
Integer index = valueKeyToIndex.get(key);
if (index == null) {
addValue(value);
}
index = valueKeyToIndex.get(key);
((RadioButton) getWidget(index)).setValue(true);
}
}
Solved it, my POJO missed a setter for that field.

Categories