Перетащите изображение внутри JLabel щелчком мыши.

У меня есть изображение внутри JLabel.

JLabel label = new JLabel(new ImageIcon("C:\\image.jpg"));
label.setSize(300,300);

Я хочу следующую функциональность.

-Я нажимаю на место внутри JLabel (на изображении).

-Нажав кнопку мыши, я могу изменить расположение изображения в JLabel. (Я перетаскиваю изображение в разные места в JLabel)

Что ж, это означает, что во многих случаях изображение будет обрезано и не будет видно.

Подскажите, пожалуйста, как реализовать этот функционал?

Какие прослушиватели событий следует добавить в JLabel?


person ThePrince    schedule 29.10.2012    source источник
comment
Скорее всего, вам потребуется создать класс, расширяющий JLabel.   -  person Code-Apprentice    schedule 30.10.2012
comment
Почему это должно быть JLabel ?   -  person Robin    schedule 30.10.2012
comment
Робин, что вы предлагаете мне использовать для этой функции? JLabel был первым, что пришло на ум, когда я подумал об изображениях, но на самом деле я новичок в изображениях.   -  person ThePrince    schedule 30.10.2012
comment
Вам нужно добавить MouseListener. Вам также необходимо создать класс, как сказал Code-Guru, который расширяет JLabel и переопределяет paintComponent. Внутри paintComponent вам нужно будет удалить части вашего изображения на mousePressed. Я работаю над этим, и я опубликую ответ в ближайшее время.   -  person Coupon22    schedule 30.10.2012
comment
Вероятно, вам также следует взглянуть на MouseMoationListeners.   -  person Coupon22    schedule 30.10.2012


Ответы (3)


Это базовый пример...

Он работает путем разделения метки на сетку 3x3, где каждая ячейка представляет возможную позицию для значка.

public class TestMouseDrag {

    public static void main(String[] args) {
        new TestMouseDrag();
    }

    public TestMouseDrag() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException ex) {
                } catch (InstantiationException ex) {
                } catch (IllegalAccessException ex) {
                } catch (UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new DragMyIcon());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    protected class DragMyIcon extends JPanel {

        private JLabel label;

        public DragMyIcon() {

            ImageIcon icon = null;

            try {
                icon = new ImageIcon(ImageIO.read(getClass().getResource("/bomb.png")));
            } catch (IOException ex) {
                ex.printStackTrace();
            }

            label = new JLabel(icon);
            label.setHorizontalAlignment(JLabel.CENTER);
            label.setVerticalAlignment(JLabel.CENTER);

            setLayout(new BorderLayout());
            add(label);

            MouseHandler handler = new MouseHandler();
            label.addMouseListener(handler);
            label.addMouseMotionListener(handler);

        }

    }

    protected class MouseHandler extends MouseAdapter {

        private boolean active = false;

        @Override
        public void mousePressed(MouseEvent e) {

            JLabel label = (JLabel) e.getComponent();
            Point point = e.getPoint();

            active = getIconCell(label).contains(point);
            if (active) {
                label.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
            } else {
                label.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
            }

        }

        @Override
        public void mouseReleased(MouseEvent e) {
            active = false;
            JLabel label = (JLabel) e.getComponent();
            label.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
        }

        @Override
        public void mouseDragged(MouseEvent e) {
            if (active) {
                JLabel label = (JLabel) e.getComponent();
                Point point = e.getPoint();

                int verticalAlign = label.getVerticalAlignment();
                int horizontalAlign = label.getHorizontalAlignment();

                if (isWithInColumn(label, point, 0)) {
                    horizontalAlign = JLabel.LEFT;
                } else if (isWithInColumn(label, point, 1)) {
                    horizontalAlign = JLabel.CENTER;
                } else if (isWithInColumn(label, point, 2)) {
                    horizontalAlign = JLabel.RIGHT;
                }

                if (isWithInRow(label, point, 0)) {
                    verticalAlign = JLabel.TOP;
                } else if (isWithInRow(label, point, 1)) {
                    verticalAlign = JLabel.CENTER;
                } else if (isWithInRow(label, point, 2)) {
                    verticalAlign = JLabel.BOTTOM;
                }

                label.setVerticalAlignment(verticalAlign);
                label.setHorizontalAlignment(horizontalAlign);

                label.invalidate();
                label.repaint();

            }
        }

        @Override
        public void mouseMoved(MouseEvent e) {
        }

        protected boolean isWithInColumn(JLabel label, Point p, int gridx) {
            int cellWidth = label.getWidth() / 3;
            int cellHeight = label.getHeight();

            Rectangle bounds = new Rectangle(gridx * cellWidth, 0, cellWidth, cellHeight);

            return bounds.contains(p);
        }

        protected boolean isWithInRow(JLabel label, Point p, int gridY) {
            int cellWidth = label.getWidth();
            int cellHeight = label.getHeight() / 3;

            Rectangle bounds = new Rectangle(0, cellHeight * gridY, cellWidth, cellHeight);

            return bounds.contains(p);
        }

        private Rectangle getIconCell(JLabel label) {

            Rectangle bounds = new Rectangle();

            int cellWidth = label.getWidth() / 3;
            int cellHeight = label.getHeight() / 3;

            bounds.width = cellWidth;
            bounds.height = cellHeight;

            if (label.getHorizontalAlignment() == JLabel.LEFT) {
                bounds.x = 0;
            } else if (label.getHorizontalAlignment() == JLabel.CENTER) {
                bounds.x = cellWidth;
            } else if (label.getHorizontalAlignment() == JLabel.RIGHT) {
                bounds.x = cellWidth * 2;
            } else {
                bounds.x = 0;
                bounds.width = 0;
            }
            //if (label.getHorizontalAlignment() == JLabel.TOP) {
            //    bounds.y = 0;
            //} else if (label.getHorizontalAlignment() == JLabel.CENTER) {
            //    bounds.y = cellHeight;
            //} else if (label.getHorizontalAlignment() == JLabel.BOTTOM) {
            //    bounds.y = cellHeight * 2;
            //} else {
            //    bounds.y = 0;
            //    bounds.height = 0;
            //}
            if (label.getVerticalAlignment() == JLabel.TOP) {
                bounds.y = 0;
            } else if (label.getVerticalAlignment() == JLabel.CENTER) {
                bounds.y = cellHeight;
            } else if (label.getVerticalAlignment() == JLabel.BOTTOM) {
                bounds.y = cellHeight * 2;
            } else {
                bounds.y = 0;
                bounds.height = 0;
            }

            return bounds;

        }

    }

}

ОБНОВЛЕНО на основе отзывов

В этом примере в основном используется JLayerdPane, чтобы разрешить изменение положения JLabels внутри его контейнера...

public class MoveMe {

    public static void main(String[] args) {
        new MoveMe();
    }

    public MoveMe() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException ex) {
                } catch (InstantiationException ex) {
                } catch (IllegalAccessException ex) {
                } catch (UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new MoveMePane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class MoveMePane extends JLayeredPane {

        public MoveMePane() {
            int width = 400;
            int height = 400;
            for (int index = 0; index < 10; index++) {
                String text = "Label " + index;
                JLabel label = new JLabel(text);
                label.setSize(label.getPreferredSize());

                int x = (int) Math.round(Math.random() * width);
                int y = (int) Math.round(Math.random() * height);
                if (x + label.getWidth() > width) {
                    x = width - label.getWidth();
                }
                if (y + label.getHeight() > width) {
                    y = width - label.getHeight();
                }
                label.setLocation(x, y);
                add(label);
            }

            MoveMeMouseHandler handler = new MoveMeMouseHandler();
            addMouseListener(handler);
            addMouseMotionListener(handler);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }
    }

    public class MoveMeMouseHandler extends MouseAdapter {

        private int xOffset;
        private int yOffset;
        private JLabel draggy;
        private String oldText;

        @Override
        public void mouseReleased(MouseEvent me) {
            if (draggy != null) {
                draggy.setText(oldText);
                draggy.setSize(draggy.getPreferredSize());
                draggy = null;
            }
        }

        public void mousePressed(MouseEvent me) {
            JComponent comp = (JComponent) me.getComponent();
            Component child = comp.findComponentAt(me.getPoint());
            if (child instanceof JLabel) {
                xOffset = me.getX() - child.getX();
                yOffset = me.getY() - child.getY();

                draggy = (JLabel) child;
                oldText = draggy.getText();
                draggy.setText("What a drag");
                draggy.setSize(draggy.getPreferredSize());
            }
        }

        public void mouseDragged(MouseEvent me) {
            if (draggy != null) {
                draggy.setLocation(me.getX() - xOffset, me.getY() - yOffset);
            }
        }
    }
}
person MadProgrammer    schedule 30.10.2012
comment
Это очень полезный пример. Вы знаете, почему это работает только при первом перетаскивании? Хм, я посмотрю код и посмотрю, быстрое ли это решение. Спасибо за ваш замечательный пример. Попробую адаптировать под нужды моей программы, а там посмотрим :) - person ThePrince; 31.10.2012
comment
@user1784129 user1784129 Наверное, потому что я не спал 3 дня :P - Я исправил ошибку, она должна работать ... лучше ;) - person MadProgrammer; 31.10.2012
comment
Я немного изменил ваш код и заставил его работать именно так, как задумано. Ваш код показал мне, каких слушателей добавить, а также как выполнять перетаскивание. Спасибо за вашу помощь. - person ThePrince; 31.10.2012

Во-первых, я бы рекомендовал использовать макет вместо setLayout(null) и setBounds(). Во-вторых, отделите ImageIcon от JLabel. Наконец, установите JLabel и ImageIcon как поля вместо локальной переменной.

Вам нужно добавить MouseListener и MouseMotionListener.

label.addMouseMotionListener(new MouseAdapter()
{
    public void mouseMoved(MouseEvent arg0)
    {
        if(clicked)
        {
            label.x++
            label.y++
            label.repaint();
        }
    }
});

label.addMouseListener(new MouseAdapter()
{
    public void mousePressed(MouseEvent arg0)
    {
        clicked = !clicked;
    }
});

(извините за многословие заранее)

Это приводит к тому, что изображение метки появляется там, где находится метка, и когда вы наводите на нее указатель мыши, оно перемещается по диагонали на юго-восток. Я создал новый класс меток, в котором я переопределяю paintComponent и добавляю туда метод значка изображения для рисования, где x и y являются переменными. Идея будет состоять в том, чтобы вычислить центральную точку на вашей этикетке, а затем переместить позицию x изображения на запад, если идти к западу от этой точки (x--), на юг, если идти на юг (y--), и т. д. Это только переместит ваше изображение внутри этикетки. Если ваша мышь окажется за пределами метки, перемещение остановится. Если части изображения находятся за пределами метки, то эта часть не будет отображаться. Я бы переопределил компонент краски в вашем классе меток и переместил изображение, а затем установил значок для каждого движения.

public class Label1 extends JLabel
{
    public int x;
    public int y;
    ImageIcon imageIcon = new ImageIcon("path");

    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
//No setLocation(x, y) method exists for an image icon or an image. You are on your own on this
        imageIcon.setLocation(x, y);
        label.setIcon(imageIcon);
    }
}
person Coupon22    schedule 29.10.2012
comment
Я собираюсь проверить это. Спасибо, что нашли время написать это. Я поработаю над setLocation() для значка изображения и посмотрю, найду ли обходной путь. Вернись через мгновение. - person ThePrince; 31.10.2012
comment
Хорошо... может быть, мне следует поместить JLabel в JPanel и переместить его внутри JPanel? Я попробую закодировать это и посмотреть, работает ли это. Спасибо за эти идеи. - person ThePrince; 31.10.2012

Этот модифицированный пример основан на коде MadProgrammer.

Это показывает поведение, которое я описал в своем исходном посте. Спасибо всем за вашу ценную помощь, особенно MadProgrammer и Coupon22. Попробуйте.

import java.awt.*;
import javax.swing.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseAdapter;

public class TestMouseDrag {

public static void main(String[] args) {
    new TestMouseDrag();
}

public TestMouseDrag() {
    EventQueue.invokeLater(new Runnable() {
        @Override
        public void run() {
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (ClassNotFoundException ex) {
            } catch (InstantiationException ex) {
            } catch (IllegalAccessException ex) {
            } catch (UnsupportedLookAndFeelException ex) {
            }

            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLayout(new BorderLayout());
            frame.add(new DragMyIcon("C://image.jpg"));
            frame.pack();
            frame.setSize(500,500);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    });
}

protected class DragMyIcon extends JPanel {

    public static final long serialVersionUID = 172L;
    private JLabel label;

    public DragMyIcon(String path) {
        setLayout(null);

        ImageIcon icon = null;

        icon = new ImageIcon(path);

        label = new JLabel(icon);
        label.setBounds(0,0,icon.getIconWidth(), icon.getIconHeight());
        setBounds(0,0,icon.getIconWidth(), icon.getIconHeight());
        label.setHorizontalAlignment(JLabel.CENTER);
        label.setVerticalAlignment(JLabel.CENTER);

        add(label);

        MouseHandler handler = new MouseHandler();
        label.addMouseListener(handler);
        label.addMouseMotionListener(handler);

    }

}

protected class MouseHandler extends MouseAdapter {

    private boolean active = false;
    private int xDisp;
    private int yDisp;

    @Override
    public void mousePressed(MouseEvent e) {
        active = true;
        JLabel label = (JLabel) e.getComponent();

        xDisp = e.getPoint().x - label.getLocation().x;
        yDisp = e.getPoint().y - label.getLocation().y;

        label.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        active = false;
        JLabel label = (JLabel) e.getComponent();
        label.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
    }

    @Override
    public void mouseDragged(MouseEvent e) {
        if (active) {
            JLabel label = (JLabel) e.getComponent();
            Point point = e.getPoint();
            label.setLocation(point.x - xDisp, point.y - yDisp);
            label.invalidate();
            label.repaint();

        }
    }

    @Override
    public void mouseMoved(MouseEvent e) {
    }
}

}

person ThePrince    schedule 31.10.2012
comment
Я бы посоветовал вам использовать JLayredPane вместо нулевого макета. - person MadProgrammer; 31.10.2012
comment
И я просто собираюсь быть придирчивым, но это не то, о чем задавался ваш вопрос;) вы просили средство для перетаскивания значка в контексте метки, а не перетаскивание самой метки, это бы многое начало проще ;р - person MadProgrammer; 31.10.2012
comment
Это правда. Я думаю, это просто притворяется, чтобы показать функциональность. Кроме того, есть ли что-то изначально неправильное в нулевом макете? - person ThePrince; 01.11.2012
comment
Макеты null обычно не рекомендуются, поскольку они могут вызвать бесконечные проблемы и повысить уровень сложности программы (обычно без какого-либо выигрыша). Вы можете взглянуть на DragLayout, который предназначен для этого. процесс проще. Не поймите меня неправильно, бывают случаи, когда макет null может быть полезен, но вы должны изучить все доступные варианты, прежде чем остановиться на нем - ИМХО - person MadProgrammer; 01.11.2012
comment
У вас есть модификация, которую вы бы сделали, чтобы сделать изображение перетаскиваемым без нулевого макета? Перетаскивание должно быть точным, а не квадратами на сетке, если только вы не сделаете сетку настолько маленькой, что она будет больше похожа на точки? Уже почти :) - person ThePrince; 02.11.2012
comment
Это открытый вопрос. Как бы я изменил свой исходный пример, вы не можете. Положение значка внутри метки контролируется самой меткой. Как бы я изменил ваш пример, проверьте здесь для реализации, которую я уже сделал ;) - person MadProgrammer; 03.11.2012
comment
Отмеченный галочкой ответ должен перетаскиваться плавно, а не по сетке. Но я действительно хочу поставить вам галочку, так что, может быть, вы могли бы сделать так, чтобы она плавно перетаскивалась, чтобы я мог поставить вам галочку, хе-хе :) Конечно, я перетаскиваю саму метку, но если вы поместите ее в JPanel, это своего рода обслуживание та же цель :) - person ThePrince; 03.11.2012