I have a table view that can't fit on one page but when I print it only one page gets printed. What should I do so that the rest of the table is printed on other pages?
Sample code below that fills a table view with 50 rows and prints it (I use foxit as a pdf printer, amend as required). The output pdf only contains one page that ends around row 30 although the TableView shows more than 45 rows.
public class FXPrint extends Application {
public static void main(String[] args) { launch(FXPrint.class); }
#Override
public void start(Stage stage) throws Exception {
TableView<String> table = new TableView<>();
for (int i = 0; i < 25; i++) {
TableColumn<String, String> tc = new TableColumn<>("" + i);
int index = i;
tc.setCellValueFactory(param -> {
String s = param.getValue();
if (s.length() <= index) return new SimpleStringProperty();
else return new SimpleStringProperty(s.substring(index, index + 1));
});
table.getColumns().add(tc);
}
for (int i = 0; i < 50; i++) table.getItems().add(i + "sdfghjklrthyjkcasdghasfdsfgdfgsdfggfdg");
Button print = new Button("print");
print.setOnAction(e -> print(table));
VBox box = new VBox(10, table, print);
VBox.setVgrow(table, Priority.ALWAYS);
stage.setScene(new Scene(box));
stage.setWidth(600);
stage.setHeight(1200);
stage.show();
}
public void print(final TableView<?> table) {
Printer printer = Printer.getAllPrinters().stream().filter(p -> p.getName().contains("Foxit")).findAny().get();
PageLayout pageLayout = printer.createPageLayout(Paper.A4, PageOrientation.PORTRAIT, Printer.MarginType.DEFAULT);
PrinterJob job = PrinterJob.createPrinterJob(printer);
if (job != null) {
job.getJobSettings().setPageLayout(pageLayout);
if (job.printPage(table)) {
job.endJob();
}
}
}
}
You should split your TableView on multiple Nodes according to height of printable area and height of your rows. Then call job.PrintPage for all nodes, and once job.endJob.
Here is my approach for multiple pages printing:
public static List<Node> getLineRecordPages(Collection<LineRecord> lineRecords, PageLayout layout) {
LinkedList<Node> result = new LinkedList<>();
VBox node = null;
double totalHeight = Double.POSITIVE_INFINITY;
for (LineRecord record : lineRecords) {
PrintLineRecordView view = new PrintLineRecordView();
final PrintLineRecordPresenter presenter = (PrintLineRecordPresenter) view.getPresenter();
presenter.bind(record);
final double elementHeight = view.getView().prefHeight(layout.getPrintableWidth());
if (elementHeight + totalHeight > layout.getPrintableHeight()) {
node = new VBox();
node.setMaxWidth(layout.getPrintableWidth());
result.add(node);
totalHeight = 0;
}
totalHeight+=elementHeight;
if (node != null) {
node.getChildren().add(view.getView());
}
}
return result;
}
And actualy sending pages to printing job:
Printer printer = Printer.getDefaultPrinter();
PageLayout pageLayout = printer.getDefaultPageLayout();
PrinterJob printerJob = PrinterJob.createPrinterJob(printer);
final List<Node> pages = Formatter.getLineRecordPages(lineRecords, pageLayout);
final boolean print = printerJob.showPrintDialog(null);
if (print) {
pages.forEach(printerJob::printPage);
printerJob.endJob();
}
Related
I initialized rowSize (number of rows) and colSize (number of columns) inside the MapTest class. I created 3 methods: startStage1(), startStage2() and startStage3(); each is assigned to a button. Each method assigns the rowSize and colSize to a new integer.
I want to be able to the re-size of my GridPane whenever I click a button but it does not works that way.
What could I have done differently?
import javafx.application.Application;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class MapTest extends Application implements EventHandler<KeyEvent> {
static Stage theStage;
static Scene scene1, scene2;
// top box
HBox topBox = new HBox();
// bottom box
HBox bottomBox = new HBox();
// grid dimensions (I'm trying to manipulate these variables)
int rowSize;
int colSize;
int tileSize;
GridPane gridPane;
GridMapT gridMap;
#Override
public void start(Stage firstStage) throws Exception {
theStage = firstStage;
// scene 2 //////////////
Label label2 = new Label("scene 2: choose a stage");
Button stage1_btn = new Button("Room 5x5");
Button stage2_btn = new Button("Room 7x7");
Button stage3_btn = new Button("Room 10x10");
Button button5 = new Button("Exit");
stage1_btn.setOnAction(e -> {
startStage1();
});
stage2_btn.setOnAction(e -> {
startStage2();
});
stage3_btn.setOnAction(e -> {
startStage3();
});
button5.setOnAction(e -> System.exit(0));
// Layout1
VBox layout = new VBox(20);
layout.setAlignment(Pos.CENTER);
layout.getChildren().addAll(label2, stage1_btn, stage2_btn, stage3_btn, button5);
scene1 = new Scene(layout, 800, 600);
// Scene 3 ////////////////////
// top box
Label title = new Label("Map test");
topBox.getChildren().add(title);
// bottom box
Label instruction = new Label("");
bottomBox.getChildren().add(instruction);
// scene 3
BorderPane gameScreen = new BorderPane();
scene2 = new Scene(gameScreen);
// set up gridPane
gridPane = new GridPane();
gridMap = new GridMapT(rowSize, colSize);
for (int x = 0; x < rowSize; x++) {
for (int y = 0; y < colSize; y++) {
String grid = gridMap.getMap()[x][y];
// floor labels
if (grid == "floor") {
Label table = new Label("F");
table.setMinWidth(tileSize);
table.setMinHeight(tileSize);
gridPane.add(table, x, y);
}
// wall labels
if (grid == "wall") {
Label table = new Label("W");
table.setMinWidth(tileSize);
table.setMinHeight(tileSize);
gridPane.add(table, x, y);
}
}
}
////// ////////////////////////////////////////////////////////////////////////
// add a clickable reset and Debug Mode to bottom box
Button resetBtn = new Button();
resetBtn.setText("Quit game");
bottomBox.getChildren().add(resetBtn);
resetBtn.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<Event>() {
#Override
public void handle(Event event) {
try {
restart(firstStage);
} catch (Exception e) {
e.printStackTrace();
}
}
});
// keyboard input
scene2.setOnKeyPressed(this);
// setting up the whole borderPane
gameScreen.setTop(topBox);
gameScreen.setBottom(bottomBox);
gameScreen.setCenter(gridPane);
// set scene1 as start up screen
firstStage.setScene(scene1);
firstStage.show();
}
// restart method
public void restart(Stage stage) throws Exception {
topBox.getChildren().clear();
bottomBox.getChildren().clear();
gridPane.getChildren().clear();
gridMap = new GridMapT(rowSize, colSize);
start(stage);
}
///////////////////////////////////////////////////////////////////////////////////////
// stage setting methods
public void startStage1() {
rowSize = 21;
colSize = 21;
tileSize = 40;
theStage.setScene(scene2);
}
public void startStage2() {
rowSize = 29;
colSize = 29;
tileSize = 30;
theStage.setScene(scene2);
}
public void startStage3() {
rowSize = 41;
colSize = 41;
tileSize = 20;
theStage.setScene(scene2);
}
#Override
public void handle(KeyEvent event) {
// TODO Auto-generated method stub
}
public static void main(String[] args) {
launch(args);
}
/////////////////////////////////////////////////////////////////////////////////////////////////
// GridMap Class
public class GridMapT {
private String[][] map;
public GridMapT(int rowSize, int colSize) {
this.map = new String[rowSize][colSize];
// set up wall and fog
for (int i = 0; i < rowSize; i++) {
for (int j = 0; j < colSize; j++) {
if (i % 4 == 0 || j % 4 == 0) {
map[i][j] = "wall";
} else {
map[i][j] = "floor";
}
}
}
}
public String[][] getMap() {
return map;
}
}
}
I made a couple of changes to your code and made it work.
Firstly Application#start method should not be called by anybody but system. It is your app's entry point and calling it manually breaks app's lifecycle.
Secondly there's no need to create GridMapT instance in start method just because you don't know by the time it runs how many columns and rows there should be. And that's why there's no need to put map into your GridPane here too.
Thirdly you need to create new GridMapT instance or change existing any time you need to update your map. So after you created new map or updated existing (added or removed items) you need to put it into your GridPane. Both pieces of code that creates map and updates grid seems to be ok, but they are called at the wrong time and in the wrong place.
Lastly int type is a primitive in Java. Changing any int value affects only it and you still need to recreate and/or update anything that depends on it manually. This means that you need manually create/update map with new colSize and rowSize values each time you change them. If you create new map you need manually pass it to your GridPane instance.
In general your app's logic should look like that:
Create stage, scenes, components.
Show first scene
Wait for user's input (click, size, etc.)
Update second scene
Show second scene
Wait for user's input (click Quit)
Close second scene
Repeat 3-7 according to user's actions
There are still a couple of things that can be done better here and I wish you luck to find and improve them :)
Improved code:
public class Test extends Application implements EventHandler<KeyEvent>{
static Stage theStage;
static Scene scene1, scene2;
// top box
HBox topBox = new HBox();
// bottom box
HBox bottomBox = new HBox();
// grid dimensions (I'm trying to manipulate these variables)
int rowSize;
int colSize;
int tileSize;
GridPane gridPane;
GridMapT gridMap;
#Override
public void start(Stage firstStage) throws Exception{
theStage = firstStage;
// scene 2 //////////////
Label label2 = new Label("scene 2: choose a stage");
Button stage1_btn = new Button("Room 5x5");
Button stage2_btn = new Button("Room 7x7");
Button stage3_btn = new Button("Room 10x10");
Button button5 = new Button("Exit");
stage1_btn.setOnAction(e -> {
startStage1();
});
stage2_btn.setOnAction(e -> {
startStage2();
});
stage3_btn.setOnAction(e -> {
startStage3();
});
button5.setOnAction(e -> System.exit(0));
// Layout1
VBox layout = new VBox(20);
layout.setAlignment(Pos.CENTER);
layout.getChildren().addAll(label2, stage1_btn, stage2_btn, stage3_btn, button5);
scene1 = new Scene(layout, 800, 600);
// Scene 3 ////////////////////
// top box
Label title = new Label("Map test");
topBox.getChildren().add(title);
// bottom box
Label instruction = new Label("");
bottomBox.getChildren().add(instruction);
// scene 3
BorderPane gameScreen = new BorderPane();
scene2 = new Scene(gameScreen);
// set up gridPane
gridPane = new GridPane();
////// ////////////////////////////////////////////////////////////////////////
// add a clickable reset and Debug Mode to bottom box
Button resetBtn = new Button();
resetBtn.setText("Quit game");
bottomBox.getChildren().add(resetBtn);
resetBtn.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<Event>(){
#Override
public void handle(Event event){
try {
//There's no need to call start method anymore.
//Think of the stage like it's your app's window
//and of a scene like it is window's content.
//restart(firstStage);
firstStage.setScene(scene1);
} catch (Exception e) {
e.printStackTrace();
}
}
});
// keyboard input
scene2.setOnKeyPressed(this);
// setting up the whole borderPane
gameScreen.setTop(topBox);
gameScreen.setBottom(bottomBox);
gameScreen.setCenter(gridPane);
// set scene1 as start up screen
firstStage.setScene(scene1);
firstStage.show();
}
//TODO pass rowSize and colSize here
private void createMap(){
gridMap = new GridMapT(rowSize, colSize);
}
//TODO pass gridMap
private void redrawMap(){
gridPane.getChildren().clear();
for (int x = 0; x < rowSize; x++) {
for (int y = 0; y < colSize; y++) {
String grid = gridMap.getMap()[x][y];
// floor labels
if (grid == "floor") {
Label table = new Label("F");
table.setMinWidth(tileSize);
table.setMinHeight(tileSize);
gridPane.add(table, x, y);
}
// wall labels
if (grid == "wall") {
Label table = new Label("W");
table.setMinWidth(tileSize);
table.setMinHeight(tileSize);
gridPane.add(table, x, y);
}
}
}
}
// restart method
public void restart(Stage stage) throws Exception{
topBox.getChildren().clear();
bottomBox.getChildren().clear();
gridPane.getChildren().clear();
gridMap = new GridMapT(rowSize, colSize);
start(stage);
}
///////////////////////////////////////////////////////////////////////////////////////
// stage setting methods
public void startStage1(){
rowSize = 21;
colSize = 21;
tileSize = 40;
createMap();
redrawMap();
theStage.setScene(scene2);
}
public void startStage2(){
rowSize = 29;
colSize = 29;
tileSize = 30;
createMap();
redrawMap();
theStage.setScene(scene2);
}
public void startStage3(){
rowSize = 41;
colSize = 41;
tileSize = 20;
createMap();
redrawMap();
theStage.setScene(scene2);
}
#Override
public void handle(KeyEvent event){
// TODO Auto-generated method stub
}
public static void main(String[] args){
launch(args);
}
/////////////////////////////////////////////////////////////////////////////////////////////////
// GridMap Class
public class GridMapT{
private String[][] map;
public GridMapT(int rowSize, int colSize){
this.map = new String[rowSize][colSize];
// set up wall and fog
for (int i = 0; i < rowSize; i++) {
for (int j = 0; j < colSize; j++) {
if (i % 4 == 0 || j % 4 == 0) {
map[i][j] = "wall";
} else {
map[i][j] = "floor";
}
}
}
}
public String[][] getMap(){
return map;
}
}
}
I want to display a db-result in a tablview. Therefore i have a dynamically created tableview :
public void createTableView() throws Exception {
List<String> columnNames = new ArrayList<>();
columnNames = test.getColumnNames();
for (int i = 0; i < test.getColumnCount(); i++) {
TableColumn<ObservableList<String>,String> column = new TableColumn<>(columnNames.get(i));
final int j = i;
column.setCellValueFactory(new Callback<CellDataFeatures<ObservableList<String>, String>, ObservableValue<String>>(){
public ObservableValue<String> call(CellDataFeatures<ObservableList<String>, String> param) {
return new SimpleStringProperty(param.getValue().get(j).toString());
}
});
tableView.getColumns().add(column);
}
Add db-result:
public void initializeTableView(List<List<String>> result) throws Exception {
ObservableList<ObservableList<String>> data = FXCollections.observableArrayList();
if (tableView.getColumns().size() == 0) {
createTableView();
}
for (int i = 0; i < result.size(); i++) {
ObservableList<String> row = FXCollections.observableArrayList();
row.addAll(result.get(i));
data.add(row);
}
tableView.setItems(data);
Now im getting a second db-result that I´m showing in the same tableview. Now i want to compare the first resultlist with the second resultlist and if the values are not equal, I want to show the first result in a tooltip and change the cellcolor to red.
You can use a custom TableCell that compares the data. Of course it may be better to not store the whole data twice. In case the changes are sparse, using a Map to store the original values would be more memory efficient, but the idea of styling the cells is just the same.
Example:
#Override
public void start(Stage primaryStage) {
// create sample data
List<List<String>> result = Arrays.asList(
Arrays.asList("a", "b"),
Arrays.asList("c", "d"),
Arrays.asList("e", "f"),
Arrays.asList("g", "h"),
Arrays.asList("i", "j")
);
TableView<ObservableList<String>> tableView = new TableView<>();
List<String> columnNames = Arrays.asList("column1", "column2");
for (int i = 0; i < columnNames.size(); i++) {
TableColumn<ObservableList<String>, String> column = new TableColumn<>(columnNames.get(i));
final int j = i;
column.setCellValueFactory((CellDataFeatures<ObservableList<String>, String> param) -> Bindings.stringValueAt(param.getValue(), j));
// use custom cells
column.setCellFactory(tc -> new TableCell<ObservableList<String>, String>() {
private final Tooltip tooltip = new Tooltip();
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
// clear the cell
setStyle(null);
setText(null);
setTooltip(null);
} else {
setText(item);
String original = result.get(getIndex()).get(j);
if (Objects.equals(original, item)) {
// same value as original -> no tooltip, no background
setStyle(null);
setTooltip(null);
} else {
// different -> red background + tooltip
setStyle("-fx-background-color: red;");
setTooltip(tooltip);
tooltip.setText(original);
}
}
}
});
tableView.getColumns().add(column);
}
ObservableList<ObservableList<String>> data = FXCollections.observableArrayList();
for (int i = 0; i < result.size(); i++) {
ObservableList<String> row = FXCollections.observableArrayList();
row.addAll(result.get(i));
data.add(row);
}
tableView.setItems(data);
// do some modifications
data.get(0).set(1, "1");
data.get(3).set(0, "3");
Scene scene = new Scene(tableView);
primaryStage.setScene(scene);
primaryStage.show();
}
I have a table with on my SQL server with 16 rows, I am trying to print out the BasePT column into a bunch of TextFields but I cannot figure out how. Am I suppose to create a separate string for each row? How can I minimize code and be able to get each row to show up on each TextField?
//Table 100
// Button
public void loadButton(){
connection = SqlConnection.FormulaConnection();
try {
String SQL = "Select * FROM '100';";
ResultSet rs = connection.createStatement().executeQuery(SQL);
while (rs.next()) {
//insert BasePT from Row Yellow into YellowText TextField
String Yellow = rs.getString("BasePt");
YellowText.setText(Yellow);
//insert BasePT from Row 012 Yellow into TwoYellowText TextField
String TwoYellow = rs.getString("BasePT");
TwoYellowText.setText(TwoYellow);
}
} catch (Exception e) {
e.printStackTrace();
}
}
If you do NOT want to use a Tableview as yelliver suggested, you can try this:
public VBox loadButton(){
connection = SqlConnection.FormulaConnection();
VBox vBox = new VBox();
try {
String SQL = "Select * FROM '100';";
ResultSet rs = connection.createStatement().executeQuery(SQL);
while (rs.next()) {
String yellow = rs.getString("BasePt");
TextField textField = new TextField(yellow);
vBox.getChildren().add(textField);
}
} catch (Exception e) {
e.printStackTrace();
}
return vBox;
}
You can then add the VBox where you want to have all the textFields.
In the following examples I use a array of String[] arrays with elements for the row data. It should be easy enough to use the data from the database instead.
The standard way: TableView
Simply use a Item class containing a property for each table column:
public class Item {
public Item(String baseFormula, String basePt) {
this.baseFormula = new SimpleStringProperty(baseFormula);
this.basePt = new SimpleStringProperty(basePt);
}
private final StringProperty basePt;
private final StringProperty baseFormula;
public final String getBaseFormula() {
return this.baseFormula.get();
}
public final void setBaseFormula(String value) {
this.baseFormula.set(value);
}
public final StringProperty baseFormulaProperty() {
return this.baseFormula;
}
public final String getBasePt() {
return this.basePt.get();
}
public final void setBasePt(String value) {
this.basePt.set(value);
}
public final StringProperty basePtProperty() {
return this.basePt;
}
#Override
public String toString() {
return "Item{" + "basePt=" + basePt.get() + ", baseFormula=" + baseFormula.get() + '}';
}
}
And use a TableView with a column for each database column. Use a cellFactory that displays TextFields and modifies the property belonging to the column on a change of the TextField's text property:
#Override
public void start(Stage primaryStage) {
TableView<Item> table = new TableView<>();
Callback<TableColumn<Item, String>, TableCell<Item, String>> factory = column -> new TableCell<Item, String>() {
private final TextField textField;
{
textField = new TextField();
textField.textProperty().addListener((observable, oldValue, newValue) -> {
// write to property
WritableValue<String> property = (WritableValue<String>) getTableColumn().getCellObservableValue(getIndex());
property.setValue(newValue);
});
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
setGraphic(textField);
if (!Objects.equals(textField.getText(), item)) {
// only modify if TextField wasn't source of the change
// to prevent carret movement
textField.setText(item);
}
}
}
};
TableColumn<Item, String> formulaColumn = new TableColumn<>("baseFormula");
formulaColumn.setCellValueFactory(cd -> cd.getValue().baseFormulaProperty());
formulaColumn.setCellFactory(factory);
TableColumn<Item, String> ptColumn = new TableColumn<>("basePt");
ptColumn.setCellValueFactory(cd -> cd.getValue().basePtProperty());
ptColumn.setCellFactory(factory);
table.getColumns().addAll(formulaColumn, ptColumn);
String[][] data = {
{"Hello", "World"},
{"Hello2", "World2"},
{"Hello3", "World3"},
{"Hello4", "World4"},
{"Hello5", "World5"},
{"Hello6", "World6"}
};
for (String[] d : data) {
table.getItems().add(new Item(d[0], d[1]));
}
Button btn = new Button("print");
btn.setOnAction(evt -> System.out.println(table.getItems()));
Scene scene = new Scene(new VBox(table, btn));
primaryStage.setScene(scene);
primaryStage.show();
}
Alternative
If you don't want to use a TableView, GridPane would be a suitable Pane to produce a layout like this:
#Override
public void start(Stage primaryStage) {
String[][] data = {
{"Hello", "World"},
{"Hello2", "World2"},
{"Hello3", "World3"},
{"Hello4", "World4"},
{"Hello5", "World5"},
{"Hello6", "World6"}
};
Insets margin = new Insets(4);
int nextRow = 1;
GridPane gridPane = new GridPane();
Text heading1 = new Text("BaseFormula");
Text heading2 = new Text("BasePT");
GridPane.setMargin(heading1, margin);
GridPane.setMargin(heading2, margin);
gridPane.addRow(0, heading1, heading2);
for (String[] d : data) {
TextField tf = new TextField(d[0]);
TextField tf2 = new TextField(d[1]);
GridPane.setMargin(tf, margin);
GridPane.setMargin(tf2, margin);
gridPane.addRow(nextRow++, tf, tf2);
}
// add lines
// subtract stroke width
DoubleBinding height = gridPane.heightProperty().subtract(1);
// margin = 1/2 stroke width
Insets vMargin = new Insets(0.5, 0, 0.5, 0);
// add vertical lines
for (int i = 0; i < 3; i++) {
Line vLine = new Line();
GridPane.setMargin(vLine, vMargin);
System.out.println(vLine.getStrokeWidth());
vLine.endYProperty().bind(height);
gridPane.add(vLine, i, 0, 1, nextRow);
}
// procede accordingly with horizontal lines
DoubleBinding width = gridPane.widthProperty().subtract(1);
Insets hMargin = new Insets(0, 0.5, 0, 0.5);
for (int i = 0; i <= nextRow; i++) {
Line hLine = new Line();
GridPane.setMargin(hLine, hMargin);
hLine.setStartX(1);
hLine.endXProperty().bind(width);
// Insert at the top of the cell
GridPane.setValignment(hLine, VPos.TOP);
gridPane.add(hLine, 0, i, 2, 1);
}
Scene scene = new Scene(new StackPane(new Group(gridPane)), 500, 500);
primaryStage.setScene(scene);
primaryStage.show();
}
you can replace this snippet with your ResultSet rs
ResultSet rs = null;//reference it to your rs
TableView<String[]> tv = new TableView<String[]>();
final int columnCount = rs.getMetaData().getColumnCount();
for(int i =1; i <= columnCount; i++){
TableColumn<String[], String> tc = new TableColumn<String[], String>();
tc.setText(rs.getMetaData().getColumnName(i));
final int k = i-1;
tc.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<
String[],String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(CellDataFeatures<String[],
String> param) {
return new SimpleStringProperty(param.getValue()[k]);
}
});
tc.setCellFactory(new Callback<TableColumn<String[],String>,
TableCell<String[],String>>() {
#Override
public TableCell<String[], String> call(TableColumn<String[],
String> param) {
return new TableCell<String[], String>(){
#Override
protected void updateItem(String arg0, boolean arg1) {
super.updateItem(arg0, arg1);
if(arg1){
setText("");
return;
}else{
setText(arg0);
}
}
};
}
});
}
while(rs.next()){
String[] s = new String[columnCount];
for(int i = 1; i <= columnCount; i++){
s[i -1] = rs.getString(i);
}
tv.getItems().add(s);
}
This will put you in the right direction. Hope it helps/.
I have the following problem: I created a code to separate each variable of Euclid's algorithm and store in a Array or object . I can not think of a way to retrieve the data and populate a tableView so that , depending on the MDC (a, b ) if fore very large, the columns are added automatically.
In fact , it is a representation of the resolution of the MDC by successive divisions. I did a test , but I can not run it. Follows the code .
MDC
public List<Inteiros_old> listMdc(Inteiros_old inteiros) {
//List<Integer> lista_resto = new ArrayList<Integer>();
System.out.println(" Dividendo" + "\t" + " Divisor " + "\t" + " Quociente" + "\t" + " Resto ");
System.out.println("----------" + "\t" + "----------" + "\t" + "----------" + "\t" + "----------");
int max_inteiros = Math.max(inteiros.getDividendo(), inteiros.getDivisor());
int min_inteiros = Math.min(inteiros.getDividendo(), inteiros.getDivisor());
//System.out.println(k + "\t" + m);
List<Inteiros_old> lista = new ArrayList<Inteiros_old>();
while( min_inteiros != 0) {
inteiros.setResto(max_inteiros % min_inteiros);
inteiros.setQuociente(max_inteiros/min_inteiros);
System.out.println(max_inteiros + "\t\t " + min_inteiros + "\t\t " + inteiros.getQuociente() + "\t\t" + inteiros.getResto());
max_inteiros = min_inteiros;
min_inteiros = inteiros.getResto();
//lista.add(inteiros.getResto());
}
return lista;
}
JAVAFX
public class TabelaDinamica extends Application {
private TableView<Inteiros> table = new TableView<Inteiros>();
private final ObservableList<Inteiros> data = FXCollections.observableArrayList();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
// -------------------------------------------------------------------------
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(300);
stage.setHeight(500);
final Label label = new Label("Dynamic Table with autoColumns");
label.setFont(new Font("Arial", 16));
table.setEditable(true);
// -------------------------------------------------------------------------
int sizeColumns = 4;
for (int j = 0; j < sizeColumns; j++) {
data.add(new Inteiros(1,2,3,4));
String nome = new String("col"+j);
TableColumn col = new TableColumn();
col.setMinWidth(100);
col.setCellValueFactory(
new PropertyValueFactory<Inteiros, String>(nome));
//table.getColumns().add(j, col);
table.setItems(data);
table.getColumns().addAll(col);
}
// -------------------------------------------------------------------------
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table);
((Group) scene.getRoot()).getChildren().addAll(vbox);
// -------------------------------------------------------------------------
stage.setScene(scene);
stage.show();
}
}
public class Inteiros {
private SimpleIntegerProperty dividendo; //dividen
private SimpleIntegerProperty divisor; //divisor
private SimpleIntegerProperty quociente; //quotient
private SimpleIntegerProperty resto; //rest
Inteiros(Integer dividendo, Integer divisor, Integer quociente, Integer resto) {
this.dividendo = new SimpleIntegerProperty(dividendo);
this.divisor = new SimpleIntegerProperty(divisor);
this.quociente = new SimpleIntegerProperty(quociente);
this.resto = new SimpleIntegerProperty(resto);
}
public Integer getDividendo() {
return dividendo.get();
}
public void setDividendo(Integer int_num) {
dividendo.set(int_num);
}
public Integer getDivisor() {
return divisor.get();
}
public void setDivisor(Integer int_num) {
divisor.set(int_num);
}
public Integer getQuociente() {
return quociente.get();
}
public void setQuociente(Integer int_num) {
quociente.set(int_num);
}
public Integer resto() {
return resto.get();
}
public void setResto(Integer int_num) {
resto.set(int_num);
}
try this
String[] nome = {"dividendo", "divisor", "quociente", "resto"}
int sizeColumns = 4;
for (int j = 0; j < sizeColumns; j++) {
data.add(new Inteiros(1,2,3,4));
TableColumn col = new TableColumn();
col.setMinWidth(100);
col.setCellValueFactory(new PropertyValueFactory<Inteiros, String>(nome[j]));
table.getColumns().addAll(col);
}
table.setItems(data);
because the value of String nome has to match with the name of the variable
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);