0

So after my first ever assignment with JavaFX I was assigned a project of what I would consider massive proportions for only being my 2nd time with JavaFX. I had to create a photo album with basic functionality: add images, remove images, sort by title/description/location, the ability to edit the title, date taken, description, and location. Good news: I've gotten all but one of these things complete.

Deleting a photo, or rather getting it off the screen after its been removed from the imageData array has proven to complicated. I'm pretty sure this is due to the way I have organized this. I tried making my own "changedProperty" for the Album class and observing it from the source but apparently I somehow mucked that up or it doesn't work the way I thought it did.

private BooleanProperty changed = new SimpleBooleanProperty();
public final boolean getChanged(){return changed.get();}
public final void setChanged(boolean value){changed.set(value);}
public BooleanProperty changedProperty(){return changed;}

So that was a flop. I also tried an ObservableList but because that was created in source I could not obsList.remove() from within my Album class. Again, I may have just implemented it wrong.

So just to reiterate - I am able to remove the images from the ArrayList imageData but they stay in the gallery FlowPane.

Source.java

import javafx.application.Application;
import javafx.geometry.*;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.*;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.File;


/**
 * Created by Alyssa on 9/27/2015.
 */
public class Source extends Application  {

    protected Stage mainWin;
    protected FlowPane gallery;
    private static boolean delete;

    public static void main(String[] args){
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception{
        Album album = new Album();

        Scene mainScene;

        for(int i = 0; i < 52;i++) {
            album.getImageData().add(new ImageData(("cards/" + String.valueOf(i + 1) + ".png"), "cardtitle" + i, "carddesc", "cardloc"));
        }

        album.getObsList().addListener(new ListChangeListener(){ 
        @Override 
        public void onChanged(ListChangeListener.Change change){ 
            refreshGallery(); } 
        });

        //FILE MENU
        Menu fileMenu = new Menu("_File");
        MenuItem addImg = new MenuItem("_Add Photo...");
        addImg.setOnAction(e -> {
            FileChooser fileChooser = new FileChooser();
            fileChooser.setTitle("Open Resource File");
            File file = fileChooser.showOpenDialog(mainWin);
            String absPath = "file:" + file.getAbsolutePath();
            absPath = absPath.replace('\\', '/');
            album.addImage(absPath, "","","");
            refreshGallery();
        });

        MenuItem exit = new MenuItem("_Exit");
        exit.setOnAction(e -> mainWin.close());
        fileMenu.getItems().addAll(addImg, new SeparatorMenuItem(), exit);

        //SORT MENU
        Menu sortMenu = new Menu("_Sort");
        MenuItem sortTitle = new MenuItem("_Title");
        sortTitle.setOnAction(e -> {
            album.sortAlbumTitle(album);
            refreshGallery();
        });
        MenuItem sortDate = new MenuItem("_Date Taken");
        sortDate.setOnAction(e -> {
            album.sortAlbumDate(album);
            refreshGallery();
        });
        MenuItem sortLoc = new MenuItem("_Location");
        sortLoc.setOnAction(e -> {
            album.sortAlbumLoc(album);
            refreshGallery();
        });

        sortMenu.getItems().addAll(sortTitle, sortDate,sortLoc);

        //HELP MENU
        Menu helpMenu = new Menu("_Help");
        MenuItem helpAbout = new MenuItem("_About");
        helpAbout.setOnAction(e -> Help.displayAbout());
        MenuItem helpHelp = new MenuItem("_Help");
        helpHelp.setOnAction(e -> Help.displayHelp());
        helpMenu.getItems().addAll(helpAbout, helpHelp);

        //MENU BAR
        MenuBar menuBar = new MenuBar();
        menuBar.getMenus().addAll(fileMenu, sortMenu, helpMenu);
        menuBar.setStyle("-fx-background-color: #383838;");

        mainWin = primaryStage;
        mainWin.setTitle("Photo Album");

        ScrollPane center = new ScrollPane();
        //center.setMinWidth(800);
        center.setFitToWidth(true);

        gallery = new FlowPane();
        gallery.setPadding(new Insets(5, 5, 5, 5));
        gallery.setAlignment(Pos.CENTER);
        gallery.setColumnHalignment(HPos.CENTER);
        gallery.setRowValignment(VPos.CENTER);
        gallery.setHgap(10);
        gallery.setVgap(10);
        gallery.setPrefWrapLength(785);

        for (int i =0 ; i < album.getImageData().size(); i++){
            gallery.getChildren().add(album.getImageData().get(i).getImageBtn());
        }

        center.setContent(gallery);

        BorderPane borderPane = new BorderPane();
        borderPane.setCenter(center);
        borderPane.setTop(menuBar);

        mainScene = new Scene(borderPane, 800,600);
        mainScene.getStylesheets().add("styles.css");
        mainWin.setScene(mainScene);
        mainWin.setResizable(false);
        mainWin.show();
    }

    private void refreshGallery(){

        gallery.getChildren().clear();

        for (int i =0 ; i < Album.getImageData().size(); i++){
            gallery.getChildren().add(Album.getImageData().get(i).getImageBtn());
        }
    }
}

Album.java

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.image.ImageView;
import java.util.*;

/**
 * Created by Alyssa on 9/27/2015.
 */
public class Album extends Observable{
    private static List<ImageData> imageData = new ArrayList<>();

    ObservableList<ImageData> observableList = FXCollections.observableList(imageData);
    public ObservableList<ImageData> getObsList(){return observableList;}

    private BooleanProperty changed = new SimpleBooleanProperty();
    public final boolean getChanged(){return changed.get();}
    public final void setChanged(boolean value){changed.set(value);}
    public BooleanProperty changedProperty(){return changed;}

    public Album(){}

    public static List<ImageData> getImageData(){ return imageData; }

    public void addImage(String image, String title, String desc, String loc) {
        imageData.add(0, new ImageData(image, title, desc, loc));
    }

    public void removeImage(ImageView img){

        List<ImageData> toRemove = new ArrayList<>();
        for(ImageData a: imageData){
            if(a.getImageView() == img){
                toRemove.add(a);
                //observableList.remove(a);
                break;
            }
        }

        imageData.removeAll(toRemove);
        if (this.getChanged() == true)
            this.setChanged(false);
        else
            this.setChanged(true);
    }

    public void sortAlbumTitle(Album album) {
        System.out.println("Sort by Title");
        Collections.sort(album.getImageData(), new Comparator<ImageData>() {
            public int compare(ImageData img1, ImageData img2) {
                return img1.getTitle().compareToIgnoreCase(img2.getTitle());
            }
        });
    }

    public void sortAlbumDate(Album album) {
        System.out.println("Sort by Date");
        Collections.sort(album.getImageData(), new Comparator<ImageData>() {
            public int compare(ImageData img1, ImageData img2) {
                return img1.getDate().compareTo(img2.getDate());
            }
        });
    }

    public void sortAlbumLoc(Album album) {
        System.out.println("Sort by description");
        Collections.sort(album.getImageData(), new Comparator<ImageData>() {
            public int compare(ImageData img1, ImageData img2) {
                return img1.getLocation().compareToIgnoreCase(img2.getLocation());
            }
        });
    }
}

ImageData.java

import javafx.event.EventHandler;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;


/**
 * Created by Alyssa on 9/27/2015.
 */
public class ImageData extends Album implements EventHandler<MouseEvent>{

private Image image;
private ImageView smImgView;
private ImageView imgView;
private String title = null;
private String desc = null;
private Date dateAdded = null;
private Date date = null;
private String location = null;
private Button btn;



public ImageData(){};
public ImageData(String img, String nTitle, String nDesc, String loc){

    image = new Image(img);
    smImgView = new ImageView(image);   //thumbnail
    getSmImageView().setFitWidth(80);
    smImgView.setPreserveRatio(true);
    imgView = new ImageView(image); //fullsize

    //image info
    title = nTitle;
    desc=nDesc;
    location = loc;
    dateAdded = new java.util.Date();
    date = new java.util.Date();


    btn = new Button();
    btn.setGraphic(smImgView);
    btn.setOnMouseClicked(this);

}

//getters/setters
public String getTitle(){ return title; }

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

public String getDesc() { return desc; }

public void setDesc(String nDesc) { this.desc = nDesc; }

public java.util.Date getDateAdded() { return dateAdded; }

public Date getDate() { return date; }

public void setDate(Date nDate) { this.date = nDate; }

public String getLocation() { return location; }

public void setLocation(String nLocation) { this.location = nLocation; }

public ImageView getSmImageView(){ return this.smImgView; }

public ImageView getImageView(){ return this.imgView; }

public String getImage(){return this.image.toString(); }

public Button getImageBtn(){return this.btn; }

@Override
public void handle(MouseEvent event) {
    ArrayList arr = DisplayEditDetail.display(imgView, title, dateAdded, date, desc, location);
    title = arr.get(0).toString();

    desc = arr.get(2).toString();
    location = arr.get(3).toString();
    if (arr.get(1) != null) {
        try {
            SimpleDateFormat dateformat = new SimpleDateFormat("MM/dd/yyyy");
            Date nDate = dateformat.parse(arr.get(1).toString());
            date = nDate;
        }
        catch (ParseException ex) {

        }

    }
    if (arr.get(4) == "True") {
        super.removeImage(imgView);
    }
}
}

DisplayEditDetail.java

import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import java.util.ArrayList;

/**
 * Created by Alyssa on 9/27/2015.
 */
public class DisplayEditDetail extends Album{

private Boolean delete = false;

public static ArrayList<String> display(ImageView img, String title, Date dateAdded, Date date, String desc, String loc){

    String cdate = null, olddate = null;
    if (date != null){
        cdate = date.toString();
        DateFormat df = new SimpleDateFormat("MM/dd/YYYY");
        olddate = df.format(date);
    }

    ArrayList<String> changes = new ArrayList<>();
    changes.add(title);
    changes.add(cdate);
    changes.add(desc);
    changes.add(loc);
    changes.add("False");

    Stage window = new Stage();

    window.initModality(Modality.APPLICATION_MODAL);
    window.setTitle(title);
    window.setWidth(img.getFitWidth());

    Label dateALbl = new Label("Date Added: " + dateAdded.toString());

    Label titleLbl = new Label("Title");
    TextField titleTf = new TextField(title);
    int TITLE_MAX = 100;
    titleTf.textProperty().addListener(new ChangeListener<String>(){
        @Override
        public void changed(ObservableValue<? extends String> observable,
                            String oldValue, String newValue) {

            if(titleTf.getText().length() > TITLE_MAX ) {
                titleTf.setText(titleTf.getText().substring(0, TITLE_MAX ));
            }
        }
    });


    Label dateTLbl = new Label("Date Taken");

    TextField dateTf = new TextField(olddate);

    Label descLbl = new Label("Description");
    TextArea descTf = new TextArea(desc);
    descTf.setWrapText(true);
    descTf.setMaxWidth(250);
    descTf.setMaxHeight(100);
    int DESC_MAX = 300;
    descTf.textProperty().addListener(new ChangeListener<String>() {
        @Override
        public void changed(ObservableValue<? extends String> observable,
                            String oldValue, String newValue) {

            if(descTf.getText().length() > DESC_MAX){
                descTf.setText(descTf.getText().substring(0, DESC_MAX));
            }
        }
    });

    Label locLbl = new Label("Location");
    TextField locTf = new TextField(loc);
    //Create two buttons

    Button confirmBtn = new Button("Save");
    Button cancelBtn = new Button("Cancel");
    Button deleteBtn = new Button("Delete");

    confirmBtn.setOnAction(e-> {
        try {
            changes.set(0, titleTf.getText());
            changes.set(1, dateTf.getText());
            if (dateTf.getText() != null) {    //throws parse exception
                SimpleDateFormat dateformat = new SimpleDateFormat("MM/dd/yyyy");
                Date nDate = dateformat.parse(dateTf.getText());
            }
            changes.set(2, descTf.getText());
            changes.set(3, locTf.getText());
            window.close();
        }
        catch(ParseException ex) {
            AlertBox.display("Invalid Date", "Error: Invalid Date Format.\n Use format MM/DD/YYYY");
            window.fireEvent(new WindowEvent(window, WindowEvent.WINDOW_CLOSE_REQUEST));
        }
    });

    cancelBtn.setOnAction(e-> {
        window.close();
    });

    deleteBtn.setOnAction(e ->{
        changes.set(4, "True");
        window.close();
    });

    window.setOnCloseRequest(e-> {
        boolean confirm = ConfirmBox.display("Exit?", "Are you sure you want to exit without saving?");
        e.consume();
        if (confirm)
            window.close();

    });

    HBox buttons = new HBox(10);
    buttons.getChildren().addAll(confirmBtn, cancelBtn, deleteBtn);
    buttons.setAlignment(Pos.CENTER);
    buttons.setPadding(new Insets(5,5,5,5));

    VBox details = new VBox(5);
    details.getChildren().addAll(dateALbl, titleLbl, titleTf, dateTLbl, dateTf, descLbl, descTf, locLbl, locTf, buttons);
    details.setAlignment(Pos.CENTER);
    details.setPadding(new Insets(5,5,5,5));

    HBox layout = new HBox(10);
    layout.getChildren().addAll(img, details);
    layout.setAlignment(Pos.CENTER);
    layout.setPadding(new Insets(10,5,10,10));
    layout.setStyle("-fx-background-color: #1d1d1d");

    Scene scene = new Scene(layout);
    scene.getStylesheets().add("styles.css");
    window.setScene(scene);
    window.showAndWait(); //must be closed before anything else

    return changes;

}

}
Aly
  • 57
  • 1
  • 9
  • You should define your `imageData` in `Album` as an ObservableList together with getter methods (one returning the content as a list, and one returning the ObservableList itself). Then you can add a `ChangeListener` in `Source` on that property to update the view. – hotzst Oct 05 '15 at 07:27
  • @hotzst I'm not sure I completely understand how to do that. Is there an example you could direct me to? – Aly Oct 05 '15 at 07:35
  • The closest thing that I could find for the `ChangeListener` is this: http://stackoverflow.com/questions/24782280/bind-over-the-observablelist-in-javafx. For the definition of the observable list, I suggest you search for ObservableList examples or tutorials. – hotzst Oct 05 '15 at 07:43
  • When I make the getter for the `ObservableList` does that not return the content? – Aly Oct 05 '15 at 19:34
  • @hotzst I added `ObservableList observableList = FXCollections.observableList(imageData);` and `public ObservableList getObsList(){return observableList;}` to the Album.java file. And I added `album.getObsList().addListener(new ListChangeListener(){ Override public void onChanged(ListChangeListener.Change change){ refreshGallery(); } });` to my Source.java file But this hasn't worked. Yes I included the [at] symbol before the ovverride.. the comment wouldn't allow me to include a second [at] symbol beyond the one used to tag you. – Aly Oct 05 '15 at 20:03
  • i have changed some code .it is working for me,but can u explain what are u doing in deletebtn onAction, then i ll post working code – Gaali Prabhakar Oct 06 '15 at 05:13
  • @user99370 I figured the only way for me to get the delete command back up and out through the classes was to pass it back in an array. Since I was already passing other values back I passed a value back for that as well. – Aly Oct 06 '15 at 05:40
  • Could you update the code in your code with the changes you made in the meantime as well as the code for the deletbtn as requested by @user99370? – hotzst Oct 06 '15 at 05:42
  • @hotzst I updated the changes to my code the code for the deletebtn has been in DisplayEditDetail the entire time. Nothing changed with that. Like I said.. the image deleted from the array list... just not from the FlowPane. – Aly Oct 06 '15 at 05:50
  • " I am able to remove the images from the ArrayList imageData but they stay in the gallery FlowPane. Why?". Because you are not "refreshing the gallery" after removing the image. You have defined `changedProperty` but I could not observe its usage. You should compare objects with `equals()` instead of `==` operator, at `arr.get(4) == "True"` and `a.getImageView() == img`. – Uluk Biy Oct 06 '15 at 06:34

1 Answers1

0

When you removing image .your removing from List, you have to remove form ObservableList then only it calls changeListListner.Here is my working code Change this classes it will work for you any ?s comment

Album.java

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.image.ImageView;

import java.util.*;


public class Album extends Observable{
private static ObservableList<ImageData> imageData = FXCollections.observableArrayList();

private BooleanProperty changed = new SimpleBooleanProperty();
public final boolean getChanged(){return changed.get();}
public final void setChanged(boolean value){changed.set(value);}
public BooleanProperty changedProperty(){return changed;}

public Album(){}

public static ObservableList<ImageData> getImageData(){ return imageData; }

public void addImage(String image, String title, String desc, String loc) {
    imageData.add(0, new ImageData(image, title, desc, loc));
}

public void removeImage(ImageView img){

    List<ImageData> toRemove = new ArrayList<>();
    for(ImageData a: imageData){
        if(a.getImageView() == img){
            toRemove.add(a);
            //observableList.remove(a);
            break;
        }
    }

    imageData.removeAll(toRemove);
    if (this.getChanged() == true)
        this.setChanged(false);
    else
        this.setChanged(true);
}

public void sortAlbumTitle(Album album) {
    System.out.println("Sort by Title");
    Collections.sort(album.getImageData(), new Comparator<ImageData>() {
        public int compare(ImageData img1, ImageData img2) {
            return img1.getTitle().compareToIgnoreCase(img2.getTitle());
        }
    });
}

public void sortAlbumDate(Album album) {
    System.out.println("Sort by Date");
    Collections.sort(album.getImageData(), new Comparator<ImageData>() {
        public int compare(ImageData img1, ImageData img2) {
            return img1.getDate().compareTo(img2.getDate());
        }
    });
}

public void sortAlbumLoc(Album album) {
    System.out.println("Sort by description");
    Collections.sort(album.getImageData(), new Comparator<ImageData>() {
        public int compare(ImageData img1, ImageData img2) {
            return img1.getLocation().compareToIgnoreCase(img2.getLocation());
        }
    });
}
}

Source.java

import javafx.application.Application;
import javafx.collections.ListChangeListener;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.FileChooser;
import javafx.stage.Stage;

import java.io.File;


public class Source extends Application {

protected Stage mainWin;
protected FlowPane gallery;
private static boolean delete;

public static void main(String[] args) {
    launch(args);
}

@Override
public void start(Stage primaryStage) throws Exception {
    Album album = new Album();

    Scene mainScene;

    for (int i = 1; i < 10; i++) {
        String s = "D:\\prabhu\\w\\1";
        album.getImageData().add(
                new ImageData((s + " (" + i + ").jpg"), "cardtitle" + i,
                        "carddesc", "cardloc"));
    }

    album.getImageData().addListener(new ListChangeListener() {
        @Override
        public void onChanged(ListChangeListener.Change change) {
            refreshGallery();
        }
    });

    // FILE MENU
    Menu fileMenu = new Menu("_File");
    MenuItem addImg = new MenuItem("_Add Photo...");
    addImg.setOnAction(e -> {
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("Open Resource File");
        File file = fileChooser.showOpenDialog(mainWin);
        String absPath = "file:" + file.getAbsolutePath();
        absPath = absPath.replace('\\', '/');
        album.addImage(absPath, "", "", "");
        refreshGallery();
    });

    MenuItem exit = new MenuItem("_Exit");
    exit.setOnAction(e -> mainWin.close());
    fileMenu.getItems().addAll(addImg, new SeparatorMenuItem(), exit);

    // SORT MENU
    Menu sortMenu = new Menu("_Sort");
    MenuItem sortTitle = new MenuItem("_Title");
    sortTitle.setOnAction(e -> {
        album.sortAlbumTitle(album);
        refreshGallery();
    });
    MenuItem sortDate = new MenuItem("_Date Taken");
    sortDate.setOnAction(e -> {
        album.sortAlbumDate(album);
        refreshGallery();
    });
    MenuItem sortLoc = new MenuItem("_Location");
    sortLoc.setOnAction(e -> {
        album.sortAlbumLoc(album);
        refreshGallery();
    });

    sortMenu.getItems().addAll(sortTitle, sortDate, sortLoc);

    // HELP MENU
    Menu helpMenu = new Menu("_Help");
    MenuItem helpAbout = new MenuItem("_About");
    helpAbout.setOnAction(e -> Help.displayAbout());
    MenuItem helpHelp = new MenuItem("_Help");
    helpHelp.setOnAction(e -> Help.displayHelp());
    helpMenu.getItems().addAll(helpAbout, helpHelp);

    // MENU BAR
    MenuBar menuBar = new MenuBar();
    menuBar.getMenus().addAll(fileMenu, sortMenu, helpMenu);
    menuBar.setStyle("-fx-background-color: #383838;");

    mainWin = primaryStage;
    mainWin.setTitle("Photo Album");

    ScrollPane center = new ScrollPane();
    // center.setMinWidth(800);
    center.setFitToWidth(true);

    gallery = new FlowPane();
    gallery.setPadding(new Insets(5, 5, 5, 5));
    gallery.setAlignment(Pos.CENTER);
    gallery.setColumnHalignment(HPos.CENTER);
    gallery.setRowValignment(VPos.CENTER);
    gallery.setHgap(10);
    gallery.setVgap(10);
    gallery.setPrefWrapLength(785);

    for (int i = 0; i < album.getImageData().size(); i++) {
        gallery.getChildren()
                .add(album.getImageData().get(i).getImageBtn());
    }

    center.setContent(gallery);

    BorderPane borderPane = new BorderPane();
    borderPane.setCenter(center);
    borderPane.setTop(menuBar);

    mainScene = new Scene(borderPane, 800, 600);
    mainScene.getStylesheets().add("styles.css");
    mainWin.setScene(mainScene);
    mainWin.setResizable(false);
    mainWin.show();
}

private void refreshGallery() {

    gallery.getChildren().clear();

    for (int i = 0; i < Album.getImageData().size(); i++) {
        gallery.getChildren()
                .add(Album.getImageData().get(i).getImageBtn());
    }
}
}

I have chaned just 3 lines of code. i hope this will help u. and any ?s post a comment

Gaali Prabhakar
  • 583
  • 6
  • 23