How do I handle media metadata in separate class? - javafx

Im making a mediaplayer using JavaFX Media classes. I made a SongModel class, that incapsulates all metadata from a file and creates Media and MediaPlayer instances.
It looks something like this:
private final StringProperty album =
new SimpleStringProperty(this, "album");
public String getAlbum(){ return album.get(); }
public void setAlbum(String value){ album.set(value); }
public StringProperty albumProperty() { return album; }
There are also artist, year, title, and albumCover fields that look just like that. Also, MediaPlayer property is exposed as a read-only:
public MediaPlayer getMediaPlayer(){ return mediaPlayer.get(); }
public ReadOnlyObjectProperty<MediaPlayer> mediaPlayerProperty(){
return mediaPlayer.getReadOnlyProperty();
}
I use a MapChangelistener to check if the field is available and then pass it to the handleMetadata method:
private void initializeMedia(String url){
try {
final Media media = new Media(url);
media.getMetadata().addListener(new MapChangeListener<String, Object>(){
#Override
public void onChanged(MapChangeListener.Change<? extends String, ? extends Object> ch) {
if(ch.wasAdded()){
handleMetadata(ch.getKey(), ch.getValueAdded());
}
}
});
mediaPlayer.setValue(new MediaPlayer(media));
mediaPlayer.get().setOnError(new Runnable() {
#Override
public void run() {
String errorMessage = mediaPlayer.get().getError().getMessage();
System.out.println("MediaPlayer error: "+errorMessage);
}
});
}catch(RuntimeException e){
System.out.println("Construction error: "+e);
}
}
private void handleMetadata(String key, Object value){
if(key.equals("album")){
setAlbum(value.toString());
} else if (key.equals("artist")){
setArtist(value.toString());
} if (key.equals("title")){
setTitle(value.toString());
} if (key.equals("year")){
setYear(value.toString());
} if (key.equals("image")){
setAlbumCover((Image)value);
}
}
Then I made an AbstractView class that provides access to SongModel:
public abstract class AbstractView {
protected final SongModel songModel;
protected final Node viewNode;
public AbstractView(SongModel songModel){
this.songModel = songModel;
this.viewNode = initView();
}
public Node getViewNode() {
return viewNode;
}
protected abstract Node initView();
}
But when I try to make a MetadataView class, I run into some problems.
Heres how it looks:
public class MetadataView extends AbstractView{
public Label artist;
public Label album;
public Label title;
public Label year;
public ImageView albumCover;
public MetadataView(SongModel songModel) {
super(songModel);
}
#Override
protected Node initView() {
artist = new Label();
artist.setId("artist");
album = new Label();
album.setId("album");
title = new Label();
title.setId("title");
year = new Label();
year.setId("year");
final Reflection reflection = new Reflection();
reflection.setFraction(0.2);
final URL url = getClass().getResource("resources/defaultAlbum.png");
Image image = new Image(url.toString());
albumCover = new ImageView(image);
albumCover.setFitWidth(240);
albumCover.setPreserveRatio(true);
albumCover.setSmooth(true);
albumCover.setEffect(reflection);
final GridPane gp = new GridPane();
gp.setPadding(new Insets(10));
gp.setHgap(20);
gp.add(albumCover, 0,0,1, GridPane.REMAINING);
gp.add(title, 1,0);
gp.add(artist, 1,1);
gp.add(album, 1,2);
gp.add(year, 1,3);
final ColumnConstraints c0 = new ColumnConstraints();
final ColumnConstraints c1 = new ColumnConstraints();
c1.setHgrow(Priority.ALWAYS);
gp.getColumnConstraints().addAll(c0,c1);
final RowConstraints r0 = new RowConstraints();
r0.setValignment(VPos.TOP);
gp.getRowConstraints().addAll(r0,r0,r0,r0);
return gp;
}
}
And heres how I call it in the start method:
metaDataView = new MetadataView(songModel);
The problem is that it displays only default metadata without taking it from the songmodel class. I tried running metadata view code together with data handling in one class and everything worked, but when i try to put them in separate classes - it doesnt. Music runs just fine, its just the data thats not displaying. Could anybody tell me what am I missing? How do i make it display metadata from a SongModel class? Ive spent a lot of time on that and dont want it to go to waste.

After a day of searching I have found an answer: binds. All I had to do was to bind label property of SongModel class to label property of MetadataView class:
title.textProperty().bind(songModel.titleProperty());
artist.textProperty().bind(songModel.artistProperty());
album.textProperty().bind(songModel.albumProperty());
year.textProperty().bind(songModel.yearProperty());
albumCover.imageProperty().bind(songModel.albumCoverProperty());

Related

Null data Passing Variable from Controller A to Controller B

Hello I am Trying to pass Variable data from database from one Controller to another, but I Have one problem, when I am passing the data to the controller A to Controller B, appear a Null value, the jerarchy between Scenes is this:
LoginController->AdminController->UserController
in the LoginController to Access AdminController and Pass the Value i do this:
public String getRoladmin() {
return roladmin;
}
public void setRoladmin(String roladmin) {
this.roladmin = roladmin;
}
public String roladmin="";
I Capture the Variable roladmin in this portion of my code like this:
while(rs.next()) {
comparauser=rs.getString("NOMBREUSUARIO");
comparapass=rs.getString("PASS");
miadmin=rs.getString("NOMBRE_ADMIN");
roladmin=rs.getString("ROL_ADMIN");
}
to access the Stage I validate if the user and pass are correct like this:
----rest of code--- if(comparauser.equals(fusuario.getText().toString())&&comparapass.equals(fcontrasena.getText().toString().trim())) {
try {
Stage administrador=new Stage();
FXMLLoader carga = new FXMLLoader(getClass().getResource("Admin.fxml"));
Parent StackPane = (Parent)carga.load();
Scene scene = new Scene(StackPane);
AdminScreenController loadl = carga.<AdminScreenController>getController();
/*loadl.UserScreen.setText("Bienvenido"+" "+fusuario.getText().toString());*/
loadl.UserScreen.setText("Bienvenido"+" "+miadmin);
/* String r=loadl.UserScreen.getText().toString();*/
-----in this part I call the LoginController and pass the variable Roladmin------
----begin of call--
String h=LoginController.this.roladmin;
LoginController.this.setRoladmin(h);
String r=LoginController.this.getRoladmin();
loadl.setCapdata(r);
-----end of call-----
if(!r.equals("ADMINISTRADOR")) {
loadl.btnimg5.setDisable(true);
loadl.btnimg5.setStyle("-fx-opacity:0.65;");
}
else {
loadl.btnimg5.setDisable(false);
}
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
administrador.setScene(scene);
administrador.setTitle("AdminScreen");
Stage login=(Stage)fusuario.getScene().getWindow();
login.hide();
administrador.show();
}catch(Exception e) {
Logger.getLogger(Application.class.getName()).log(Level.SEVERE, null, e);
}
}
---rest of code---
now i Pass that variable to the AdminController like this
in AdminController i have this:
public String capdata;
public String h;
public String getCapdata() {
return capdata;
}
public String setCapdata(String data) {
return this.capdata = data;
}
this is the code i have to load UserController Stage:
public void UserView() throws IOException {
Stage userstage = new Stage();
FXMLLoader cargauser =new FXMLLoader(getClass().getResource("UsuarioScreen.fxml"));
Parent StackPane = (Parent)cargauser.load();
UserController cargatodouser = cargauser.<UserController>getController();
String pasadato=UserScreen.getText().toString();
cargatodouser.iduser.setText(pasadato);
---begin of call to pass the variable---
h=AdminScreenController.this.getCapdata();
---end of call---
/*String r=cargatodouser.iduser.getText().toString();*/
Scene scene = new Scene(StackPane);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
userstage.setScene(scene);
userstage.setTitle("QuoraApp");
Stage AdminScreen=(Stage)btnimg1.getScene().getWindow();
AdminScreen.hide();
userstage.show();
}
now when i am passing the variable to the UserController Class i have a null value I am doing this:
In UserController class i have this to go back AdminController:
public String capturau;
public String getCapturau() {
return capturau;
}
public String setCapturau(String capturau) {
return this.capturau = capturau;
}
public void inicio() throws IOException {
Stage administrador=new Stage();
FXMLLoader carga = new FXMLLoader(getClass().getResource("Admin.fxml"));
Parent StackPane =(Parent) carga.load();
AdminScreenController loadl = carga.<AdminScreenController>getController();
String pasadato=iduser.getText().toString();
loadl.UserScreen.setText(pasadato);
/*Captura.setText(loadl.getCapdata());
String captura=Captura.getText().toString();
System.out.println(captura);*/
UserController.this.setCapturau(loadl.getCapdata());
String gg=UserController.this.getCapturau();
System.out.println(gg);
}
what i am doing wrong? a little help here please.
You need to look into the concept of static variables
Here is a static variable I declare on one Controller and use and another Controller
A BIG word of caution about using static variables or Global Variables
What ever you put in a static variable it holds that value till you clear it or change it
static Integer strID;
Here is the use of the static variable strID in the other Controller Class
Notice it needs to be imported to the new Controller
import static diary.ChildTableViewController.strID;
private void ReadDetailView() throws SQLException{
stmnt = conn.createStatement();
///String sql = "SELECT * FROM Child WHERE CID = "+strID;
///ResultSet rs = stmnt.executeQuery(sql);
ResultSet rs = stmnt.executeQuery("SELECT * FROM Child WHERE CID = "+strID);
Welcome to SO

How to build JavaFX styleable objects properly, that can be GC

I decided to refactor my application because of hugh memory leaks in the old version. For visualization objects, I decide to not more using fxml, but Styleable interface.
So I created a class Sim Photo like this :
public class SimPhoto extends Control {
private static final String DEFAULT_CLASS_NAME = "sim-photo";
private static final Double DEFAULT_STROKE_WIDTH = 0.0;
#Getter
#Setter
private static String DEFAULT_PHOTO = "";
private StyleableStringProperty imgPath;
private StyleableIntegerProperty arcHeight;
private StyleableIntegerProperty arcWidth;
private DoubleProperty strokeWidth;
private ObjectProperty<Paint> stroke;
private ObjectProperty<Paint> fill;
public SimPhoto() {
initialize();
}
public SimPhoto(#NamedArg("imgPath") String imgPath) {
this();
this.imgPathProperty().set(imgPath);
}
//Example of init properties
public final StyleableIntegerProperty arcHeightProperty() {
if (arcHeight == null) {
arcHeight = new SimpleStyleableIntegerProperty(
StyleableProperties.ARC_WIDTH,
SimPhoto.this,
"arcWidth",
0
);
}
return arcHeight;
}
public final StringProperty imgPathProperty() {
if(imgPath == null) {
imgPath = new SimpleStyleableStringProperty(
StyleableProperties.IMG_PATH,
SimPhoto.this,
"imgPath",
"");
}
return imgPath;
}
(...)
}
In my skin class, I use binding of properties from Control Class
public class SimPhotoSkin extends SkinBase<SimPhoto> {
#Getter
private Rectangle photoFond = new Rectangle();
private Rectangle photoView = new Rectangle();
private boolean invalidate = false;
private InvalidationListener invalidListener = this::invalidated;
private ChangeListener<String> pathListener = this::pathChanged;
public SimPhotoSkin(SimPhoto control) {
super(control);
initVisualization();
initListeners();
}
private void initVisualization() {
getChildren().addAll(photoFond, photoView);
if (getSkinnable().imgPathProperty() != null) {
setNewFond(getSkinnable().getImgPath());
}
}
private void initListeners() {
photoFond.widthProperty().bind(getSkinnable().widthProperty().subtract(5));
photoFond.heightProperty().bind(getSkinnable().heightProperty().subtract(5));
photoView.widthProperty().bind(photoFond.widthProperty().subtract(photoFond.strokeWidthProperty()));
photoView.heightProperty().bind(photoFond.heightProperty().subtract(photoFond.strokeWidthProperty()));
photoView.arcWidthProperty().bind(getSkinnable().arcWidthProperty());
photoView.arcHeightProperty().bind(getSkinnable().arcHeightProperty());
photoFond.arcWidthProperty().bind(getSkinnable().arcWidthProperty());
photoFond.arcHeightProperty().bind(getSkinnable().arcHeightProperty());
photoFond.fillProperty().bind(getSkinnable().fillProperty());
photoFond.strokeProperty().bind(getSkinnable().strokeProperty());
photoFond.strokeWidthProperty().bind(getSkinnable().strokeWidthProperty());
getSkinnable().imgPathProperty().addListener(pathListener);
}
private void pathChanged(ObservableValue<? extends String> observable, String oldValue, String newValue) {
(...)
}
private void setNewFond(String path) {
(...)
}
private void invalidated(Observable observable) {
invalidate = true;
}
}
I know that object cannot been GC while exist a reference to it. So I have a big problem, because event these objects are no more used, thay cannot be GC , and in my application ,when I need creating more than 300 objects at time is a big problem.
I tried to create method clean(), that will be unbind all bidnings and listeners, but it's not realy helpful. Problem still persist.
I'm thinking about any workaround like a Manager, that will store all objects in queue and while calling will return one objects disponibles or create new one.
But this is the last possibility, if I dont find any solution for my problem, and I would like avoid this.

No content displayed when populating TableView with attributes of an Object

Ok, so I know this is a common problem that has been posted about a lot but as much as I try to follow the advice given, my TableView till displays no data... I'll reduce my object a bit to keep things as short as possible. Here is my Object:
public SimpleStringProperty itemCode, itemName;
public ResourceItem(String code, String name) {
this.itemCode = new SimpleStringProperty(code);
this.itemName = new SimpleStringProperty(name);
}
public String getItemCode() {
return itemCode.get();
}
public void setItemCode(String code) {
itemCode.set(code);
}
public SimpleStringProperty itemCodeProperty() {
return itemCode;
}
public SimpleStringProperty itemNameProperty() {
return itemName;
}
public String getItemName() {
return itemName.get();
}
public void setItemName(String name) {
itemName.set(name);
}
And here is where I create the TableColumns:
TableColumn<ResourceItem, String> code = new TableColumn("Item Code");
code.setCellValueFactory(new PropertyValueFactory("itemCode"));
TableColumn<ResourceItem, String> code = new TableColumn("Item Name");
name.setCellValueFactory(new PropertyValueFactory("itemName"));
I add Resource Items to the ObservableList through a for loop and set my items of the TableView to that list:
ObservableList<ResourceItem> data = FXCollections.observableArrayList();
....
itemsInDB.setItems(data);
itemsInDB.getColumns().addAll(code, name);
And then nothing is added. Can someone help me out please?
EDIT:
Here is a testable version. It does require you set up a database called ims, a table called im_resoureitem_br with two columns: IMItemCode Varchar(4) and IMItemName Varchar(30).
public class TableViewTest extends Application {
final String DRIVER = "com.mysql.jdbc.Driver";
String urlHead = "jdbc:mysql://localhost/ims";
final String USER = "root";
final String PASS = "";
Connection connection;
Statement statement;
private TableView<ResourceItem> table = new TableView<ResourceItem>(); //creates table to hold Course objects
private final ObservableList<ResourceItem> data
= FXCollections.observableArrayList();
#Override
public void start(Stage stage) throws ClassNotFoundException {
Scene scene = new Scene(new Group());
stage.setTitle("Fall 2015 Schedule"); //title of stage, appears at top bar
stage.setWidth(700);
stage.setHeight(500);
final Label label = new Label("Brenna Morss-Fish Fall Schedule 2015");
table.setEditable(true);
table.setItems(data); //sets rows of table as data from course arraylist
TableColumn<ResourceItem, String> code = new TableColumn<ResourceItem, String>("Code:");//creates first column
code.setMinWidth(100);
code.setCellValueFactory(
new PropertyValueFactory("itemCode"));
TableColumn<ResourceItem, String> name = new TableColumn<ResourceItem, String>("Name:");//creates first column
name.setMinWidth(100);
name.setCellValueFactory(
new PropertyValueFactory("itemName")); //defines what column holds according to name field of Course class
String query = "select * from ims.im_resourceItem_br; ";
ArrayList<String[]> items = new ArrayList<String[]>();
TableView<ResourceItem> itemsInDB = new TableView();
items = getQueryResult(query);
//itemsInDB.setEditable(false);
ResourceItem item = new ResourceItem("", "");
ObservableList<ResourceItem> data = FXCollections.observableArrayList();
data.removeAll(data);
//System.out.println(items.get(0).toString());
for (int i = 0; i < items.size(); i++) {
item.setItemCode(items.get(i)[1]);
item.setItemName(items.get(i)[2]);
data.add(item);
}
code.setCellValueFactory(new PropertyValueFactory("itemCode"));
name.setCellValueFactory(new PropertyValueFactory("itemName"));
itemsInDB.setItems(data);
System.out.println(itemsInDB.getItems());
itemsInDB.getColumns().addAll(code, name);
table.getColumns().addAll(code, name);
//adds previously defined columns to the table in the order they will appear
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.getChildren().addAll(label, table); //adds label and course table to VBox layout container
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene); //adds scene to the stage
stage.show(); //displays stage
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
public ArrayList getQueryResult(String stmt) throws ClassNotFoundException {
String results = "";
ResultSet resultSet = null;
String row = "";
ArrayList<String[]> list = new ArrayList<String[]>();
try {
Class.forName(DRIVER);
connection = DriverManager.getConnection(urlHead, USER, PASS);
statement = connection.createStatement();
resultSet = statement.executeQuery(stmt);
int columnCount = resultSet.getMetaData().getColumnCount();
while (resultSet.next()) {
String delims = "[%]";
row = "";
for (int i = 1; i <= columnCount; i++) {
row += resultSet.getString(i) + "%";
}
String[] array = row.split(delims);
list.add(array);
}
} catch (SQLException e) {
e.printStackTrace();
}
return list;
}
public static class ResourceItem {
public SimpleStringProperty itemCode, itemName;
public ResourceItem(String code, String name) {
this.itemCode = new SimpleStringProperty(code);
this.itemName = new SimpleStringProperty(name);
}
public String getItemCode() {
return itemCode.get();
}
public void setItemCode(String code) {
itemCode.set(code);
}
public SimpleStringProperty itemCodeProperty() {
return itemCode;
}
public SimpleStringProperty itemNameProperty() {
return itemName;
}
public String getItemName() {
return itemName.get();
}
public void setItemName(String name) {
itemName.set(name);
}
public String toString() {
String print = itemCode + " " + itemName + " ";
return print;
}
}
}
You have created two tables: one called table, which you display in the UI when you add it to vbox, and another called itemsInDB, which you populate but never display. Since the table you do display has no data in it, and the table you populate is never displayed, you never see the data.
You have another logical error when you populate the observable list: you add the same item over and over to the list, and update its properties each time. So if it were displayed you would see the same item repeatedly in the table.
There may be other errors I haven't noticed, but since you haven't created a MCVE, I can't actually run it and test it.

Double binding: Model <-> Bean <-> JavaFX

Considering I have the following three classes:
Model:
class Field {
private String label;
}
Bean:
class FieldBean {
public FieldBean(Field f) { this.field = f};
private Field field;
private SimpleStringProperty label = new SimpleStringProperty();
public String getLabel() { return label.get(); }
}
JavaFX Application:
class MyApp extends Application {
public void start(Stage stage) {
Label lblTest = new Label();
FieldBean fieldBean = new FieldBean(model.getField());
Bindings.bindBidirectional(fieldBean.getLabel(), label.textProperty());
}
}
What I am trying to achieve is to have the Label updated, whenever i change the Field label. From what I know of this model binding so far, I need to add a PropertyChangeListener, but I haven't got a clue, where about it should be attached. My guess would be in the FieldBean. (and my model already has property change support, just stripped it for better readability).
You can use a JavaBeanStringProperty, which is basically an adapter between a JavaFX StringProperty and a bound JavaBeans property. For example:
class FieldBean {
private final StringProperty label ;
private final Field field;
public FieldBean(Field f) {
this.field = f;
label = JavaBeanStringPropertyBuilder.create()
.bean(this.field)
.name(label)
.build();
public String getLabel() { return label.get(); }
public StringProperty labelProperty() { return label ; }
public void setLabel(String label) { this.label.set(label); }
}
then (assuming you have property change listeners set up for Field.label), Field.label and FieldBean.label will automatically be bound.
As Tomas points out, you don't need both of these classes as they effectively represent exactly the same thing. You can omit the Field class as Tomas shows, or, if your Field class already exists as part of a data representation that's already written, you can just use the JavaBeanStringProperty as an adapter to bind directly to a bound JavaBean property:
Field field = new Field();
// ...
Label uiLabel = new Label();
uiLabel.textProperty().bind(JavaBeanStringPropertyBuilder.create()
.bean(field)
.name(label)
.build());
Calling field.setLabel(...) now automatically changes uiLabel's text. In this example the FieldBean class is omitted entirely.
If FieldBean.field.label and FieldBean.label.get() are meant to represent the same value, then there is no need to have them both.
class FieldBean {
private StringProperty label;
public FieldBean(String label) {
this.label = new SimpleStringProperty(label);
}
public StringProperty labelProperty() { return label; }
public String getLabel() { return label.get(); }
}
Then, to keep a Label updated, you just do
label.textProperty().bind(fieldBean.labelProperty());
Notice that the class Field is omitted altogether.

How to get at Apache Wicket PropertyColumn data to dynamically change CSS class based on content?

I have an Apache Wicket page that has a DataTable with a column that needs to show the statuses, Red, Yellow, Green. If the content of the column is Red, I want to change the CSS class to red-status, if yellow yellow-status, else green-status. I can't seem to get at the data in the way you can from a clickable property column. How do you get at the data in a PropertyColumn, or is there another way to do this in a DataTable? Thank you!
UPDATE
Thank you, Martin. Here's what I came up with:
#Override
public void populateItem(Item<ICellPopulator<T>> cellItem, String componentId, final IModel<T> rowModel) {
Label label = new Label(componentId, getDataModel(rowModel));
cellItem.add(label);
LOGGER.debug("populateItem: label DefaultModelObject: {}", (String) label.getDefaultModelObject());
label.add(new AttributeModifier("class", new AbstractReadOnlyModel<String>() {
private static final long serialVersionUID = 1L;
ProcessingTime processingTime = (ProcessingTime) rowModel.getObject();
#Override
public String getObject() {
String cssClass = null;
if (StringUtils.equals("Red", processingTime.getStatus())) {
cssClass = "red-status";
} else if (StringUtils.equals("Yellow", processingTime.getStatus())) {
cssClass = "yellow-status";
} else if (StringUtils.equals("Green", processingTime.getStatus())) {
cssClass = "green-status";
} else {
cssClass = "process-status";
}
return cssClass;
}
}));
}
First thing first, look at the populateItem of PropertyColumn, how does the implementation looks like, in Wicket 6 (similar like other versions) it is:
public class PropertyColumn<T, S> extends AbstractColumn<T, S> implements IExportableColumn<T, S, Object>
...
#Override
public void populateItem(final Item<ICellPopulator<T>> item, final String componentId,
final IModel<T> rowModel)
{
item.add(new Label(componentId, createLabelModel(rowModel)));
}
...
}
You have to modify the inner component that is create as the label of the column.
First method: create your own component (also your component is able to contain its own mechanism of creation css class or style instead of adding an AttributeModifier here):
#Override
public void populateItem(final Item<ICellPopulator<T>> item, final String componentId,
final IModel<T> rowModel)
{
super.populateItem(item, componentId, rowModel);
MarkupContainer c = item.get(componentId);
c.add(new AttributeModifier("class", new AbstractReadonlyModel<String>() {
private static final long serialVersionUID = 1L;
#Override
public String getObject() {
// some logic how to which css you want to apply
return "MY-CSS-CLASS";
}
}));
}
or you can let Wicket to create the Label component itself and you just add an AttributeModifier:
#Override
public void populateItem(final Item<ICellPopulator<T>> item, final String componentId, final IModel<T> rowModel)
{
super.populateItem(item, componentId, rowModel);
Label l = new Label(componentId, createLabelModel(rowModel));
item.add(l);
l.add(new AttributeModifier("class", new AbstractReadonlyModel<String>() {
private static final long serialVersionUID = 1L;
#Override
public String getObject() {
// some logic how to which css you want to apply
return "MY-CSS-CLASS";
}
}));
}
NOTE: the method 'createLabelModel' is deprecated in Wicket 6, rather to use 'getDataModel' instead.

Resources