Создание нескольких кнопок JB (сотни) создает огромную задержку при их создании. - PullRequest
2 голосов
/ 18 апреля 2019

Когда я создаю JButton, я получаю очень большую задержку. Вот пример кода того, как я создаю свои кнопки:

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

public class Scratch {

public static void main(String[] args) {
    Runnable r = () -> {
        JOptionPane.showMessageDialog(
                null, new Scratch().getUI(new TileSet("Content/Graphics/tileSets/12x12x3 - tileSet.png", 12, 12, 3)));
        JOptionPane.showMessageDialog(
                null, new Scratch().getUI(new TileSet("Content/Graphics/tileSets/16x16x0 - tileSetItems.png", 12, 12, 3)));
        JOptionPane.showMessageDialog(
                null, new Scratch().getUI(new TileSet("Content/Graphics/tileSets/29x18x1 - roguelikeDungeon_transparent.png", 12, 12, 3)));
    };
    SwingUtilities.invokeLater(r);
}

public final JComponent getUI(TileSet tileSet) {
    JPanel ui = new JPanel();
    JPanel tilePanel = new JPanel();
    tilePanel.setLayout(new GridLayout(12, 12, 5, 5));

    long t1 = System.currentTimeMillis();

    TileButton tileMenuButtons[] = new TileButton[tileSet.tileSet.length];
    long tot = 0;
    for (int i = 0; i < tileMenuButtons.length; i++) {
        long t2 = System.currentTimeMillis();
        tileMenuButtons[i] = new TileButton(i,tileSet);
        long t3 = System.currentTimeMillis();
        tot += (t3-t2);
        System.out.println(String.format("It took : "+ tot +"ms for loading "+i+ ". Button "));
        tilePanel.add(tileMenuButtons[i]);
    }

    long t4 = System.currentTimeMillis();

    JScrollPane scrollPane = new JScrollPane();
    scrollPane.getVerticalScrollBar().setUnitIncrement(16);
    scrollPane.setOpaque(true);
    scrollPane.setViewportView(tilePanel);

    ui.add(scrollPane);
    System.out.println(String.format("It took in total : "+ (t4-t1) +"ms for loading "+tileMenuButtons.length+ " TileButtons"));
    return ui;
}

Распечатка дала мне следующий результат:

  It took in total : 9661ms for loading the TileSet (144 Buttons)
  It took in total : 13806ms for loading the TileSet (256 Buttons)
  It took in total : 27745ms for loading the TileSet (522 Buttons)

После измерения времени создания каждой кнопки вся задержка вызывается кнопками:

  It took 30915ms for loading the 521st Button 
  It took in total : 30979ms for loading the TileSet

Я мог бы отфильтровать, что проблема вызвана моим классом кнопок, но я не понимаю, где и почему?

  class TileButton extends JButton {
    private int id;
    private TileSet ts = new TileSet("Content/Graphics/tileSets/12x12x3 - tileSet.png", 12, 12, 3);
    private int size = 50;
    public TileButton(int id, TileSet tileSet) {
        super();
        this.ts = tileSet;
        this.id = id;
        loadImage(id);
    }


    public void loadImage(int imageno) {
        this.setBorder(null);
        try {
            Image img = ts.tileSet[imageno].tileImage;
            img = img.getScaledInstance(size, size, Image.SCALE_SMOOTH);
            ImageIcon icon = new ImageIcon(img);
            this.setIcon(icon);
        } catch (Exception e) {
            System.out.println("Fehler beim Laden von Bild");
        }
    }
}

static class TileSet{
    private String tileSetImagePath;
    private int numberOfTilesX, numberOfTilesY;
    private BufferedImage tileSetImage;
    public Tile[] tileSet;
    private int width = Tile.TILEWIDTH, height = Tile.TILEHEIGHT;
    private int border;

    public TileSet(String pTileSetImagePath, int pNumberOfTilesX, int pNumberOfTilesY, int pBorder){
        tileSetImagePath = pTileSetImagePath;
        numberOfTilesX = pNumberOfTilesX;
        numberOfTilesY = pNumberOfTilesY;
        border = pBorder;
        tileSet = new Tile[numberOfTilesX * numberOfTilesY];
        createTileSetImages();
    }

    public void createTileSetImages(){
        try {
            tileSetImage = ImageIO.read(new File(tileSetImagePath));
            width = tileSetImage.getWidth() / numberOfTilesX - border;
            height = tileSetImage.getHeight() / numberOfTilesY - border;
        } catch (IOException e) {
            e.printStackTrace();
        }
        int i = 0;
        for(int y = 0; y < numberOfTilesY; y++) {
            for(int x = 0; x < numberOfTilesX; x++) {
                BufferedImage bi = tileSetImage.getSubimage(x * (width + border), y * (height + border), width, height);
                bi.getScaledInstance(Tile.TILEWIDTH, Tile.TILEHEIGHT, Image.SCALE_SMOOTH);
                tileSet[i++] = new Tile(bi);
            }
        }
    }
}
}
   class Tile extends JPanel{
   public Image tileImage;

   public Tile(Image pTileImage)  {
   super();
   setOpaque(true);
   tileImage = pTileImage;
    }
   }

Как предположил Эндрю, возможно, ScaledInstance вызывает задержку. Есть ли другой способ масштабирования изображения без такой большой задержки? РЕДАКТИРОВАТЬ: масштабирование не вызывает задержки: создание одной кнопки с масштабированием занимает 1 мс. (Извините за длинный код, но он необходим, потому что, если я просто использую (упрощенные) значки и кнопки, это не относится к моей проблеме и, следовательно, не поможет) После попытки создания кнопок без ScaledInstance задержка все еще существует.

Ответы [ 2 ]

5 голосов
/ 18 апреля 2019

Вот MCVE / SSCCE для добавления от 100 кнопок к 6400 кнопкам в GUI, каждая со своим собственным значком.

Типичный вывод здесь:

It took 14 milliseconds for 100 buttons.
It took 110 milliseconds for 1600 buttons.
It took 138 milliseconds for 6400 buttons.

Как это может выглядеть в кадре.

image

import java.awt.*;
import java.awt.image.*;
import java.io.IOException;
import java.net.*;
import javax.swing.*;
import javax.imageio.*;

public class LotsOfButtons {

    public final JComponent getUI(int pts) {
        JComponent ui = new JPanel(new GridLayout(0, pts));
        try {
            BufferedImage image = ImageIO.read(new URL(
                    "https://i.stack.imgur.com/OVOg3.jpg"));

            int wT = image.getWidth() / pts;
            int hT = image.getHeight() / pts;
            Insets insets = new Insets(0, 0, 0, 0);

            long t1 = System.currentTimeMillis();
            for (int jj = 0; jj < pts; jj++) {
                for (int ii = 0; ii < pts; ii++) {
                    int x = ii * wT;
                    int y = jj * hT;
                    JButton b = new JButton(new ImageIcon(
                            image.getSubimage(x, y, wT, hT)));
                    b.setMargin(insets);
                    ui.add(b);
                }
            }
            long t2 = System.currentTimeMillis();

            System.out.println(String.format(
                    "It took %1s milliseconds for %1s buttons.",
                    (t2 - t1), pts*pts));
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return ui;
    }

    public static void main(String[] args) {
        Runnable r = () -> {
            JOptionPane.showMessageDialog(
                    null, new LotsOfButtons().getUI(10));
            JOptionPane.showMessageDialog(
                    null, new LotsOfButtons().getUI(40));
            JOptionPane.showMessageDialog(
                    null, new LotsOfButtons().getUI(80));
        };
        SwingUtilities.invokeLater(r);
    }
}

Так что, учитывая, что 14 milliseconds не где-то рядом "(> 30 сек.)" Я предполагаю, что вы делаете это ... по-другому. Если этот источник (выше) не поможет вам решить проблему, я предлагаю подготовить и опубликовать MCVE / SSCCE, чтобы горячие ссылки на изображение, как и приведенный выше исходный код, были бы наилучшим шагом в решении проблемы.

2 голосов
/ 19 апреля 2019

Ваша проблема, вероятно, в классе TileButton:

class TileButton extends JButton {
    private int id;
    private TileSet ts = new TileSet("Content/Graphics/tileSets/12x12x3 - tileSet.png", 12, 12, 3);
    private int size = 50;
    public TileButton(int id, TileSet tileSet) {
        super();
        this.ts = tileSet;
        this.id = id;
        loadImage(id);
    }

Для каждого TileButton вы создаете новый TileSet. Этот набор плиток читает из файла - это может вызвать значительные задержки. Затем вы игнорируете этот набор плиток и используете tileSet, переданный в конструктор.

Так что вместо этого вы не должны каждый раз создавать новый TileSet:

class TileButton extends JButton {
    private int id;
    private final TileSet ts;
    private int size = 50;
    public TileButton(int id, TileSet tileSet) {
        super();
        this.ts = tileSet;
        this.id = id;
        loadImage(id);
    }
...