0

(This is similar to a homework question.)

I recently made an example UI in Scenebuilder for something I later had to program with Java Swing. That more or less worked. Now it is my task, not for the actual development of the program, but for learning something in my job training, to make a similar UI with Scenebuilder, but this time an actually working one. The specifications are:

  • It's a window with a table in it.
  • At least two of the columns have radio boxes in them that look like checkboxes or checkboxes that act like radio boxes (because the company has weird standards).
  • It uses FXML files made with Scenebuilder for the layout.

Making the check boxes act as radio boxes should be easy, if I could just enable the editing. I found a lot of examples that do almost what I want, but are still all not really applicable to my situation. Here are some of them:

  • I started with this video and almost exactly copied the code to first have a working example. Then I adjusted it to my needs until I only had the check boxes to do (first working prototype had Booleans instead).
  • Then I took part of the full code of this answer to add the check boxes. That worked, but they don't react to clicks.
  • This, this and this seems to only apply to text fields, not checkboxes.
  • I then used the two lambda expressions from the second code block in this answer (actually I used the variant in the first answer and manually resolved some errors until suddenly Eclipse automatically converted it to lambda expressions). I also added the public ObservableValue<Boolean> getCompleted() method, Eclipse suggested some magic and then I had what you can see in the corresponding code below (without the console print).
  • I also added a listener to the boolean property, like this site (archive) apparently does (I think), but that doesn't seem like it helped much.

Here is a picture of how the dialog looks now, I still can't use the check boxes: enter image description here

My question: How can I make it so that the check boxes react to clicks? React can mean outputting something on the console, I don't need a given code that makes it automatically disable the other checkbox, I want to figure that part out myself.

My code:

src.controller.MainController.java

package controller;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import view.Table;
public class MainController implements Initializable{
 @FXML TableView<Table> tableID;
 @FXML TableColumn<Table,String> iFirstName;
 @FXML TableColumn<Table,String> iLastName;
 @FXML TableColumn<Table,Boolean> iMalebox;
 @FXML TableColumn<Table,Boolean> iFemalebox;
 @Override public void initialize(URL location,ResourceBundle resources){
  iFirstName.setCellValueFactory(new PropertyValueFactory<Table,String>("rFirstName"));
  iLastName.setCellValueFactory(new PropertyValueFactory<Table,String>("rLastName"));
  iMalebox.setCellValueFactory(p->p.getValue().getCompleted());
  iMalebox.setCellFactory(p->new CheckBoxTableCell<>());
  iMalebox.setEditable(true);
  // iMalebox.setCellValueFactory(p->p.getValue().getCompleted());
  // iMalebox.setCellFactory(p->new CheckBoxTableCell<>());
  iFemalebox.setCellValueFactory(p->p.getValue().getCompleted());
  iFemalebox.setCellFactory(p->new CheckBoxTableCell<>());
  // iMalebox.setCellValueFactory(new PropertyValueFactory<Table,Boolean>("rMalebox"));
  // iFemalebox.setCellValueFactory(new PropertyValueFactory<Table,Boolean>("rFemalebox"));
  tableID.setItems(FXCollections.observableArrayList(new Table("Horst","Meier",true,false),new Table("Anna","Becker",false,true),new Table("Karl","Schmidt",true,false)));
  tableID.setEditable(true);
 }
}

src.controller.MainView.java

package controller;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class MainView extends Application{
 @Override public void start(Stage primaryStage){
  try{
   // FXMLLoader.load(MainView.class.getResource("MainController.fxml"));
   AnchorPane page=(AnchorPane)FXMLLoader.load(MainView.class.getResource("MainController.fxml"));
   Scene scene=new Scene(page);
   primaryStage.setScene(scene);
   primaryStage.setTitle("Window Title");
   primaryStage.show();
  }catch(Exception e){
   Logger.getLogger(MainView.class.getName()).log(Level.SEVERE,null,e);
  }
 }
 public static void main(String[] args){
  Application.launch(MainView.class,(java.lang.String[])null);
 }
}

src.controller.MainController.fxml

<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controller.MainController">
 <children>
<TableView fx:id="tableID" prefHeight="494.0" prefWidth="798.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columns>
<TableColumn fx:id="iFirstName" prefWidth="75.0" text="First name" />
<TableColumn fx:id="iLastName" prefWidth="75.0" text="Last name" />
            <TableColumn fx:id="iMalebox" prefWidth="75.0" text="Male" />
            <TableColumn fx:id="iFemalebox" prefWidth="75.0" text="Female" />
</columns>
</TableView>
 </children>
</AnchorPane>

src.view.Table.java

package view;
import javafx.beans.InvalidationListener;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
public class Table{
 private SimpleStringProperty rFirstName;
 private SimpleStringProperty rLastName;
 private SimpleBooleanProperty rMalebox;
 private SimpleBooleanProperty rFemalebox;
 public Table(String sFirstName,String sLastName,Boolean sMalebox,Boolean sFemalebox){
  rFirstName=new SimpleStringProperty(sFirstName);
  rLastName=new SimpleStringProperty(sLastName);
  rMalebox=new SimpleBooleanProperty(sMalebox);
  rMalebox.addListener((ChangeListener)(observable,oldValue,newValue)->{
   System.out.println("test");
   System.out.println("abc");
  });
  rFemalebox=new SimpleBooleanProperty(sFemalebox);
 }
 public String getRFirstName(){
  return rFirstName.get();
 }
 public void setRFirstName(String v){
  rFirstName.set(v);
 }
 public String getRLastName(){
  return rLastName.get();
 }
 public void setRLastName(String v){
  rLastName.set(v);
 }
 public Boolean getRMalebox(){
  return rMalebox.get();
 }
 public void setRMalebox(Boolean v){
  rMalebox.set(v);
 }
 public Boolean getRFemalebox(){
  return rFemalebox.get();
 }
 public void setRFemalebox(Boolean v){
  rFemalebox.set(v);
 }
 public ObservableValue<Boolean> getCompleted(){
  return new ObservableValue<Boolean>(){
   @Override public void removeListener(InvalidationListener arg0){}
   @Override public void addListener(InvalidationListener arg0){}
   @Override public void removeListener(ChangeListener<? super Boolean> listener){}
   @Override public Boolean getValue(){
    return null;
   }
   @Override public void addListener(ChangeListener<? super Boolean> listener){
    System.out.println("Test");
   }
  };
 }
}
4

1 回答 1

1

我找到了解决方案。因为我改变了很多(在提出这个问题后,我努力让这个愚蠢的东西工作了将近 20 个小时),列举我所做的所有改变并不是很有用。但这里至少是一个可行的例子。
很少有行负责使“男性”和“女性”框触发彼此的对立面(这是特定于我的任务),其他一切都只是为了让 CheckBoxTableCells 实际正常工作。人们会认为这是一个非常常见的情况,应该有标准的方法,比如“将文本打印到控制台”或“读取文件”,但显然不是,显然在使用 UI 编程时一切都必须复杂。

无论如何,除了咆哮,这是工作代码:

src.controller.MainView.java

package controller;
import java.io.*;
import javafx.application.*;
import javafx.fxml.*;
import javafx.scene.*;
import javafx.scene.layout.*;
import javafx.stage.*;
public class MainView extends Application{
 @Override public void start(Stage primaryStage) throws IOException{
  primaryStage.setScene(new Scene((AnchorPane)FXMLLoader.load(MainView.class.getResource("MainController.fxml"))));
  primaryStage.show();
 }
 public static void main(String[] args){
  Application.launch(MainView.class);
 }
}

src.controller.MainController.java

package controller;
import java.net.*;
import java.util.*;
import javafx.beans.value.*;
import javafx.collections.*;
import javafx.fxml.*;
import javafx.scene.control.*;
import javafx.scene.control.TableColumn.*;
import javafx.scene.control.cell.*;
import javafx.util.*;
import view.*;
public class MainController implements Initializable{
 @FXML TableView<Table> tableID;
 @FXML TableColumn<Table,String> iFirstName;
 @FXML TableColumn<Table,String> iLastName;
 @FXML TableColumn<Table,Boolean> iMalebox;
 @FXML TableColumn<Table,Boolean> iFemalebox;
 @Override public void initialize(URL location,ResourceBundle resources){
  iFirstName.setCellValueFactory(new PropertyValueFactory<Table,String>("rFirstName"));
  iLastName.setCellValueFactory(new PropertyValueFactory<Table,String>("rLastName"));
  iMalebox.setCellValueFactory(new Callback<CellDataFeatures<Table,Boolean>,ObservableValue<Boolean>>(){
   @Override public ObservableValue<Boolean> call(CellDataFeatures<Table,Boolean> cellData){
    return cellData.getValue().maleCheckedProperty(true);
   }
  });
  iMalebox.setCellFactory(new Callback<TableColumn<Table,Boolean>,TableCell<Table,Boolean>>(){
   @Override public TableCell<Table,Boolean> call(TableColumn<Table,Boolean> param){
    return new CheckBoxTableCell<>();
   }
  });
  iFemalebox.setCellValueFactory(new Callback<CellDataFeatures<Table,Boolean>,ObservableValue<Boolean>>(){
   @Override public ObservableValue<Boolean> call(CellDataFeatures<Table,Boolean> cellData){
    return cellData.getValue().femaleCheckedProperty(true);
   }
  });
  iFemalebox.setCellFactory(new Callback<TableColumn<Table,Boolean>,TableCell<Table,Boolean>>(){
   @Override public TableCell<Table,Boolean> call(TableColumn<Table,Boolean> param){
    return new CheckBoxTableCell<>();
   }
  });
  tableID.setItems(FXCollections.observableArrayList(new Table("Horst","Meier",true),new Table("Anna","Becker",false),new Table("Karl","Schmidt",true)));
 }
}

src.view.Table.java

package view;
import javafx.beans.property.*;
public class Table{
 private String rFirstName;
 private String rLastName;
 public Table(String sFirstName,String sLastName,Boolean sMale){
  rFirstName=sFirstName;
  rLastName=sLastName;
  maleCheckedProperty(false).set(sMale);
 }
 private SimpleBooleanProperty maleChecked=new SimpleBooleanProperty(false);
 private SimpleBooleanProperty femaleChecked=new SimpleBooleanProperty(false);
 public SimpleBooleanProperty maleCheckedProperty(boolean recursion){
  if(recursion) femaleCheckedProperty(false).set(!maleChecked.get());
  return maleChecked;
 }
 public SimpleBooleanProperty femaleCheckedProperty(boolean recursion){
  if(recursion) maleCheckedProperty(false).set(!femaleChecked.get());
  return femaleChecked;
 }
 public String getRFirstName(){
  return rFirstName;
 }
 public String getRLastName(){
  return rLastName;
 }
}

src.controller.MainController.fxml

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="98.0" prefWidth="218.0" xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controller.MainController">
 <children>
 <TableView fx:id="tableID" editable="true" prefHeight="494.0" prefWidth="798.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
  <columns>
   <TableColumn fx:id="iFirstName" maxWidth="1.7976931348623157E308" minWidth="-1.0" prefWidth="63.0" text="First name" />
   <TableColumn fx:id="iLastName" maxWidth="1.7976931348623157E308" minWidth="-1.0" prefWidth="63.0" text="Last name" />
   <TableColumn fx:id="iMalebox" maxWidth="1.7976931348623157E308" minWidth="-1.0" prefWidth="45.0" text="Male"/>
   <TableColumn fx:id="iFemalebox" maxWidth="1.7976931348623157E308" minWidth="-1.0" prefWidth="45.0" text="Female"/>
   </columns>
  </TableView>
 </children>
</AnchorPane>
于 2017-10-30T15:20:29.873 回答