Вопрос

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

Все примеры, которые я видел до сих пор в Учебники по свингу, особенно в Примеры свинга использовать ImageIconс.

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

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

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

Это было полезно?

Решение

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

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            
    }

}

Другие советы

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

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

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

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

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

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

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

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

Загрузить

JLabel imgLabel = new JLabel(new ImageIcon("path_to_image.png"));
<Ол>
  • Не должно быть никаких проблем (кроме общих проблем, которые могут возникнуть у вас с очень большими изображениями).
  • Если вы говорите о добавлении нескольких изображений на одну панель, я бы использовал ImageIcon . Для одного изображения я бы подумал о создании пользовательского подкласса JPanel и переопределении его метода paintComponent для рисования изображения.
  • (см. 2)
  • Вы можете создать подкласс 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); }
        }
    

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

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

    Я делаю что-то очень похожее в частном проекте, над которым работаю.До сих пор я без проблем генерировал изображения размером до 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()).

    Создайте исходную папку в каталоге вашего проекта, в данном случае я назвал ее 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();
    

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

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

     JPanel jPanel = new JPanel();      
     jPanel.add(new JLabel(new ImageIcon(getClass().getClassLoader().getResource("resource/images/polygon.jpg"))));
    

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

    File f = new File("hello.jpg");
    JLabel imgLabel = new JLabel(new ImageIcon(file.getName()));
    

    Я вижу много ответов, но на самом деле не затрагиваю три вопроса ФП.

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

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

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

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

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

    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), а затем добавить этот к вашему макету компонентов графического интерфейса.

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

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

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

    • узнать предпочтительную структуру байтов BufferedImage (в настоящее время можно с уверенностью предположить RGB или RGBA, что составляет 4 байта на пиксель)
    • узнать используемую строку сканирования и размер сканирования (например,у вас может быть изображение шириной 142 пикселя, но в реальной жизни оно будет храниться в виде байтового массива шириной 256 пикселей, поскольку его быстрее обрабатывать и маскировать неиспользуемые пиксели аппаратным обеспечением графического процессора)
    • затем, как только у вас будет построен массив в соответствии с этими принципами, метод массива setRGB BufferedImage может скопировать ваш массив в BufferedImage.
    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top