Як додати зображення до 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 (найімовірніше, @ саме тому у вас виникають проблеми з перемальовуванням) - він повинен гарантувати покриття всієї площі, якщо вона повідомляє про непрозорість (що за замовчуванням), що найлегше досягти, викликавши super.paintComponent
kleopatra

5
@kleopatra, Дякую, я не усвідомлював, що ... за словами javadoc: "Далі, якщо ви не викликаєте реалізацію супер, ви повинні шанувати властивість непрозорого, тобто якщо цей компонент непрозорий, ви повинні повністю заповнити фон в непрозорому кольорі. Якщо ви не шануєте непрозорі властивості, ви, ймовірно, побачите візуальні артефакти ". Я зараз оновлю відповідь.
Брендан Кешман

8
Будь ласка, завжди поважайте Principle of Encapsulationпри цьому переважаючі методи paintComponent(...)protectedpublic
Суперкласу, специфікатором

1
Це не є корисним, це нічого не стосується розміщення панелі до зображення. Більше коду доведеться додати пізніше, щоб вирішити це. Набагато простіше використовувати відповідь JLabel.
Keilly

2
@Jonathan: Мені дуже шкода, що стосується деяких джерел Principle of Encapsulation. Просто пам’ятайте під час програмування те, що повинно бути приховано від решти коду, потрібно підтримувати таким чином, а не бути видимим усім у коді. Здається, це одна з таких справ, яка приходить з досвідом, як кожен вчиться щодня. Будь-яка книга може дати основне уявлення про те, що таке інкапсуляція, але саме те, як ми реалізуємо наш код, визначає, наскільки ми дотримуємось цієї концепції.
NIcE КЛЮЧ

520

Якщо ви використовуєте JPanel, то, ймовірно, працюєте з 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?
Аторрас

3
@ATorras Я знаю, що ти це поцікавився ще раз, але якщо у будь-яких інших новачків виникли мої проблеми, пам’ятай про picLabel.setBounds ();
Кайл

Чи потрібно мені створювати новий об'єкт JLabel кожного разу, коли фотографія завантажується?
0x6B6F77616C74

2
@coding_idiot новий JLabel (новий ImageIcon (backgroundImage.getScaledInstance (ширина, висота, зображення .SCALE_FAST)));
Девід

37

Шлях Фреда Хаслама прекрасно працює. Однак у мене виникли проблеми з файловим шляхом, оскільки я хочу посилатись на зображення в банці. Для цього я використав:

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

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


1
Я замінив перший рядок, BufferedImage previewImage = ImageIO.read(new URL(imageURL));щоб отримати свої зображення з сервера.
intcreator

Про людське око зображень з глечика, спробуйте цей stackoverflow.com/a/44844922/3211801
Нанд

33

Я думаю, що не потрібно нічого підкласувати. Просто використовуйте Jlabel. Ви можете встановити зображення на Jlabel. Отже, змініть розмір Jlabel, а потім заповніть його зображенням. Нічого страшного. Це я і роблю.



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в тому, що конструктор блокує зчитування зображення. Насправді не проблема під час завантаження з банку додатків, але, можливо, якщо ви потенційно читаєте через мережеве з'єднання. Там безліч прикладів AWT епохи використання MediaTracker, ImageObserverі друзів, навіть в демо - версії JDK.


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

Створіть папку з джерелом у своєму каталозі проектів, в такому випадку я назвав її зображеннями.

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()));

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), і тому власне малювання займе незначну кількість часу - якщо ви не змінили зображення .

Змінення зображення відбудеться з додатковою передачею пам’яті між основною пам'яттю та пам’яттю графічного процесора - що відбувається повільно. Уникайте "перемальовування" зображення в BuferiImage, тому уникайте використання getPixel і setPixel будь-якими способами.

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

ImageIcon використовуватиме BufferedImage під кришкою, але в основному ключовим є виділення BufferedImage у відповідному графічному режимі, і для цього не докладається жодних зусиль.

2) Звичайним способом цього є нанесення BuferiImage методом JPAnel, що перекривається фарбою. Хоча Java підтримує велику кількість додаткових вигод, таких як буферні ланцюги, які управляють VolatileImages, кешованими в пам'яті GPU, немає необхідності використовувати жодне з них, оскільки Java 6, яка виконує досить гарну роботу, не піддаючи всі ці подробиці прискорення GPU.

Зауважте, що прискорення GPU може не працювати для певних операцій, наприклад, розтягування напівпрозорих зображень.

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

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

"Додавання" має сенс, якщо зображення є частиною макета. Якщо вам це потрібно як фонове або фонове зображення, що заповнює JPanel, просто намалюйте paintComponent. Якщо ви віддаєте перевагу заготовленню загального компонента Swing, який може показувати ваше зображення, то це та сама історія (ви можете використовувати JComponent і замінити її метод paintComponent) - а потім додайте це до свого макета компонентів графічного інтерфейсу.

4) Як перетворити масив у буферне зображення

Перетворення байтових масивів у PNG та завантаження його досить ресурсомістке. Кращий спосіб - перетворити наявний масив байтів у BufferedImage.

Для цього: не використовувати для циклів і копіювати пікселі. Це дуже повільно. Замість цього:

  • вивчити кращу байтну структуру BufferedImage (сьогодні безпечно вважати RGB або RGBA, що становить 4 байти на піксель)
  • вивчіть сканування та скануйте розмір у використанні (наприклад, у вас може бути зображення шириною 142 пікселів - але в реальному житті воно буде зберігатися як байтовий масив 256 пікселів, оскільки швидше обробити та замаскувати невикористані пікселі апаратним забезпеченням GPU )
  • то, як тільки у вас буде побудова масиву згідно з цими принципами, метод масиву setRGB масиву BufferedImage може скопіювати свій масив у BufferedImage.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.