JavaFX如何将String值设置为TableView

时间:2017-04-04 20:06:41

标签: java javafx reflection

给出一些背景知识:我现在能够将文件加载到我的mp3程序中并播放它们,但我的tableview中的所有值都为空? enter image description here

我的歌曲课程

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;
}





}

起初我觉得很奇怪,因为我的String变量没有正确设置并被设置为null,因为当我在构造Song对象时将这些打印行测试它时,它在控制台中显示为null。以下是我测试时的控制台示例。

null
null
artist:Foo Fighters
album:Saint Cecilia EP
title:Saint Cecilia

这是我的控制器类

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()));
}

但我为我的&#34; Stop&#34;制作了另一个测试打印行。控制器类中的按钮,加载完所有内容后按下它,打印出艺术家和标题。我看到了这个other thread并检查了我的getter方法,它们似乎是正确的?我真的很遗憾,如果有人能提供一些见解和解决方案,是因为我的变量是null还是我的PropertyValueFactory没有正确完成

另外我注意到空值是第一个,即使它们不是最后打印的东西,因为当我在控制器类中创建一个新的歌曲对象时,运行的第一个打印行是在if语句中吗?

2 个答案:

答案 0 :(得分:2)

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

  1. 您的Song课程没有正确遵循JavaFX properties pattern。特别是,您将每个“属性”存储两次,一次存储在“传统”JavaBean样式字段中,例如private String title,一次存储在JavaFX属性中:private StringProperty pTitle;。每个属性应存储一次。如果希望表在值更改时知道,则应使用JavaFX属性,并使用“标准”getXXX()setXXX()检索并设置存储在这些属性中的基础值。
  2. 您附加到媒体元数据的侦听器将来在某个不确定的位置称为异步 。将歌曲添加到表的列表时,将在某个时刻执行附加到列的单元格值工厂,并从Song实例检索已分配的属性。使用您当前拥有它的方式,只有在调用元数据上的侦听器时才会实际创建这些属性实例。因此,有可能(可能可能)单元值工厂将在实例化JavaFX属性之前检查Song实例的属性,从而使表无法正确观察属性并响应其中的更改。您应该在创建Song实例时实例化JavaFX属性,并在元数据的侦听器中设置它们的值。
  3. 在任何时候都不会将您在控制器中创建的列添加到表中。如果您在FXML文件中创建它们(您没有在问题中发布),则应将这些列注入控制器并使用单元格值工厂初始化那些列。 (由于屏幕截图显示表中有列,我将假设它们在FXML文件中定义,并且具有适当的fx:id s。)
  4. 所以你的Song课应该是这样的:

    public class Song {
    
        private final StringProperty title = new SimpleStringProperty();
        private final StringProperty artist = new SimpleStringProperty();
        private final StringProperty album = new SimpleStringProperty();
        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())) {
                        setArtist(c.getValueAdded().toString());
                    } else if ("title".equals(c.getKey())) {
                        setTitle(c.getValueAdded().toString());
                    } else if ("album".equals(c.getKey())) {
                        setAlbum(c.getValueAdded().toString());
                    } else if ("image".equals(c.getKey())) {
                        // maybe this needs to be a JavaFX property too: it is not clear from your question:
                        coverArt = (Image) c.getValueAdded();
                    }
                }
            });
            mp = new MediaPlayer(music);
        }
    
        public void play() {
            mp.play();
        }
    
        public void pause() {
            mp.pause();
        }
    
        public void stop() {
            mp.stop();
        }
    
        public StringProperty titleProperty() {
            return title ;
        }
    
        public final String getTitle(){
            return titleProperty().get();
        }
    
        public final void setTitle(String title){
            titleProperty().set(title);
        }
    
        public StringProperty artistProperty() {
            return artist ;
        }
    
        public final String getArtist(){
            return artistProperty().get();
        }
    
        public final void setArtist(String artist){
            artistProperty.set(artist);
        }
    
        public StringProperty albumProperty() {
            return album ;
        }
    
        public final String getAlbum(){
            return albumProperty().get();
        }
    
        public final void setAlbum(String album){
            albumProperty().set(album);
        }
    
        public Image getCover(){
            return coverArt;
        }
    
        public MediaPlayer getMP(){
            return mp;
        }
    
    
    }
    

    对于您的控制器,我将假设您的FXML文件已定义表格列,fx:id分别为"songCol""artistCol""albumCol"。您需要像对待其他列一样将这些注入到控制器中。我还强烈建议使用PropertyValueFactory类,它使用反射并且在编译时检查方面缺乏很多,并且自己实现回调。使用lambda表达式使这很容易。

    所以你的控制器应该是这样的:

    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...
    
    }
    

    您没有发布最小,完整,可验证的示例,因此代码中可能存在其他错误,导致表无法正确显示。不过,这应该可以让你开始。

答案 1 :(得分:1)

通常,TableColumns将在FXML中定义并通过table.getColumns().add(songCol); 注入。

如果您不想这样做,则需要执行以下操作:

songCol.setCellValueFactory(
    new PropertyValueFactory<Song,String>("title")
);

同样适用于其他专栏。

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

photoDispaly.transform = CGAffineTransform(rotationAngle: M_PI/4)

有关详细信息,请参阅: