Рендеринг BufferedImage в ячейке JTable

Мне нужно отобразить BufferedImage в одном столбце JTable. Я перезаписал метод JTable

@Override
public Class<?> getColumnClass(int column) {
    if (column == 1){
        return BufferedImage.class;
    }
    return super.getColumnClass(column);
}

Но я все еще получаю строковое представление объекта вместо самого изображения. Кто-нибудь знает, что мне не хватает?


person MiSu    schedule 10.02.2013    source источник
comment
Я думаю, что эта ссылка решает это наоборот.   -  person MiSu    schedule 10.02.2013


Ответы (2)


Я бы заполнил столбец, который должен отображать изображение, с помощью ImageIcons, и метод getColumnClass() вернул бы Icon.class, а затем отобразил бы его с помощью JLabel, который отображает значок. На самом деле я считаю, что DefaultCellRenderer действительно является JLabel, и поэтому он уже должен знать, как обращаться с иконками.

Да, все, что нужно модели, это знать, что она содержит Icons. Например, этот код ниже работает в программе ниже:

  DefaultTableModel model = new DefaultTableModel(COL_NAMES, 0) {
     @Override
     public Class<?> getColumnClass(int column) {
        if (getRowCount() > 0) {
           return getValueAt(0, column).getClass();
        }

        return super.getColumnClass(column);
     }
  };

Например:

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;

public class ImageColumnTest2 {
   public static final String IMAGE_SHEET_PATH = "http://speckycdn.sdm.netdna-cdn.com/"
         + "wp-content/uploads/2010/08/flag_icons_04.jpg";
   public static final String[] COUNTRIES = {
      "Denmark", "China", "Chile", "Canada", "Belgium", "Austria",
      "Argentina", "France", "Malaysina", "Lebanon", "Korea", "Japan",
      "Italy", "Ireland", "India", "Hong Kong", "Greece", "Germany"
   };
   public static final int COLS = 6;
   public static final int ROWS = 3;
   private static final String[] COL_NAMES = {"Country", "Flag"};

   private JTable table = new JTable();
   private JScrollPane mainPane = new JScrollPane(table);

   public ImageColumnTest2() throws IOException {
      DefaultTableModel model = new DefaultTableModel(COL_NAMES, 0) {
         @Override
         public Class<?> getColumnClass(int column) {
            if (getRowCount() > 0) {
               return getValueAt(0, column).getClass();
            }

            return super.getColumnClass(column);
         }
      };
      URL url = new URL(IMAGE_SHEET_PATH);
      BufferedImage img = ImageIO.read(url);
      int x1 = 15;  // sorry about the magic numbers
      img = img.getSubimage(x1, 0, img.getWidth() - 2 * x1, img.getHeight());

      int y1 = 20 ;  // ditto!
      int w = img.getWidth() / COLS;
      int h = img.getHeight() / ROWS;
      for (int row = 0; row < ROWS; row++) {
         int y = (row * img.getHeight()) / ROWS;
         for (int col = 0; col < COLS; col++) {
            int x = (col * img.getWidth()) / COLS;
            BufferedImage subImg = img.getSubimage(x, y, w, h);

            subImg = subImg.getSubimage(x1, 0, subImg.getWidth() - 2 * x1, subImg.getHeight() - y1);

            ImageIcon icon = new ImageIcon(subImg);
            String country = COUNTRIES[col + row * COLS];
            Object[] rowData = {country, icon};
            model.addRow(rowData);
         }
      }


      table.setModel(model);
      table.setRowHeight(((ImageIcon)model.getValueAt(0, 1)).getIconHeight());
   }

   public JComponent getMainComponent() {
      return mainPane;
   }

   private static void createAndShowGui() {
      ImageColumnTest2 imgColumnTest = null;
      try {
         imgColumnTest = new ImageColumnTest2();
      } catch (MalformedURLException e) {
         e.printStackTrace();
         System.exit(-1);
      } catch (IOException e) {
         e.printStackTrace();
         System.exit(-1);
      }

      JFrame frame = new JFrame("ImageColumnTest");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(imgColumnTest.getMainComponent());
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}
person Hovercraft Full Of Eels    schedule 10.02.2013
comment
несвязанная придирка: запрос значения для его класса работает только в том случае, если содержимое != null и тип всех ячеек точно такой же. Это - возможно, но в этом и заключается придирка :-) - сильно на границе недопустимой реализации, потому что возвращаемый класс должен быть одинаковым для пустой и заполненной модели. - person kleopatra; 10.02.2013
comment
+1, ключевым моментом является добавление в модель ImageIcon, а не BufferedImage, тогда можно использовать средство визуализации Icon по умолчанию. Конечно, вам все равно нужно переопределить метод getColumnClass(), чтобы сообщить модели, что столбец содержит значки. - person camickr; 10.02.2013

TableCellRenderer для BufferedImage по умолчанию нет, вам придется его указать

Создайте себе новый класс, который расширяется от DefaultTableCellRenderer. Переопределить метод getTableCellRendererComponent

В этом методе проверьте, что переданное значение является BufferedImage, если это так, создайте экземпляр ImageIcon, передав ему BufferedImage.

Используйте метод cell renderes setIcon, передав ему новый экземпляр ImageIcon

В вашем экземпляре таблицы используйте метод setDefaultRenderer, чтобы связать средство визуализации ячеек с классом BufferedImage.

table.setDefaultRenderer(BufferedImage.class, myInstanceOfBufferedImageCellRenderer)

Ознакомьтесь с разделом Использование пользовательских средств визуализации для получения дополнительной информации.

Добавлен пример

Итак, я собрал быстрый пример, используя обе идеи от себя и от Hovercraft.

введите здесь описание изображения

Мое личное мнение заключается в том, что идея Hovercraft будет использовать меньше ресурсов и работать быстрее, чем использование модуля рендеринга ячеек, если вы создаете ImageIcon один раз для каждого BufferedImage и поддерживаете эту ссылку.

Вы можете заставить пользовательский рендерер ячеек делать то же самое, но вам нужно будет побаловаться с WeakHashMaps, чтобы сохранить ссылку между BufferedImage и Icon, и все еще существует риск того, что рассматриваемый BufferedImage никогда не будет собран, оставив ссылку Icon висящей. вокруг.

Если бы вы не делали ничего особенного с BufferedImage в плане рендеринга, я бы воспользовался предложением Hovercraft исключительно с точки зрения простоты использования и управления ресурсами.

public class BufferedImageTableCellRenderer {

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

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

                File[] files = new File("some folder some where").listFiles(new FileFilter() {
                    @Override
                    public boolean accept(File pathname) {
                        String name = pathname.getName().toLowerCase();
                        return name.endsWith(".gif") || name.endsWith(".jpg") || name.endsWith(".png");
                    }
                });

                ImageTableModel model = new ImageTableModel();
                for (File file : files) {
                    try {
                        model.add(ImageIO.read(file));
                    } catch (IOException ex) {
                        ex.printStackTrace();
                    }
                }

                JTable table = new JTable(model);
                table.setRowHeight(100);
                table.setDefaultRenderer(BufferedImage.class, new BufferedImageCellRenderer());

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

    public class BufferedImageCellRenderer extends DefaultTableCellRenderer {
        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
            super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
            if (value instanceof BufferedImage) {
                setIcon(new ImageIcon((BufferedImage)value));
                setText(null);
            } else {
                setText("Bad image");
            }
            return this;
        }
    }

    public class ImageTableModel extends AbstractTableModel {

        private List<BufferedImage> images = new ArrayList<>(25);
        private List<Icon> icons = new ArrayList<>(25);

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

        public void add(BufferedImage image) {
            images.add(image);
            icons.add(new ImageIcon(image));
            fireTableRowsInserted(images.size() - 1, images.size() - 1);
        }

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

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            Object value = null;
            switch (columnIndex) {
                case 0:
                    value = images.get(rowIndex);
                    break;
                case 1:
                    value = icons.get(rowIndex);
                    break;
            }
            return value;
        }

        @Override
        public Class<?> getColumnClass(int columnIndex) {
            Class clazz = String.class;
            switch (columnIndex) {
                case 0:
                    clazz = BufferedImage.class;
                    break;
                case 1:
                    clazz = Icon.class;
                    break;
            }
            return clazz;
        }

        @Override
        public String getColumnName(int column) {
            String name = null;
            switch (column) {
                case 0:
                    name = "BufferedImage";
                    break;
                case 1:
                    name = "Icon";
                    break;
            }
            return name;
        }
    }
}
person MadProgrammer    schedule 10.02.2013