How to search subfolders and repaint a jTable with new data in Java?

泪湿孤枕 提交于 2019-12-28 07:09:13

问题


I'm having a couple problems with my program. I wanted a jTable to display various data points added to its TableModel, but I can't seem to get the table to update with the new data.

Also, it seems my search for png images within a directory has fallen short of actually searching the directory completely for all png images and stops only at the first folder's contents.

I've placed all data points into appropriate ArrayLists for their data types and converted them to Object arrays before placing them within the TableModel of my jTable. It's not worked, and I've come short of solving these problems with my own knowledge.

Some code:

private void btnPatchSelActionPerformed(java.awt.event.ActionEvent evt) {                                            
    fchsFolderChooser.showOpenDialog(null);
    File selFile = fchsFolderChooser.getSelectedFile();
    fldPatchSel.setText(selFile.getPath());
    File pngs[] = selFile.listFiles(IMAGE_FILTER);
    for (File png : pngs) {
        try {BufferedImage img = ImageIO.read(png);
            patchWs.add(img.getWidth()); patchHs.add(img.getHeight());
            patchDims.add(img.getWidth() + "x" + img.getHeight());
            patchImgs.add(img); patchImgPaths.add(png.getPath());
            sels.add(false);
            System.out.println("Found " + png.getPath());
        } catch (IOException e) {
            System.out.println("Bad image: " + png); e.printStackTrace();
        }
    }
    if(!(fldPatchSel.getText().endsWith("...")||fldModSel.getText().endsWith("..."))) {BuildTable();}
}

The mod asset selection's counterpart is analogous but without setting the sels ArrayList.

private void BuildTable(){
        DefaultTableModel model = (DefaultTableModel) tblImgList.getModel();
        Object[] boolSels = sels.toArray();
        Object[] stringPatchPaths = patchImgPaths.toArray();
        Object[] stringPatchImgDims = patchDims.toArray();
        model.addColumn("", boolSels);
        model.addColumn("Patch Image", stringPatchPaths);
        model.addColumn("W x H", stringPatchImgDims);
        tblImgList.repaint();
    }

How would you solve my two problems? The program's not searching subfolders and not able to display new data in the table.


回答1:


First, drop the DefaultTableModel, you data model is becoming to complex. Start with a POJO (Plain Old Java Object) which wraps the context of what you want to display...

public class ImageProperty {

    private File source;
    private Dimension size;
    private boolean selected;

    public ImageProperty(File source, Dimension size, boolean selected) {
        this.source = source;
        this.size = size;
        this.selected = selected;
    }

    public Dimension getSize() {
        return size;
    }

    public File getSource() {
        return source;
    }

    public boolean isSelected() {
        return selected;
    }

}

This holds all the information for a single image file and will be used to represent the contents of a row in the table.

Second, create a custom TableModel. This is kind of a personal thing, but with DefaultTableModel, you spend way more time hacking around it trying to get it to do what you want which would be better solved by using your own...

public class ImageTableModel extends AbstractTableModel {

    private List<ImageProperty> images;

    public ImageTableModel() {
        images = new ArrayList<ImageProperty>(25);
    }

    @Override
    public int getRowCount() {
        return images.size();
    }

    @Override
    public int getColumnCount() {
        return 4;
    }

    @Override
    public String getColumnName(int column) {
        String name = "";
        switch (column) {
            case 1:
                name = "Path";
                break;
            case 2:
                name = "Name";
                break;
            case 3:
                name = "Size";
                break;
        }
        return name;
    }

    @Override
    public Class<?> getColumnClass(int columnIndex) {
        return columnIndex == 0 ? Boolean.class : String.class;
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        ImageProperty row = images.get(rowIndex);
        Object value = null;
        switch (columnIndex) {
            case 0:
                value = row.isSelected();
                break;
            case 1:
                value = row.getSource().getParent();
                break;
            case 2:
                value = row.getSource().getName();
                break;
            case 3:
                value = row.getSize().width + "x" + row.getSize().height;
                break;
        }
        return value;
    }

    public void addImage(ImageProperty image) {

        images.add(image);
        fireTableRowsInserted(images.size() - 1, images.size() - 1);

    }

    public void addImages(List<ImageProperty> newImages) {

        int firstRow = images.size();
        int lastRow = firstRow + newImages.size() - 1;

        images.addAll(newImages);
        fireTableRowsInserted(firstRow, lastRow);

    }

    public void clear() {

        int lastRow = images.size() -1;
        images.clear();
        fireTableRowsDeleted(0, lastRow);

    }

}

Third, don't try and do directory scanning in the Event Dispatching Thread, this will cause you UI to "pause" until the scan is complete, and depending on the size of your images and the number of sub directories, could take minutes to complete.

See Concurrency in Swing for more details...

There are any number of ways you might overcome this, but SwingWorker is probably the simplest, as it provides functionality to provide updates back the EDT safely, so you can update the UI without causing further issues...

public class ScanWorker extends SwingWorker<Object, ImageProperty> {

    private File source;
    private ImageTableModel model;

    public ScanWorker(File source, ImageTableModel model) {
        this.source = source;
        this.model = model;
    }

    @Override
    protected void process(List<ImageProperty> chunks) {
        model.addImages(chunks);
    }

    @Override
    protected Object doInBackground() throws Exception {
        scan(source);
        return null;
    }

    protected void scan(File dir) {
        firePropertyChange("directory", dir.getParent(), dir);
        File pngs[] = dir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return pathname.getName().toLowerCase().endsWith(".png");
            }
        });
        for (File png : pngs) {
            try {
                BufferedImage img = ImageIO.read(png);
                publish(new ImageProperty(png, new Dimension(img.getWidth(), img.getHeight()), false));
            } catch (IOException e) {
                System.out.println("Bad image: " + png);
                e.printStackTrace();
            }
        }
        File dirs[] = dir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return pathname.isDirectory();
            }
        });
        if (dirs != null && dirs.length > 0) {
            for (File subDir : dirs) {
                scan(subDir);
            }
        }
    }

}

This worker uses a recursive method call to scan subdirectories...

And, finally the glue...

This snippet disables the "scan" button, clears the current table model, creates the SwingWorker and registers a PropertyChangeListener to it and finally starts the worker...

model.clear();
scan.setEnabled(false);
ScanWorker worker = new ScanWorker(new File("..."), model);
worker.addPropertyChangeListener(new PropertyChangeListener() {
    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        if ("state".equalsIgnoreCase(evt.getPropertyName())) {
            SwingWorker.StateValue state = (SwingWorker.StateValue) evt.getNewValue();
            scan.setEnabled(state == SwingWorker.StateValue.DONE);
            scan.setText("Scan");
        } else if ("directory".equalsIgnoreCase(evt.getPropertyName())) {

            scan.setText(text);

        }
    }
});
worker.execute();

The PropertyChangeListener monitors for changes to the worker's state and resets the button to enabled when it completes. It also monitors for changes to the scanning directory and updates the text of the button to reflect the current directory been processed...nice

The above code snippet is triggered by the ActionListener attached to the "scan" button

And a nice runnable example...

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.AbstractTableModel;

public class ShowImages {

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

    public ShowImages() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                final ImageTableModel model = new ImageTableModel();
                JTable table = new JTable(model);

                final JButton scan = new JButton("Scan");
                scan.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        model.clear();
                        scan.setEnabled(false);
                        ScanWorker worker = new ScanWorker(new File("C:\\Users\\shane\\Dropbox\\MegaTokyo"), model);
                        worker.addPropertyChangeListener(new PropertyChangeListener() {
                            @Override
                            public void propertyChange(PropertyChangeEvent evt) {
                                if ("state".equalsIgnoreCase(evt.getPropertyName())) {
                                    SwingWorker.StateValue state = (SwingWorker.StateValue) evt.getNewValue();
                                    scan.setEnabled(state == SwingWorker.StateValue.DONE);
                                    scan.setText("Scan");
                                } else if ("directory".equalsIgnoreCase(evt.getPropertyName())) {

                                    String text = ((File)evt.getNewValue()).getPath().replaceAll("shane", "...");
                                    text = text.replaceAll("C:\\\\", "...");
                                    text = text.replaceAll("Dropbox", "...");
                                    scan.setText(text);

                                }
                            }
                        });
                        worker.execute();
                    }
                });

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new JScrollPane(table));
                frame.add(scan, BorderLayout.SOUTH);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class ImageProperty {

        private File source;
        private Dimension size;
        private boolean selected;

        public ImageProperty(File source, Dimension size, boolean selected) {
            this.source = source;
            this.size = size;
            this.selected = selected;
        }

        public Dimension getSize() {
            return size;
        }

        public File getSource() {
            return source;
        }

        public boolean isSelected() {
            return selected;
        }

    }

    public class ImageTableModel extends AbstractTableModel {

        private List<ImageProperty> images;

        public ImageTableModel() {
            images = new ArrayList<ImageProperty>(25);
        }

        @Override
        public int getRowCount() {
            return images.size();
        }

        @Override
        public int getColumnCount() {
            return 4;
        }

        @Override
        public String getColumnName(int column) {
            String name = "";
            switch (column) {
                case 1:
                    name = "Path";
                    break;
                case 2:
                    name = "Name";
                    break;
                case 3:
                    name = "Size";
                    break;
            }
            return name;
        }

        @Override
        public Class<?> getColumnClass(int columnIndex) {
            return columnIndex == 0 ? Boolean.class : String.class;
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            ImageProperty row = images.get(rowIndex);
            Object value = null;
            switch (columnIndex) {
                case 0:
                    value = row.isSelected();
                    break;
                case 1:
                    value = row.getSource().getParent();
                    break;
                case 2:
                    value = row.getSource().getName();
                    break;
                case 3:
                    value = row.getSize().width + "x" + row.getSize().height;
                    break;
            }
            return value;
        }

        public void addImage(ImageProperty image) {

            images.add(image);
            fireTableRowsInserted(images.size() - 1, images.size() - 1);

        }

        public void addImages(List<ImageProperty> newImages) {

            int firstRow = images.size();
            int lastRow = firstRow + newImages.size() - 1;

            images.addAll(newImages);
            fireTableRowsInserted(firstRow, lastRow);

        }

        public void clear() {

            int lastRow = images.size() -1;
            images.clear();
            fireTableRowsDeleted(0, lastRow);

        }

    }

    public class ScanWorker extends SwingWorker<Object, ImageProperty> {

        private File source;
        private ImageTableModel model;

        public ScanWorker(File source, ImageTableModel model) {
            this.source = source;
            this.model = model;
        }

        @Override
        protected void process(List<ImageProperty> chunks) {
            model.addImages(chunks);
        }

        @Override
        protected Object doInBackground() throws Exception {
            scan(source);
            return null;
        }

        protected void scan(File dir) {
            firePropertyChange("directory", dir.getParent(), dir);
            File pngs[] = dir.listFiles(new FileFilter() {
                @Override
                public boolean accept(File pathname) {
                    return pathname.getName().toLowerCase().endsWith(".png");
                }
            });
            for (File png : pngs) {
                try {
                    BufferedImage img = ImageIO.read(png);
                    publish(new ImageProperty(png, new Dimension(img.getWidth(), img.getHeight()), false));
                } catch (IOException e) {
                    System.out.println("Bad image: " + png);
                    e.printStackTrace();
                }
            }
            File dirs[] = dir.listFiles(new FileFilter() {
                @Override
                public boolean accept(File pathname) {
                    return pathname.isDirectory();
                }
            });
            if (dirs != null && dirs.length > 0) {
                for (File subDir : dirs) {
                    scan(subDir);
                }
            }
        }

    }

}

Take a look at Worker Threads and SwingWorker for more details



来源:https://stackoverflow.com/questions/26477744/how-to-search-subfolders-and-repaint-a-jtable-with-new-data-in-java

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!