点击图片即可进行JFrame图片更新

问题描述

tldr; 如何在JFrame对象(特别是JLabel)上使用MouseEvent更新JFrame中显示的图像

我正在尝试创建一个程序,在该程序中,将图像分解为图块,并单击其中一个图块,该程序将图块移至图像中的空白处。 (有关更多信息,请参见Sliding Puzzle

我目前能够选择一张图片,将该图片分成小块,然后“随机删除其中一个小块。

我的下一步是,单击其中一个图块,将其与空图块交换(我将处理稍后要交换的图块的“资格”,但现在,只想能够将所选图块与当前空白图块交换)

要为3x3网格创建图像,我将bufferedImage分成9个相等的部分,将其中一张图像“随机”地空白,然后使用GridLayout将它们排列在jLabel中。

当我将mouseListeners添加到每个jLabel时,我可以看到正在进入MouseListener,因为可以看到如下所示的控制台日志消息“ Clicked”,但是无法根据需要更新图像。 / p>

如何在这些JFrame JLabel上使用MouseEvent来调用ImageContainer中的方法(即move())并更新显示的图像?

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Scanner;

public class ImageJumble extends JPanel {

    static int difficulty = 0;

    ImageContainer imageContainer;
    JFrame jFrame = new JFrame("Image Jumble");

    private void run() {
        SwingUtilities.invokelater(this::displayImage);
    }

    public static void main(String[] args) {
        System.out.print("Please enter the difficulty level (1-3): ");
        Scanner scanner = new Scanner(system.in);
        difficulty = scanner.nextInt();

        new ImageJumble().run();
    }

    private void displayImage() {
        JFileChooser fc = new JFileChooser();
        fc.setDialogTitle("Please choose an image...");
        FileNameExtensionFilter filter = new FileNameExtensionFilter("JPEG","jpeg","jpg","png","bmp","gif");
        fc.addChoosableFileFilter(filter);

        BufferedImage image = null;

        if (fc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
            File selectedFile = fc.getSelectedFile();
            try {
                image = ImageIO.read(selectedFile);
            } catch (IOException ex) {
                ex.printstacktrace();
            }
        }


        jFrame.setDefaultCloSEOperation(WindowConstants.EXIT_ON_CLOSE);
        jFrame.setSize(image.getWidth(),image.getHeight());
        jFrame.setVisible(true);
        jFrame.setLayout(new GridLayout(difficulty,difficulty,0));


        imageContainer = new ImageContainer(image,difficulty);

        createImage();
    }

    private void createImage() {
        imageContainer.randomize();
        JLabel[] jLabels = new JLabel[difficulty * difficulty];
        for(int i = 0; i < jLabels.length; i++) {
            JLabel jLabel = new JLabel(new ImageIcon(imageContainer.getBufferedImages().get(i)));
            jFrame.add(jLabel);
            jLabel.addMouseListener(new MouseAdapter()
            {
                public void mouseClicked(MouseEvent e)
                {
                    System.out.println("Clicked!");
                    imageContainer.move(i);
                    jFrame.removeAll();
                    createImage();
                }
            });
        }
    }


}


import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class ImageContainer {
    private List<BufferedImage> bufferedImages = new ArrayList<>();
    private int blankLocation = 0;

    public ImageContainer(BufferedImage image,int difficulty) {
        BufferedImage bi = new BufferedImage(image.getWidth(null),image.getHeight(null),BufferedImage.TYPE_INT_RGB);
        Graphics g = bi.createGraphics();
        g.drawImage(image,null);
        int width = bi.getWidth();
        int height = bi.getHeight();
        int swidth = width / difficulty;
        int sheight = height / difficulty;

        for (int i = 0; i < difficulty; i++) {
            for (int j = 0; j < difficulty; j++) {
                BufferedImage bimg = bi.getSubimage(j * swidth,i * sheight,swidth,sheight);
                bufferedImages.add(bimg);
            }
        }


    }


    public List<BufferedImage> getBufferedImages() {
        return bufferedImages;
    }

    public void setBufferedImages(List<BufferedImage> bufferedImages) {
        this.bufferedImages = bufferedImages;
    }

    public void randomize() {
        int size = bufferedImages.size();
        int width = bufferedImages.get(0).getWidth();
        int height = bufferedImages.get(0).getHeight();
        blankLocation = new Random().nextInt(size);
        bufferedImages.set(blankLocation,new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB));
    }

    public void move(int i) {
        bufferedImages.set(blankLocation,bufferedImages.get(i));
        blankLocation = i;
    }
}

解决方法

此答案基于this的上一个答案,该答案适用于您的代码。
这是一个文件的文件:可以将整个代码复制粘贴到ImageJumble.java文件中并运行。
它支持从任何网格位置到任何其他位置的DnD。您可能要更改它。
请注意注释:

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetAdapter;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;

public class ImageJumble extends JPanel {

    private static int difficulty = 3;
    private static final String FLOWER = "http://www.digitalphotoartistry.com/rose1.jpg";
    private static final int GAP = 4;

    private JPanel content;

    private void run() {
        SwingUtilities.invokeLater(this::displayImage);
    }

    public static void main(String[] args) {
        new ImageJumble().run();
    }

    private void displayImage() {

        BufferedImage bi = null;

        try {
            bi = ImageIO.read(new URL(FLOWER));
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        content = new JPanel(new GridLayout(difficulty,difficulty,GAP,GAP));
        createImage(bi);
        JFrame jFrame = new JFrame("Image Jumble");
        jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        jFrame.add(content);
        jFrame.pack();
        jFrame.setVisible(true);
    }

    private void createImage( BufferedImage bi) {

        ImageContainer imageContainer = new ImageContainer(bi,difficulty);
        imageContainer.randomize();

        for(int i = 0; i < difficulty * difficulty; i++) {
            content.add(new DragDropPane(imageContainer.getBufferedImages().get(i)));;
        }
    }
}

class ImageContainer {
    private final List<BufferedImage> bufferedImages = new ArrayList<>();
    private int blankLocation = 0;

    public ImageContainer(BufferedImage image,int difficulty) {
        BufferedImage bi = new BufferedImage(image.getWidth(null),image.getHeight(null),BufferedImage.TYPE_INT_RGB);
        Graphics g = bi.createGraphics();
        g.drawImage(image,null);
        int width = bi.getWidth();
        int height = bi.getHeight();
        int swidth = width / difficulty;
        int sheight = height / difficulty;

        for (int i = 0; i < difficulty; i++) {
            for (int j = 0; j < difficulty; j++) {
                BufferedImage bimg = bi.getSubimage(j * swidth,i * sheight,swidth,sheight);
                bufferedImages.add(bimg);
            }
        }
    }

    public List<BufferedImage> getBufferedImages() {
        return bufferedImages;
    }

    public void randomize() {
        Collections.shuffle(bufferedImages);//shuffle images
        int size = bufferedImages.size();
        int width = bufferedImages.get(0).getWidth();
        int height = bufferedImages.get(0).getHeight();
        blankLocation = new Random().nextInt(size);
        bufferedImages.set(blankLocation,new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB));
    }
}

class DragDropPane extends JPanel implements DragGestureListener,DragSourceListener {

    private JComponent dragable;
    private final BufferedImage bi;

    public DragDropPane(BufferedImage bi) {

        setBackground(Color.BLACK);
        this.bi = bi;
        var label = new JLabel(new ImageIcon(bi),JLabel.CENTER);
        setContent(label);
        new MyDropTargetListener(this);
        DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(
                                        this,DnDConstants.ACTION_COPY,this);
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(bi.getWidth(),bi.getHeight());
    }

    public void setContent(JComponent component) {
        removeAll();
        dragable = component;
        add(component);
        repaint();
    }

    //-->DragGestureListener implementation

    @Override
    public void dragGestureRecognized(DragGestureEvent dge) {

        // Create our transferable wrapper
        Transferable transferable = new TransferableComponent(dragable);

        // Start the "drag" process...
        DragSource ds = dge.getDragSource();
        ds.startDrag(dge,Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR),transferable,this);
        remove(dragable);
        revalidate(); repaint();
    }

    //-->DragSourceListener implementation

    @Override
    public void dragEnter(DragSourceDragEvent dsde) {}

    @Override
    public void dragOver(DragSourceDragEvent dsde) {}

    @Override
    public void dropActionChanged(DragSourceDragEvent dsde) {}


    @Override
    public void dragExit(DragSourceEvent dse) {}

    @Override
    public void dragDropEnd(DragSourceDropEvent dsde) {
        // If the drop was not successful,we need to
        // return the component back to it's previous
        // parent
        if (!dsde.getDropSuccess()) {
            setContent(dragable);
        }
    }
}

class MyDropTargetListener extends DropTargetAdapter {

    private final DragDropPane target;

    public MyDropTargetListener(DragDropPane target) {
        this.target = target;
        new DropTarget(target,this,true,null);
    }

    @Override
    public void drop(DropTargetDropEvent event) {

        try {

            var tr = event.getTransferable();
            var component = (JComponent) tr.getTransferData(TransferableComponent.component);

            if (event.isDataFlavorSupported(TransferableComponent.component)) {

                event.acceptDrop(DnDConstants.ACTION_COPY);
                target.setContent(component);
                event.dropComplete(true);

            } else {
                event.rejectDrop();
            }

        } catch (Exception e) {
            e.printStackTrace();
            event.rejectDrop();
        }
    }
}

class TransferableComponent implements Transferable {

    protected static final DataFlavor component =
            new DataFlavor(JComponent.class,"A Component");

    protected static final DataFlavor[] supportedFlavors = {
            component
    };

    private final JComponent componentToTransfer;

    public TransferableComponent(JComponent componentToTransfer) {

        this.componentToTransfer = componentToTransfer;
    }

    @Override
    public DataFlavor[] getTransferDataFlavors() {

        return supportedFlavors;
    }

    @Override
    public boolean isDataFlavorSupported(DataFlavor flavor) {

        return flavor.equals(component);
    }

    @Override
    public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException {

        if (flavor.equals(component)) return componentToTransfer;
        else  throw new UnsupportedFlavorException(flavor);
    }
}

enter image description here

,

不删除/添加组件。而是只需交换图标。

  1. 将JPanel与GridLayout一起使用,该GridLayout在每个网格中都包含一个JLabel
  2. 向每个JLabel添加ImageIcon(一个除外)
  3. 向每个JLabel添加一个MouseListner。
  4. 在mouseClicked事件中,您将获得单击的标签,然后删除ImageIcon并将图标添加到空白标签。

因此,当您创建木板时,您可能会有一个像emptyLabel这样的变量,该变量将被初始化为不带图标的标签。那么mouseClicked中的逻辑可能类似于:

JLabel clicked = (JLabel)e.getSource();
emptyLabel.setIcon( clicked.getIcon() );
emptyLabel = clicked;
,

创建一个自定义JLabel类以保留位置(i):

class MyImageLabel extends JLabel {
    private int position;
    public MyImageLabel(Icon image,int position) {
        super(image);
        this.position=position;
    }
    public int getPosition()
    {
        return position;
    }
}

自适应createImage来实例化此类而不是JLabel,在mouseListener中,您可以调用getPosition()

    for(int i = 0; i < jLabels.length; i++) {
        MyImageLabel jLabel = new MyImageLabel(new ImageIcon(imageContainer.getBufferedImages().get(i)),i);
        jFrame.add(jLabel);
        jLabel.addMouseListener(new MouseAdapter()
        {
            public void mouseClicked(MouseEvent e)
            {
                System.out.println("Clicked!");
                imageContainer.move(jLabel.getPosition());
                jFrame.removeAll();
                createImage();
            }
        });
    }
,

我的建议是不要在JFrame对象上调用.add和.removeAll,而是使用GridLayout创建一个新的JPanel并使用jframe.getContentPanel()。add(labelsPanel)。如果它没有刷新,则可以在JPanel上调用revalidate()。