给出一些背景知识:我现在能够将文件加载到我的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;
}
}
起初我觉得很奇怪,因为我的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语句中吗?
答案 0 :(得分:2)
您使用当前代码的方式存在一些问题,这可以从您在问题中发布的有限示例中看出:
Song
课程没有正确遵循JavaFX properties pattern。特别是,您将每个“属性”存储两次,一次存储在“传统”JavaBean样式字段中,例如private String title
,一次存储在JavaFX属性中:private StringProperty pTitle;
。每个属性应存储一次。如果希望表在值更改时知道,则应使用JavaFX属性,并使用“标准”getXXX()
和setXXX()
检索并设置存储在这些属性中的基础值。Song
实例检索已分配的属性。使用您当前拥有它的方式,只有在调用元数据上的侦听器时才会实际创建这些属性实例。因此,有可能(可能可能)单元值工厂将在实例化JavaFX属性之前检查Song
实例的属性,从而使表无法正确观察属性并响应其中的更改。您应该在创建Song
实例时实例化JavaFX属性,并在元数据的侦听器中设置它们的值。fx:id
s。)所以你的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)
有关详细信息,请参阅: