JavaFX Update FlowPane после изменения списка

Итак, после моего первого задания с JavaFX мне поручили проект масштабных масштабов, поскольку я только второй раз работал с JavaFX. Мне нужно было создать фотоальбом с базовым функционалом: добавление изображений, удаление изображений, сортировка по названию/описанию/местоположению, возможность редактирования названия, даты съемки, описания и местоположения. Хорошие новости: я сделал все, кроме одной из этих вещей.

Удаление фотографии или, вернее, убрать ее с экрана после того, как она была удалена из массива imageData, оказалось сложной задачей. Я почти уверен, что это связано с тем, как я это организовал. Я попытался создать свой собственный «changedProperty» для класса Album и наблюдать за ним из источника, но, видимо, я как-то это испортил или он не работает так, как я думал.

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

Так что это был провал. Я также пробовал ObservableList, но поскольку он был создан в исходном коде, я не мог obsList.remove() из своего класса Album. Опять же, я, возможно, просто реализовал это неправильно.

Так что просто повторюсь - я могу удалить изображения из ArrayList imageData, но они остаются в галерее FlowPane.

Источник.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;

}

}

person Aly    schedule 05.10.2015    source источник
comment
Вы должны определить свой imageData в Album как ObservableList вместе с методами получения (один возвращает содержимое в виде списка, а другой возвращает сам ObservableList). Затем вы можете добавить ChangeListener в Source этого свойства, чтобы обновить представление.   -  person hotzst    schedule 05.10.2015
comment
@hotzst Я не уверен, что полностью понимаю, как это сделать. Есть ли пример, на который вы могли бы мне направить?   -  person Aly    schedule 05.10.2015
comment
Самое близкое, что я смог найти для ChangeListener, это: javafx">stackoverflow.com/questions/24782280/. Для определения наблюдаемого списка я предлагаю вам поискать примеры или учебные пособия по ObservableList.   -  person hotzst    schedule 05.10.2015
comment
Когда я делаю геттер для ObservableList, он не возвращает содержимое?   -  person Aly    schedule 05.10.2015
comment
@hotzst Я добавил ObservableList<ImageData> observableList = FXCollections.observableList(imageData); и public ObservableList<ImageData> getObsList(){return observableList;} в файл Album.java. И я добавил album.getObsList().addListener(new ListChangeListener(){ Override public void onChanged(ListChangeListener.Change change){ refreshGallery(); } }); в свой файл Source.java. Но это не сработало. Да, я включил символ [at] перед ovverride.. комментарий не позволил бы мне включить второй символ [at] помимо того, который используется для пометки вас.   -  person Aly    schedule 05.10.2015
comment
я изменил некоторый код. он работает для меня, но можете ли вы объяснить, что вы делаете в deletebtn onAction, тогда я опубликую рабочий код   -  person Gaali Prabhakar    schedule 06.10.2015
comment
@user99370 user99370 Я решил, что единственный способ получить команду удаления обратно и через классы - это передать ее обратно в массиве. Поскольку я уже передавал другие значения, я также передал значение для этого.   -  person Aly    schedule 06.10.2015
comment
Не могли бы вы обновить код в своем коде с изменениями, которые вы сделали за это время, а также код для удаления, как запросил @user99370?   -  person hotzst    schedule 06.10.2015
comment
@hotzst Я обновил изменения в своем коде, код для deletebtn все время находился в DisplayEditDetail. При этом ничего не изменилось. Как я уже сказал... изображение удалено из списка массивов... только не из FlowPane.   -  person Aly    schedule 06.10.2015
comment
Я могу удалить изображения из ArrayList imageData, но они остаются в галерее FlowPane. Почему?. Потому что вы не обновляете галерею после удаления изображения. Вы определили changedProperty, но я не мог наблюдать за его использованием. Вы должны сравнивать объекты с оператором equals() вместо ==, в arr.get(4) == "True" и a.getImageView() == img.   -  person Uluk Biy    schedule 06.10.2015


Ответы (1)


Когда вы удаляете изображение. Ваше удаление из списка, вы должны удалить форму ObservableList, тогда только он вызывает changeListListner. Вот мой рабочий код. Измените эти классы, он будет работать для вас, любой комментарий?

Альбом.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());
        }
    });
}
}

Источник.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());
    }
}
}

Я изменил всего 3 строки кода. Я надеюсь, что это поможет вам. и любой? Оставить комментарий

person Gaali Prabhakar    schedule 06.10.2015