JavaFX如何将字符串值设置为TableView

JavaFX如何将字符串值设置为TableView,java,javafx,reflection,Java,Javafx,Reflection,给一些背景信息:我现在可以将文件加载到我的mp3程序中并播放它们,但是我的tableview中的所有值都是空的? 我的歌曲课 package application; //imports here public class Song { private String title; private String artist; private String album; private SimpleStringProperty pTitle; private SimpleStringProp

给一些背景信息:我现在可以将文件加载到我的mp3程序中并播放它们,但是我的tableview中的所有值都是空的?

我的歌曲课

package application;

//imports here

public class Song {
private String title;
private String artist;
private String album;
private SimpleStringProperty pTitle;
private SimpleStringProperty pArtist;
private SimpleStringProperty pAlbum;
private Media music;
private MediaPlayer mp;
private Image coverArt;

public Song(File file) {
    music = new Media(file.toURI().toString());
    music.getMetadata().addListener((Change<? extends String, ? extends Object> c) -> {
        if (c.wasAdded()) {
            if ("artist".equals(c.getKey())) {
                System.out.println(c.getKey()+":"+c.getValueAdded());
                this.pArtist = new SimpleStringProperty(c.getValueAdded().toString());
                //pArtist.set(c.getValueAdded().toString());
                artist = c.getValueAdded().toString();  
            } else if ("title".equals(c.getKey())) {
                title = c.getValueAdded().toString();
                System.out.println(c.getKey()+":"+c.getValueAdded());
            } else if ("album".equals(c.getKey())) {
                album = c.getValueAdded().toString();
                System.out.println(c.getKey()+":"+c.getValueAdded());
            } else if ("image".equals(c.getKey())) {
                coverArt = (Image) c.getValueAdded();
            }
        }
    });
    mp = new MediaPlayer(music);
    System.out.println(pArtist);
    System.out.println(artist);
    //artist = (String) mp.getMedia().getMetadata().get("artist");
    //title = (String) music.getMetadata().get("title");
    //album = (String) music.getMetadata().get("album");
    //artist = "test";
    //album = "test";
    //title = "test";
}

public void play() {
    mp.play();
}

public void pause() {
    mp.pause();
}

public void stop() {
    mp.stop();
}

public String getTitle(){
    return title;
}

public void setTitle(String title){
    this.title = title;
}

public String getArtist(){
    return artist;
}

public void setArtist(String artist){
    this.artist = artist;
}

public String getAlbum(){
    return album;
}

public void setAlbum(String album){
    this.album = album;
}

public Image getCover(){
    return coverArt;
}

public MediaPlayer getMP(){
    return mp;
}





}
这是我的控制器类

public class SceneController implements Initializable{
@FXML
private Button stopBtn;
@FXML
private Slider volume;
@FXML
private Button loadBtn;
@FXML
private Button playBtn;
@FXML
private TableView<Song> table;
@FXML
private Label label;
@FXML
private ProgressBar proBar;
private TableColumn songCol,artistCol,albumCol;
ObservableList<Song> songList = FXCollections.observableArrayList();
List<File> list;
FileChooser fileChooser = new FileChooser();
Desktop desktop;
Song mySong;



@Override
public void initialize(URL arg0, ResourceBundle arg1) {
    TableColumn songCol = new TableColumn("Song");
    TableColumn artistCol = new TableColumn("Artist");
    TableColumn albumCol = new TableColumn("Album");

    songCol.setCellValueFactory(
            new PropertyValueFactory<Song,String>("title"));
    //songCol.setCellFactory(new Callback);
    artistCol.setCellValueFactory(
            new PropertyValueFactory<Song,String>("artist"));
    albumCol.setCellValueFactory(
            new PropertyValueFactory<Song,String>("album"));
    volume.setMin(0);
    volume.setMax(100);
    volume.setValue(100);
    volume.valueProperty().addListener(new InvalidationListener() {
        @Override
        public void invalidated(Observable observable) {
            mySong.getMP().setVolume(volume.getValue()/100.0);
        }

    });

}

// Event Listener on Button[#loadBtn].onAction
@FXML
public void loadFile(ActionEvent event) {
    Node source = (Node) event.getSource();
    Window theStage = source.getScene().getWindow();
    //set fileChooser filter
    FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("MP3 files", "*.mp3");
    fileChooser.getExtensionFilters().add(extFilter);
    fileChooser.setTitle("Select MP3 files");
    //File file = fileChooser.showOpenDialog(theStage);
    //mySong = new Song(file);
    list = fileChooser.showOpenMultipleDialog(theStage);
    if(list!=null){
        for(File x: list) {
            mySong = new Song(x);
            System.out.println(mySong.getTitle());
            songList.add(mySong);
        }
    }
    table.setItems(songList);
}

@FXML
public void playSong(ActionEvent event) {
    mySong.play();
}

@FXML
public void stopSong(ActionEvent event) {
    //mySong.pause();
    System.out.println("song title: "+mySong.getArtist()+mySong.getTitle());
    ImageView img = new ImageView(mySong.getCover());
    //img.fitWidthProperty().bind(label.widthProperty());
    //img.fitHeightProperty().bind(label.heightProperty());
    img.setFitHeight(120);
    img.setFitWidth(200);
    label.setGraphic(img);
    //label.setGraphic(new ImageView(mySong.getCover()));
}
公共类SceneController实现可初始化{
@FXML
专用按钮stopBtn;
@FXML
私人卷;
@FXML
专用按钮加载;
@FXML
私人按钮播放;
@FXML
私有表视图表;
@FXML
自有品牌;
@FXML
私人律师;
私人表格栏目songCol、artistCol、albumCol;
ObservableList songList=FXCollections.observableArrayList();
名单;
FileChooser FileChooser=newfilechooser();
桌面;
宋美松;
@凌驾
公共void初始化(URL arg0,ResourceBundle arg1){
TableColumn songCol=新的TableColumn(“歌曲”);
TableColumn artistCol=新的TableColumn(“艺术家”);
TableColumn albumCol=新的TableColumn(“相册”);
松科赛尔价值工厂(
新财产价值工厂(“所有权”);
//songCol.setCellFactory(新回调);
artistCol.setCellValueFactory(
新地产价值工厂(“艺术家”);
albumCol.setCellValueFactory(
新财产价值工厂(“相册”);
体积设置最小值(0);
体积。设定最大值(100);
体积。设定值(100);
volume.valueProperty().addListener(新的InvalizationListener()){
@凌驾
公共无效(可观察到){
mySong.getMP().setVolume(volume.getValue()/100.0);
}
});
}
//按钮[#loadBtn].onAction上的事件侦听器
@FXML
公共无效加载文件(ActionEvent事件){
节点源=(节点)事件。getSource();
Window theStage=source.getScene().getWindow();
//设置文件选择器过滤器
FileChooser.ExtensionFilter extFilter=newfilechooser.ExtensionFilter(“MP3文件”,“*.MP3”);
fileChooser.getExtensionFilters().add(extFilter);
setTitle(“选择MP3文件”);
//File File=fileChooser.showOpenDialog(舞台);
//mySong=新歌(文件);
list=fileChooser.showOpenMultipleDialog(舞台);
如果(列表!=null){
用于(文件x:列表){
mySong=新歌(x);
System.out.println(mySong.getTitle());
歌曲列表.add(mySong);
}
}
表2.setItems(歌曲列表);
}
@FXML
公共无效播放歌曲(ActionEvent){
mySong.play();
}
@FXML
公共无效停止歌曲(ActionEvent事件){
//停顿();
System.out.println(“歌曲标题:+mySong.getArtist()+mySong.getTitle());
ImageView img=newImageView(mySong.getCover());
//img.fitWidthProperty().bind(label.widthProperty());
//img.fitHeightProperty().bind(label.heightProperty());
安装高度(120);
img.setFitWidth(200);
标签设置图形(img);
//label.setGraphic(新的ImageView(mySong.getCover());
}
但我为我的“停止”做了另一个测试打印行控件类中的按钮,在加载所有内容并按下它之后,它会很好地打印出艺术家和标题。我看到了这一点并检查了我的getter方法,它们似乎是正确的?我真的很迷茫,如果有人能提供一些见解和解决方案,说明这是因为我的变量为null还是因为我的PropertyValueFac托利做得不对


我还注意到,空值排在第一位,即使它们不应该是最后打印的,因为当我在控制器类中创建一个新的song对象时,运行的第一个打印行在if语句中?

通常,TableColumns将在FXML中定义,并通过
@FXML
注入

如果您不想这样做,您需要:

table.getColumns().add(songCol);
其他专栏也是如此

此外,正如HypnicJerk在评论中指出的,在使用PropertyValueFactory时,您还需要遵循适当的命名约定

songCol.setCellValueFactory(
    new PropertyValueFactory<Song,String>("title")
);
songCol.setCellValueFactory(
新财产价值工厂(“所有权”)
);
有关更多详细信息,请参阅:


通常,TableColumns将在FXML中定义,并通过
@FXML
注入

如果您不想这样做,您需要:

table.getColumns().add(songCol);
其他专栏也是如此

此外,正如HypnicJerk在评论中指出的,在使用PropertyValueFactory时,您还需要遵循适当的命名约定

songCol.setCellValueFactory(
    new PropertyValueFactory<Song,String>("title")
);
songCol.setCellValueFactory(
新财产价值工厂(“所有权”)
);
有关更多详细信息,请参阅:


从您在问题中发布的有限示例中可以明显看出,当前代码的使用方式存在一些问题:

  • 您的
    Song
    类没有正确地遵循。特别是,您将每个“属性”存储两次,一次存储在“传统”中JavaBean样式字段,例如
    private String title
    ,并在JavaFX属性中存储一次:
    private StringProperty pTitle;
    。每个属性都应存储一次。如果希望表在值更改时能够感知,则应使用JavaFX属性,并具有“标准”
    getXXX()
    setXXX()
    检索并设置存储在这些属性中的基础值
  • 您附加到媒体元数据的侦听器将在将来的某个不确定点异步调用。当您将歌曲添加到表的列表中时,附加到列的单元格值工厂将在某个点执行,并从
    song
    实例检索分配的属性。使用您当前使用的代码如果有,这些属性实例只有在元数据上的侦听器被调用后才被实际创建单元格值工厂将在实例化JavaFX属性之前检查
    Song
    实例的属性,这使得表无法正确观察属性并响应其中的更改。当
    实例
    
    public class SceneController implements Initializable{
    
        // non-table code omitted...
    
        @FXML
        private TableView<Song> table;
        @FXML
        private Label label;
        @FXML
        private ProgressBar proBar;
        @FXML
        private TableColumn<Song, String> songCol ;
        @FXML
        private TableColumn<Song, String> artistCol ;
        @FXML
        private TableColumn<Song, String> albumCol;
    
        ObservableList<Song> songList = FXCollections.observableArrayList();
        List<File> list;
        FileChooser fileChooser = new FileChooser();
        Desktop desktop;
        Song mySong;
    
        @Override
        public void initialize(URL arg0, ResourceBundle arg1) {
    
            songCol.setCellValueFactory(cellData -> cellData.getValue().titleProperty());
            artistCol.setCellValueFactory(cellData -> cellData.getValue().artistProperty());
            albumCol.setCellValueFactory(cellData -> cellData.getValue().albumProperty());
    
            // ...
    
        }
    
        // other non-table code omitted...
    
    }