Перетаскивание для перемещения JTextArea на JPanel

Я хочу иметь возможность щелкнуть JTextArea и перетащить его вокруг моего JPanel. Я не уверен, как это сделать. То, что я пытаюсь сделать, это изменить координаты x, y JTextArea по мере его перетаскивания, я не перетаскиваю JTextArea выше или ниже другого. Просто по экрану, аналогично перемещению текстовых полей в такой программе, как Microsoft PowerPoint.

Единственный метод, который я могу придумать, - это использовать MouseListener, но мне интересно, есть ли более простой способ реализовать его, кроме обнаружения наведения/нажатия/перетаскивания на JTextArea. Любые идеи о том, как я могу начать?

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JTextArea;

public class UMLEditor {

    public static void main(String[] args) {
        JFrame frame = new UMLWindow();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setBounds(30, 30, 1000, 700);
        frame.getContentPane().setBackground(Color.white);
        frame.setVisible(true);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

class UMLWindow extends JFrame {
    Canvas canvas = new Canvas();

    private static final long serialVersionUID = 1L;

    public UMLWindow() {
        addMenus();
    }

    public void addMenus() {

        getContentPane().add(canvas);

        JMenuBar menubar = new JMenuBar();

        JMenuItem newTextBox = new JMenuItem("New Text Box");
        newTextBox.setMnemonic(KeyEvent.VK_E);
        newTextBox.setToolTipText("Exit application");
        newTextBox.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent event) {
                canvas.addTextBox();
            }
        });

        menubar.add(newTextBox);

        setJMenuBar(menubar);

        setSize(300, 200);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }
}

class Canvas extends JPanel {

    JTextArea commentTextArea = new JTextArea(10, 10);

    public Canvas() {
        this.setOpaque(true);
        MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
        addMouseListener(myMouseAdapter);
        addMouseMotionListener(myMouseAdapter);
    }

    public void addTextBox() {

        commentTextArea.setLineWrap(true);
        commentTextArea.setWrapStyleWord(true);
        commentTextArea.setVisible(true);
        commentTextArea.setLocation(0, 0);
        this.add(commentTextArea);
        commentTextArea.setBounds(0, 0, 100, 100);

        revalidate();
        repaint();
    }

    class MyMouseAdapter extends MouseAdapter {

        @Override
        public void mousePressed(MouseEvent e) {

        }

        @Override
        public void mouseDragged(MouseEvent e) {

        }

        @Override
        public void mouseMoved(MouseEvent e) {

        }
    }
}

person Harry    schedule 28.10.2014    source источник
comment
Остерегайтесь смешивания тяжелых (AWT) и легких (Swing) компонентов веса, есть проблемы с z-порядком.   -  person MadProgrammer    schedule 28.10.2014
comment
Все JTextComponent имеют концепцию перетаскивания, с которой вы не хотите связываться, вместо этого вы должны предоставить область вокруг компонента, из которой пользователь может щелкнуть и перетащить, что затем позволит им расположить компонент сверху.   -  person MadProgrammer    schedule 28.10.2014
comment
Я попытался создать прямоугольник, используя координаты/размеры JTextArea следующим образом: MouseMoved -> if(rectangle.contains(e.getPoint()) { System.out.println("Testing hit") } , и он не обнаруживает наведение курсора на JTextArea. Это концептуально то, что вы имели в виду?   -  person Harry    schedule 28.10.2014
comment
Не над текстовой областью, вокруг нее...   -  person MadProgrammer    schedule 28.10.2014
comment
Я только что попробовал это, и это работает. Это лучшее, что я могу сделать? Я не могу проверить, содержит ли сам JTextArea точку?   -  person Harry    schedule 28.10.2014
comment
Я уже дал вам ответ на один из ваших предыдущих вопросов, который показал, как вы можете перетаскивать любой компонент по экрану.   -  person camickr    schedule 28.10.2014
comment
Я бы не стал, как я уже сказал, JTextComponent иметь функциональность, которая позволяет им перетаскивать и выбирать текст, вы не хотите конкурировать или нарушать эту функциональность   -  person MadProgrammer    schedule 28.10.2014
comment
@camickr в каком вопросе...?   -  person Harry    schedule 28.10.2014
comment
@MadProgrammer Я понимаю, что ты сейчас говоришь. Какие функциональные возможности предоставляют JTextComponents? И почему их нельзя (или не следует) использовать?   -  person Harry    schedule 28.10.2014
comment
Ну вы сделали комментарий, что вы посмотрите на код. По-видимому, вы этого не сделали, поэтому я позволю вам поискать ваши предыдущие вопросы.   -  person camickr    schedule 28.10.2014
comment
@camickr, мне нужно перепроверить... должно быть, я схожу с ума, лол, спасибо   -  person Harry    schedule 28.10.2014


Ответы (1)


Вы действительно не хотите пытаться «перетаскивать» JTextComponents, у них уже включена функциональность, которая позволяет пользователю щелкать и перетаскивать, чтобы выделить текст, вы действительно не хотите конкурировать в этом.

Вместо этого вы хотите определить область «горячей зоны» вокруг компонента, которая позволит вам «выделить» компонент в некоторых местах и ​​позволить пользователю перетаскивать компонент через него.

Например...

Переместить Генделя

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;

public class DragMe {

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

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

                JTextArea ta = new JTextArea(10, 20);
                ta.setText("Bananas in pajamas");
                JScrollPane sp = new JScrollPane(ta);

                DragProxyPane proxy = new DragProxyPane(sp);
                proxy.setSize(proxy.getPreferredSize());
                proxy.setLocation(100 - proxy.getWidth() / 2, 100 - proxy.getHeight()/ 2);

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setContentPane(new JPanel() {
                    @Override
                    public Dimension getPreferredSize() {
                        return new Dimension(300, 300);
                    }
                });
                frame.add(proxy);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public static class DragProxyPane extends JPanel {

        public static final int BUFFER_ZONE = 10;

        private boolean mouseInHouse;
        private JComponent component;

        private List<HotZone> hotZones;

        public DragProxyPane(JComponent comp) {
            MouseAdapter ma = new MouseAdapter() {

                @Override
                public void mouseEntered(MouseEvent e) {
                    mouseInHouse = true;
                    repaint();
                }

                @Override
                public void mouseExited(MouseEvent e) {
                    mouseInHouse = false;
                    repaint();
                }

                @Override
                public void mouseMoved(MouseEvent e) {
                    Cursor cursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);
                    for (HotZone hz : hotZones) {
                        if (hz.getBounds(getSize()).contains(e.getPoint())) {
                            cursor = hz.getCursor();
                            break;
                        }
                    }
                    setCursor(cursor);
                }

            };

            addMouseListener(ma);
            addMouseMotionListener(ma);

            setOpaque(false);
            setLayout(new BorderLayout());
            add(comp);

            setBorder(new EmptyBorder(BUFFER_ZONE, BUFFER_ZONE, BUFFER_ZONE, BUFFER_ZONE));

            hotZones = new ArrayList<>(8);
            // Top left, middle, right
            hotZones.add(new HotZone(0f, 0f, Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR)));
            hotZones.add(new HotZone(0.5f, 0f, Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR)));
            hotZones.add(new HotZone(1f, 0f, Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR)));
            // Left, right
            hotZones.add(new HotZone(0f, 0.5f, Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR)));
            hotZones.add(new HotZone(1f, 0.5f, Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)));
            // Bottom left, middle, right
            hotZones.add(new HotZone(0f, 1f, Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR)));
            hotZones.add(new HotZone(0.5f, 1f, Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR)));
            hotZones.add(new HotZone(1f, 1f, Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR)));

        }


        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            if (mouseInHouse) {
                g2d.setColor(Color.BLACK);
                for (HotZone hotZone : hotZones) {
                    g2d.draw(hotZone.getBounds(getSize()));
                }
            }
            g2d.dispose();
        }

        public class HotZone {

            private float x, y;
            private Cursor cursor;

            public HotZone(float x, float y, Cursor cursor) {
                this.x = x;
                this.y = y;
                this.cursor = cursor;
            }

            public Cursor getCursor() {
                return cursor;
            }

            public Rectangle getBounds(Dimension size) {

                return getBounds(size.width - 1, size.height - 1);

            }

            public Rectangle getBounds(int width, int height) {

                int halfBuffer = BUFFER_ZONE / 2;

                float xPos = (width * x) - halfBuffer;
                float yPos = (height * y) - halfBuffer;

                xPos = Math.min(Math.max(0, xPos), width - BUFFER_ZONE);
                yPos = Math.min(Math.max(0, yPos), height - BUFFER_ZONE);

                return new Rectangle(Math.round(xPos), Math.round(yPos), BUFFER_ZONE, BUFFER_ZONE);

            }

        }

    }

}

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

Теперь этот пример не перетаскивает, извините, у вас есть много других примеров, которые должны помочь вам преодолеть черту, но вы можете просто добавить MouseListener/MouseMoitionListener к прокси, чтобы определить, когда пользователь перетаскивает, но вы нужно будет добавить к нему еще несколько функций, чтобы определить, что на самом деле означает это перетаскивание (изменение размера или перемещение);)

person MadProgrammer    schedule 28.10.2014
comment
не жалей, что не доделал всего! Это как раз то, что мне нужно было для начала. Благодаря тонну - person Harry; 28.10.2014
comment
Точно так же вы можете самостоятельно содержать компонент, сохраняя перетаскивание/изменение размера входа в прокси-сервере, таким образом, вы можете просто поместить их на экран и позволить им работать самостоятельно;) - person MadProgrammer; 28.10.2014
comment
Что вы подразумеваете под самосодержанием компонента? - person Harry; 28.10.2014
comment
Его функциональность самодостаточна, вам не нужно добавлять к нему дополнительные функции (например, добавлять прослушиватели мыши для выполнения операций перетаскивания/размера), потому что компонент способен делать все это сам... - person MadProgrammer; 28.10.2014
comment
Попался, изучаю реализацию. Спасибо еще раз - person Harry; 28.10.2014