Как добавить изображение в JPanel?

341

У меня есть JPanel, к которому я хотел бы добавить изображения JPEG и PNG, которые я генерирую на лету.

Все примеры, которые я видел в учебниках по Swing , особенно в примерах Swing, используют ImageIcons.

Я генерирую эти изображения как байтовые массивы, и они обычно больше, чем общий значок, который они используют в примерах, в 640x480.

  1. Есть ли какая-либо (производительность или другая) проблема в использовании класса ImageIcon для отображения изображения такого размера в JPanel?
  2. Какой обычный способ это сделать?
  3. Как добавить изображение в JPanel без использования класса ImageIcon?

Изменить : более тщательное изучение учебников и API показывает, что вы не можете добавить ImageIcon непосредственно в JPanel. Вместо этого они достигают того же эффекта, устанавливая изображение в виде значка JLabel. Это просто не правильно ...

Леонель
источник
1
В зависимости от того, как вы генерируете байтовые массивы, может быть более эффективно использовать, MemoryImageSourceчем конвертировать их в формат JPEG или PNG, а затем читать, ImageIOкак предлагает большинство ответов. Вы можете получить Imageиз MemoryImageSourceсозданного с вашим данными изображения, используя createImage, и отобразить, как предложено в одном из ответов.
Олден
Проверьте мой ответ stackoverflow.com/questions/43861991/…
Tommybee

Ответы:

252

Вот как я это делаю (немного больше информации о том, как загрузить изображение):

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JPanel;

public class ImagePanel extends JPanel{

    private BufferedImage image;

    public ImagePanel() {
       try {                
          image = ImageIO.read(new File("image name and path"));
       } catch (IOException ex) {
            // handle exception...
       }
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(image, 0, 0, this); // see javadoc for more info on the parameters            
    }

}
Брендан Кэшман
источник
17
-1 для недопустимой реализации paintComponent (@Dogmatixed, скорее всего, именно поэтому у вас возникают проблемы с перерисовкой) - он должен гарантировать, что покрывает всю область, если сообщает о непрозрачности (что по умолчанию), проще всего добиться, вызвав super.paintComponent
Клеопатра
5
@kleopatra, спасибо, я не осознавал этого ... в соответствии с javadoc: "Кроме того, если вы не вызываете реализацию super, вы должны учитывать непрозрачное свойство, то есть, если этот компонент непрозрачен, вы должны полностью заполнить фон непрозрачного цвета. Если вы не соблюдаете непрозрачное свойство, вы, скорее всего, увидите визуальные артефакты. " Я обновлю ответ сейчас.
Брендан Кашман
8
Всегда соблюдать Principle of Encapsulationметоды в то время как подмену Высшего класса, доступ Тендерный из paintComponent(...)метода является protectedи не public:-)
NICE КОРОВА
1
Это не хорошо, это ничего не делает с изменением размера панели для изображения. Больше кода нужно будет добавить позже, разберитесь с этим. Намного проще использовать ответ JLabel.
Кейли
2
@Jonathan: мне очень жаль, что касается источника Principle of Encapsulation. Просто помните, что при программировании то, что должно быть скрыто от остальной части кода, должно сохраняться таким образом, вместо того, чтобы быть видимым для всего в коде. Похоже, что это одна такая вещь, которая приходит с опытом, как каждый учится каждый день. Любая книга может дать базовое представление о том, что такое инкапсуляция, но именно то, как мы реализуем наш код, определяет, насколько мы придерживаемся этой концепции.
Хорошая встреча
520

Если вы используете JPanels, то, вероятно, работаете с Swing. Попробуй это:

BufferedImage myPicture = ImageIO.read(new File("path-to-file"));
JLabel picLabel = new JLabel(new ImageIcon(myPicture));
add(picLabel);

Изображение теперь является компонентом свинга. Это становится предметом условий макета, как и любой другой компонент.

Фред Хаслам
источник
16
Как масштабировать изображение в соответствии с размером JLabel?
coding_idiot
1
Хороший код! У меня мало опыта работы с Swing, но я не могу заставить его работать. Кто-нибудь пробовал это в jdk 1.6.0_16?
ATorras
3
@ATorras Я знаю, что вы спрашивали об этом некоторое время назад, но если у других новичков были мои проблемы, не забудьте picLabel.setBounds ();
Кайл
Нужно ли создавать новый объект JLabel каждый раз при перезагрузке фотографии?
0x6B6F77616C74
2
@coding_idiot new JLabel (новый ImageIcon (backgroundImage.getScaledInstance (ширина, высота, Image.SCALE_FAST)));
Давиде
37

Путь Фреда Хаслама работает отлично. У меня возникли проблемы с путем к файлу, так как я хочу сослаться на изображение в моем банке. Для этого я использовал:

BufferedImage wPic = ImageIO.read(this.getClass().getResource("snow.png"));
JLabel wIcon = new JLabel(new ImageIcon(wPic));

Поскольку у меня есть только конечное число (около 10) изображений, которые мне нужно загрузить с помощью этого метода, он работает довольно хорошо. Он получает файл без необходимости иметь правильный относительный путь к файлу.

shawalli
источник
1
Я заменил первую строку, BufferedImage previewImage = ImageIO.read(new URL(imageURL));чтобы получить мои изображения с сервера.
intcreator
Для ссылки на изображения из jar, попробуйте этот stackoverflow.com/a/44844922/3211801
Нанда
33

Я думаю, что нет необходимости в подклассе чего-либо. Просто используйте Jlabel. Вы можете установить изображение в Jlabel. Итак, измените размер Jlabel, затем заполните его изображением. Все нормально. Это способ, которым я делаю.

CoderTim
источник
20

Вы можете избежать полного развертывания собственного подкласса Component, используя класс JXImagePanel из бесплатных библиотек SwingX .

Скачать

Дэн Винтон
источник
11
JLabel imgLabel = new JLabel(new ImageIcon("path_to_image.png"));

источник
8

Вы можете создать подкласс JPanel - вот выдержка из моей ImagePanel, которая помещает изображение в любое из 5 мест: верхнее / левое, верхнее / правое, среднее / среднее, нижнее / левое или нижнее / правое:

protected void paintComponent(Graphics gc) {
    super.paintComponent(gc);

    Dimension                           cs=getSize();                           // component size

    gc=gc.create();
    gc.clipRect(insets.left,insets.top,(cs.width-insets.left-insets.right),(cs.height-insets.top-insets.bottom));
    if(mmImage!=null) { gc.drawImage(mmImage,(((cs.width-mmSize.width)/2)       +mmHrzShift),(((cs.height-mmSize.height)/2)        +mmVrtShift),null); }
    if(tlImage!=null) { gc.drawImage(tlImage,(insets.left                       +tlHrzShift),(insets.top                           +tlVrtShift),null); }
    if(trImage!=null) { gc.drawImage(trImage,(cs.width-insets.right-trSize.width+trHrzShift),(insets.top                           +trVrtShift),null); }
    if(blImage!=null) { gc.drawImage(blImage,(insets.left                       +blHrzShift),(cs.height-insets.bottom-blSize.height+blVrtShift),null); }
    if(brImage!=null) { gc.drawImage(brImage,(cs.width-insets.right-brSize.width+brHrzShift),(cs.height-insets.bottom-brSize.height+brVrtShift),null); }
    }
Лоуренс Дол
источник
8
  1. Не должно быть никаких проблем (кроме общих проблем, которые могут возникнуть у вас с очень большими изображениями).
  2. Если вы говорите о добавлении нескольких изображений на одну панель, я бы использовал ImageIcons. Для одного изображения я бы подумал о создании собственного подкласса JPanelи переопределении его paintComponentметода для рисования изображения.
  3. (см. 2)
Майкл Майерс
источник
6

JPanelпочти всегда неправильный класс для подкласса. Почему бы вам не подкласс JComponent?

Существует небольшая проблема ImageIconв том, что конструктор блокирует чтение изображения. Не проблема при загрузке из jar приложения, но, возможно, если вы читаете по сетевому соединению. Там множество примеров AWT эпохи использования MediaTracker, ImageObserverи друзей, даже в демо - версии JDK.

Том Хотин - Tackline
источник
6

Я делаю нечто очень похожее в частном проекте, над которым я работаю. До сих пор я генерировал изображения размером до 1024x1024 без каких-либо проблем (кроме памяти) и могу отображать их очень быстро и без проблем с производительностью.

Переопределение метода рисования подкласса JPanel является излишним и требует больше работы, чем нужно.

Я делаю это так:

Class MapIcon implements Icon {...}

ИЛИ

Class MapIcon extends ImageIcon {...}

Код, который вы используете для создания изображения, будет в этом классе. Я использую BufferedImage, чтобы рисовать тогда, когда вызывается paintIcon (), используйте g.drawImvge (bufferedImage); Это уменьшает количество перепрошивок, которые вы выполняете во время создания изображений, и вы можете их обработать.

Далее я расширяю JLabel:

Class MapLabel extends Scrollable, MouseMotionListener {...}

Это потому, что я хочу поместить свое изображение в область прокрутки, то есть отобразить часть изображения и заставить пользователя прокручивать его по мере необходимости.

Затем я использую JScrollPane для хранения MapLabel, который содержит только MapIcon.

MapIcon map = new MapIcon (); 
MapLabel mapLabel = new MapLabel (map);
JScrollPane scrollPane = new JScrollPane();

scrollPane.getViewport ().add (mapLabel);

Но для вашего сценария (просто показывайте все изображение каждый раз). Вам необходимо добавить MapLabel в верхнюю JPanel и убедиться, что все их размеры соответствуют полному размеру изображения (переопределяя GetPreferredSize ()).

Томас Джонс-Лоу
источник
5

Создайте исходную папку в каталоге вашего проекта, в данном случае я назвал ее Images.

JFrame snakeFrame = new JFrame();
snakeFrame.setBounds(100, 200, 800, 800);
snakeFrame.setVisible(true);
snakeFrame.add(new JLabel(new ImageIcon("Images/Snake.png")));
snakeFrame.pack();

источник
5

Вы можете избежать использования собственной Componentбиблиотеки s и SwingX и ImageIOкласса:

File f = new File("hello.jpg");
JLabel imgLabel = new JLabel(new ImageIcon(file.getName()));
Muskovets
источник
4

Этот ответ является дополнением к ответу @ shawalli ...

Я тоже хотел сослаться на изображение внутри моей банки, но вместо BufferedImage я просто сделал это:

 JPanel jPanel = new JPanel();      
 jPanel.add(new JLabel(new ImageIcon(getClass().getClassLoader().getResource("resource/images/polygon.jpg"))));
Филипе Брито
источник
1

Я вижу много ответов, на самом деле не обращаясь к трем вопросам ОП.

1) Слово о производительности: байтовые массивы, вероятно, неэффективны, если вы не можете использовать точный порядок байтов в пикселях, который соответствует вашим адаптерам дисплея, текущему разрешению и глубине цвета.

Чтобы добиться максимальной производительности при рисовании, просто преобразуйте изображение в BufferedImage, созданный с типом, соответствующим вашей текущей графической конфигурации. См. CreateCompatibleImage на https://docs.oracle.com/javase/tutorial/2d/images/drawonimage.html

Эти изображения будут автоматически кэшироваться в памяти видеокарты после рисования несколько раз без каких-либо усилий по программированию (это стандартно в Swing начиная с Java 6), и, следовательно, фактическое рисование займет незначительное количество времени - если вы не изменили изображение ,

Изменение изображения будет сопровождаться дополнительной передачей памяти между основной памятью и памятью GPU, что является медленным. Избегайте «перерисовки» изображения в BufferedImage, поэтому избегайте использования getPixel и setPixel во всех отношениях.

Например, если вы разрабатываете игру, вместо того, чтобы рисовать всех игровых акторов в BufferedImage, а затем в JPanel, гораздо быстрее загружать всех акторов как меньшие BufferedImages и рисовать их по одному в вашем коде JPanel в их правильное положение - таким образом, нет никакой дополнительной передачи данных между основной памятью и памятью GPU, кроме начальной передачи изображений для кэширования.

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

2) Обычный способ сделать это - нарисовать BufferedImage в переопределенном методе paintComponent в JPanel. Хотя Java поддерживает большое количество дополнительных полезных функций, таких как цепочки буферов, управляющие VolatileImages, кешируемыми в памяти графического процессора, нет необходимости использовать ни один из них, так как Java 6 работает достаточно хорошо, не раскрывая всех этих деталей ускорения графического процессора.

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

3) Не добавляйте. Просто нарисуйте, как указано выше:

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    g.drawImage(image, 0, 0, this); 
}

«Добавление» имеет смысл, если изображение является частью макета. Если вам нужно это как фоновое или переднее изображение, заполняющее JPanel, просто нарисуйте paintComponent. Если вы предпочитаете создавать универсальный компонент Swing, который может отображать ваше изображение, то это та же история (вы можете использовать JComponent и переопределить его метод paintComponent), а затем добавить его в свой макет компонентов GUI.

4) Как преобразовать массив в Bufferedimage

Преобразование ваших байтовых массивов в PNG, а затем их загрузка довольно ресурсоемки. Лучший способ - преобразовать существующий байтовый массив в BufferedImage.

Для этого: не используйте петли и не копируйте пиксели. Это очень, очень медленно. Вместо:

  • узнать предпочитаемую структуру байтов BufferedImage (в настоящее время можно предположить RGB или RGBA, что составляет 4 байта на пиксель)
  • изучите отсканированную линию и размер сканирования при использовании (например, у вас может быть изображение шириной 142 пикселя - но в реальной жизни оно будет храниться в виде байтового массива шириной 256 пикселей, поскольку это быстрее обрабатывать и маскировать неиспользуемые пиксели с помощью оборудования графического процессора )
  • затем, если у вас есть сборка массива в соответствии с этими принципами, метод массива setRGB объекта BufferedImage может скопировать ваш массив в BufferedImage.
Джи Би
источник