如何创建用于输出图像集的高效JavaGUI?

如何创建用于输出图像集的高效JavaGUI?,java,image,swing,user-interface,Java,Image,Swing,User Interface,我希望在JPanel的底部动态输出一行卡片,这意味着如果我添加一张卡片,该行将自动调整并居中。此外,当用户将鼠标悬停在卡上时,我需要将卡弹出一点。我使用纯图形对象来实现这一点,但是,它非常、非常低效,并且滞后很多。尽管我不知道如何使用重叠的JPanel,但使用重叠的JPanel是否更有效 这是我现在的代码: import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; import java.awt.event.Mo

我希望在JPanel的底部动态输出一行卡片,这意味着如果我添加一张卡片,该行将自动调整并居中。此外,当用户将鼠标悬停在卡上时,我需要将卡弹出一点。我使用纯图形对象来实现这一点,但是,它非常、非常低效,并且滞后很多。尽管我不知道如何使用重叠的JPanel,但使用重叠的JPanel是否更有效

这是我现在的代码:

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Stack;

public class Launcher extends JFrame implements MouseMotionListener {
    private static final int MAX_CARD_SPAN = 900;
    private static final int CARD_WIDTH = 104;
    private static final int CARD_HEIGHT = 146;
    private static final int drawCardY = 500;
    private static final int drawSelectedCardY = 450;
    private static Deck deck;
    private static Stack<Card> userDeck;
    private static int currentX;
    private static int initialX;
    private static int offset;
    private static int overlapIndex;

    public static void main(String[] args) throws IOException {
        deck = new Deck();
        userDeck = new Stack<>();
        currentX = 80;
        initialX = 0;
        overlapIndex = -2;
        new Launcher();
    }

    public Launcher() throws IOException {
        this.setTitle("UNO");
        this.setSize(1000, 650);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setResizable(false);
        this.setVisible(true);

        addMouseMotionListener(this);

        for (int i = 0; i < 10; i++)
            userDeck.add(deck.getDeck().pop());

        repaint();
    }

    public static int getValue(String name) {
        return -1;
    }

    public void paint(Graphics g) {
        if (overlapIndex != -1)
            try {
                setBackground(Color.WHITE);
                offset = 80;

                if (userDeck.size() > 10)
                    offset = MAX_CARD_SPAN / userDeck.size();

                initialX = (MAX_CARD_SPAN - offset * userDeck.size()) / 2 + offset / 2;
                currentX = initialX;

                for (int i = 0; i < userDeck.size(); i++) {
                    BufferedImage bufferedImage = ImageIO.read(getClass().getResourceAsStream("\\images\\" + userDeck.get(i).getName() + ".jpg"));
                    g.drawImage(bufferedImage, currentX, drawCardY, null);
                    currentX += offset;
                }

                if (overlapIndex > -1)
                    g.drawImage(ImageIO.read(getClass().getResourceAsStream("\\images\\" + userDeck.get(overlapIndex - 1).getName() + ".jpg")), offset * (overlapIndex - 1) + initialX, drawSelectedCardY, null);

                overlapIndex = -1;
            } catch (Exception e) {
            }
    }

    public void paintCardOverlap(Graphics g) throws
    }

    public void cardOverlap(int x, int y) {
        overlapIndex = x / offset;
        repaint();
    }

    public void mouseMoved(MouseEvent e) {
        if (inRange(e.getX(), e.getY()))
            cardOverlap(e.getX(), e.getY());
        else
            overlapIndex = -1;
    }

    public static boolean inRange(int x, int y) {
        int sideSpace = (MAX_CARD_SPAN - offset * userDeck.size()) / 2 + offset / 2;

        return ((x >= sideSpace) && (x <= MAX_CARD_SPAN - sideSpace + CARD_WIDTH) && (y >= 600 - CARD_HEIGHT));
    }

    public void mouseDragged(MouseEvent e) {
    }
}
导入javax.imageio.imageio;
导入javax.swing.*;
导入java.awt.*;
导入java.awt.event.MouseEvent;
导入java.awt.event.MouseMotionListener;
导入java.awt.image.buffereImage;
导入java.io.IOException;
导入java.util.Stack;
公共类启动器扩展JFrame实现MouseMotionListener{
专用静态最终int MAX_CARD_SPAN=900;
专用静态最终int卡_宽度=104;
专用静态最终int卡_高度=146;
专用静态最终int drawCardY=500;
私有静态最终int DRAWSELECTED CARDY=450;
私人静态甲板;
私有静态堆栈用户组;
私有静态int-currentX;
私有静态int initialX;
私有静态整数偏移;
私有静态索引;
公共静态void main(字符串[]args)引发IOException{
甲板=新甲板();
userDeck=newstack();
电流x=80;
初始值x=0;
重叠指数=-2;
新发射器();
}
公共启动器()引发IOException{
本文件的标题为“UNO”;
此.setSize(1000650);
此.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
此参数为.setresizeable(false);
此.setVisible(true);
addMouseMotionListener(此);
对于(int i=0;i<10;i++)
添加(deck.getDeck().pop());
重新油漆();
}
公共静态int getValue(字符串名称){
返回-1;
}
公共空间涂料(图g){
如果(重叠索引!=-1)
试一试{
挫折地面(颜色:白色);
偏移量=80;
如果(userDeck.size()>10)
offset=MAX_CARD_SPAN/userDeck.size();
initialX=(MAX\u CARD\u SPAN-offset*userDeck.size())/2+offset/2;
电流x=初始值x;
对于(int i=0;i-1)
g、 drawImage(ImageIO.read(getClass().getResourceAsStream(“\\images\\”+userDeck.get(overlapIndex-1.getName()+“.jpg”)),偏移量*(overlapIndex-1)+首字母X,drawSelectedCardY,null);
重叠指数=-1;
}捕获(例外e){
}
}
公共无效彩卡重叠(图形g)抛出
}
公共空白卡片重叠(整数x,整数y){
重叠指数=x/偏移量;
重新油漆();
}
public void mouseMoved(MouseEvent e){
if(在范围内(e.getX(),e.getY())
卡片重叠(e.getX(),e.getY());
其他的
重叠指数=-1;
}
范围内的公共静态布尔值(整数x,整数y){
int sideSpace=(MAX_CARD_SPAN-offset*userDeck.size())/2+offset/2;
返回((x>=侧空间)和&(x=600-卡片高度);
}
公共无效鼠标标记(鼠标事件e){
}
}

它滞后了很多,因为你正在从一种绘画方法中读取图像,这是你永远不想做的事情。这是一种浪费,因为这意味着每次调用绘制时都要重新读取图像,而且这也会降低GUI的响应速度,因为绘制总是缓慢进行。不要这样做。将图像读入ArrayList或其他字段,并仅使用绘画进行绘画

其他无关问题

  • 过度使用静态修饰符——这会增加代码的耦合,从而导致bug
  • 你用了错误的绘画方法。您不应该使用paint绘制,而应该使用JPanel的paintComponent方法
  • 您没有在自己的覆盖中调用super的绘制方法
  • 你忽略了那个空的catch块的异常,编码相当于闭着眼睛开车。至少打印异常的stacktrace,以便知道抛出异常的原因
  • 您正在从Swing事件线程启动Swing GUI,这可能导致难以调试间歇性Swing线程异常
正在玩,因为到卡图像的链接已断开。这一个使用了一个新的链接和工程,但纠结需要解决它。下面的示例并不直接在组件的绘制方法中绘制,而是创建图像,然后创建图像图标,并将它们放置在可拖动的JLabel中

它显示为:

导入java.awt.Color;
导入java.awt.Component;
导入java.awt.Dialog.ModalityType;
导入java.awt.event.MouseAdapter;
导入java.awt.event.MouseEvent;
导入java.awt.Dimension;
导入java.awt.Graphics2D;
导入java.awt.RenderingHints;
导入java.awt.image.buffereImage;
导入java.beans.PropertyChangeEvent;
导入java.beans.PropertyChangeListener;
导入java.io.IOException;
导入java.net.URL;
导入java.util.ArrayList;
导入java.util.Collections;
导入java.util.HashMap;
导入java.util.Iterator;
导入java.util.List;
导入java.util.Map;
导入java.util.concurrent.ExecutionException;
导入javax.imageio.imageio;
导入javax.swing.*;
@抑制警告(“串行”)
公共类CardFun扩展JLayeredPane{
专用静态最终整数预值=1200;
私人静电
import java.awt.Color;
import java.awt.Component;
import java.awt.Dialog.ModalityType;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;

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

@SuppressWarnings("serial")
public class CardFun extends JLayeredPane {
    private static final int PREF_W = 1200;
    private static final int PREF_H = 900;
    private static final Color BASE_COLOR = new Color(0, 80, 0);
    private static final int CARD_COUNT = 20;
    private static final int WIDTH_SHOWING = 20;
    private static final String PATH = "https://raw.githubusercontent.com/hayeah/playing-cards-assets/master/png/";
    private static final String BACK = "back.png";
    private Map<Card, Icon> cardIconMap = new HashMap<>();
    private Icon backIcon;
    private Deck deck = new Deck();
    private JPanel basePane = new JPanel(null);
    private JDialog optionDlg;

    public CardFun() throws IOException {
        LoadImageWorker worker = new LoadImageWorker();
        worker.addPropertyChangeListener(new LoadImageWorkerListener());
        worker.execute();
        basePane.setSize(getPreferredSize());
        basePane.setBackground(BASE_COLOR);
        add(basePane, JLayeredPane.DEFAULT_LAYER);

        final CardMouseAdapter mouseAdapter = new CardMouseAdapter(this, basePane);
        addMouseListener(mouseAdapter);
        addMouseMotionListener(mouseAdapter);

        JProgressBar progBar = new JProgressBar();
        progBar.setIndeterminate(true);
        JOptionPane optionPane = new JOptionPane(progBar, JOptionPane.PLAIN_MESSAGE);
        optionDlg = optionPane.createDialog(null, "Waiting for Images to Load");
        optionDlg.setModalityType(ModalityType.APPLICATION_MODAL);

        optionDlg.setVisible(true);
    }

    @Override
    public Dimension getPreferredSize() {
        if (isPreferredSizeSet()) {
            return super.getPreferredSize();
        }
        return new Dimension(PREF_W, PREF_H);
    }

    public Icon getBackIcon() {
        return backIcon;
    }

    public void setBackIcon(Icon backIcon) {
        this.backIcon = backIcon;
    }

    class LoadImageWorkerListener implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
                @SuppressWarnings("rawtypes")
                SwingWorker worker = (SwingWorker) evt.getSource();
                try {
                    if (optionDlg != null) {
                        optionDlg.setVisible(false);
                    }
                    worker.get();
                    for (int i = 0; i < CARD_COUNT; i++) {
                        Card card = deck.deal();
                        Icon cardIcon = cardIconMap.get(card);
                        JLabel cardLbl = new JLabel(cardIcon);
                        cardLbl.setSize(cardLbl.getPreferredSize());
                        int x = (PREF_W / 2) + WIDTH_SHOWING * (CARD_COUNT - 2 * i) / 2 - 
                              cardLbl.getPreferredSize().width / 2;
                        int y = PREF_H - cardLbl.getPreferredSize().height - WIDTH_SHOWING * 2;
                        cardLbl.setLocation(x, y);
                        basePane.add(cardLbl);
                     }
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                    System.exit(-1);
                };
            }
        }
    }

    class LoadImageWorker extends SwingWorker<Void, Void> {
        @Override
        protected Void doInBackground() throws Exception {
            for (Card card : deck) {
                String imgPath = String.format("%s%s_of_%s.png", PATH, 
                        card.getRank().getText().toLowerCase(), 
                        card.getSuit().toString().toLowerCase());
                URL imgUrl = new URL(imgPath);
                // System.out.println(imgPath);
                BufferedImage img = ImageIO.read(imgUrl);
                int w = img.getWidth();
                int h = img.getHeight();
                BufferedImage img2 = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
                Graphics2D g2 = img2.createGraphics();
                g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                g2.setColor(Color.WHITE);
                g2.fillRoundRect(1, 1, w - 2, h - 2, 10, 10);
                g2.setColor(Color.BLACK);
                g2.drawRoundRect(1, 1, w - 2, h - 2, 10, 10);
                g2.drawImage(img, 0, 0, null);
                g2.dispose();
                Icon icon = new ImageIcon(img2);
                cardIconMap.put(card, icon);
            }
            String backPath = PATH + BACK;
            URL imgUrl = new URL(backPath);
            // System.out.println(imgPath);
            BufferedImage img = ImageIO.read(imgUrl);
            setBackIcon(new ImageIcon(img));
            return null;
        }
    }

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

        JFrame frame = new JFrame("CardFun");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

class CardMouseAdapter extends MouseAdapter {
    private JLabel selectedCard = null;
    private JLayeredPane cardGameTable = null;
    private JPanel basePane = null;
    private int deltaX = 0;
    private int deltaY = 0;

    public CardMouseAdapter(JLayeredPane gameTable, JPanel basePane) {
       this.cardGameTable = gameTable;
       this.basePane = basePane;
    }

    @Override
    public void mousePressed(MouseEvent mEvt) {
       Component comp = basePane.getComponentAt(mEvt.getPoint());
       if (comp != null && comp instanceof JLabel) {
          selectedCard = (JLabel) comp;
          basePane.remove(selectedCard);
          basePane.revalidate();
          basePane.repaint();

          cardGameTable.add(selectedCard, JLayeredPane.DRAG_LAYER);
          cardGameTable.revalidate();
          cardGameTable.repaint();
          deltaX = mEvt.getX() - selectedCard.getX();
          deltaY = mEvt.getY() - selectedCard.getY();
       }
    }

    @Override
    public void mouseReleased(MouseEvent mEvt) {
       if (selectedCard != null) {
          cardGameTable.remove(selectedCard);
          cardGameTable.revalidate();
          cardGameTable.repaint();

          basePane.add(selectedCard, 0);
          basePane.revalidate();
          basePane.repaint();
          selectedCard = null;
       }
    }

    @Override
    public void mouseDragged(MouseEvent mEvt) {
       if (selectedCard != null) {
          int x = mEvt.getX() - deltaX;
          int y = mEvt.getY() - deltaY;
          selectedCard.setLocation(x, y);
          cardGameTable.revalidate();
          cardGameTable.repaint();
       }
    }
 }

enum Suit {
    CLUBS, DIAMONDS, HEARTS, SPADES;
}

enum Rank {
    ACE("Ace"), TWO("2"), THREE("3"), FOUR("4"), FIVE("5"), SIX("6"), SEVEN("7"), EIGHT("8"), NINE(
            "9"), TEN("10"), JACK("Jack"), QUEEN("Queen"), KING("King");

    private String text;

    private Rank(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }

}

class Card {
    private Suit suit;
    private Rank rank;

    public Card(Suit suit, Rank rank) {
        this.suit = suit;
        this.rank = rank;
    }

    public Suit getSuit() {
        return suit;
    }

    public Rank getRank() {
        return rank;
    }

    @Override
    public String toString() {
        return "Card [suit=" + suit + ", rank=" + rank + "]";
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((rank == null) ? 0 : rank.hashCode());
        result = prime * result + ((suit == null) ? 0 : suit.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Card other = (Card) obj;
        if (rank != other.rank)
            return false;
        if (suit != other.suit)
            return false;
        return true;
    }
}

class Deck implements Iterable<Card> {
    private List<Card> cards;

    public Deck() {
        reset();
    }

    public final void reset() {
        cards = new ArrayList<>();
        for (Rank rank : Rank.values()) {
            for (Suit suit : Suit.values()) {
                cards.add(new Card(suit, rank));
            }
        }
        Collections.shuffle(cards);
    }

    public Card deal() {
        return cards.remove(0);
    }

    @Override
    public Iterator<Card> iterator() {
        return cards.iterator();
    }
}