java - 拖动文本区域

标签 java swing draggable jtextarea

问题代码:

    textArea.addMouseListener(new MouseAdapter() {
        public void mousePressed(MouseEvent e) {
            posX = e.getX();
            posY = e.getY();
        }
    });
    textArea.addMouseMotionListener(new MouseAdapter() {
        public void mouseDragged(MouseEvent e) {
            setLocation(e.getXOnScreen() - posX, e.getYOnScreen() - posY);
        }
    });

背景:

我有一个 JFrame,在那个 JFrame 中有一个 JScrollPane,在 JScrollPane 中有一个名为“textArea”的 JTextArea。这个 JTextArea 占据了整个 JFrame 而 JFrame 是未修饰的。因此,为了提供一些视角,JFrame 通常看起来像这样......

example image

当鼠标在 JTextArea 中单击并移动时,整个窗口都会被拖动。一切都设置为无法聚焦这项工作,它意味着是一个覆盖。

问题:

上面列出的代码工作正常,世界和平了。但是一旦有足够的文本让垂直滚动条出现(没有水平滚动条因为换行),拖动窗口就成了问题。当您单击并开始移动时,JFrame 会立即在屏幕上移动得更高。 JTextArea 中的线条,当您尝试移动它时,它会向上移动得越高。我假设 get*OnScreen() 方法有问题,因为它都与 JTextArea 相关。

问题类:

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Main extends JFrame {
    private JTextArea textArea;
    private JScrollPane textAreaScroll;
    private int posX = 0;
    private int posY = 0;

public Main() {
    initComponents();
    initListeners();
    for(int i = 0; i < 20; i++){
        addLine(i+" Hello");            
    }
}

public void addLine(String line){
    textArea.append("\n> "+line);
    textArea.setCaretPosition(textArea.getDocument().getLength());
}

private void initListeners(){
    textArea.addMouseListener(new MouseAdapter() {
        public void mousePressed(MouseEvent e) {
            posX = e.getX();
            posY = e.getY();
        }
    });
    textArea.addMouseMotionListener(new MouseAdapter() {
        public void mouseDragged(MouseEvent e) {
            setLocation(e.getXOnScreen() - posX, e.getYOnScreen() - posY);
        }
    });
}

private void initComponents() {
    try {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    } catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {}

    textAreaScroll = new JScrollPane();
    textArea = new JTextArea();

    setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
    setUndecorated(true);
    setAlwaysOnTop(true);
    setAutoRequestFocus(false);
    setBackground(new Color(130,210,255,130));
    setFocusCycleRoot(false);
    setFocusable(false);
    setFocusableWindowState(false);
    setName("main");
    setOpacity(0.4f);
    setResizable(false);

    textAreaScroll.setBorder(null);
    textAreaScroll.setFocusable(false);
    textAreaScroll.setRequestFocusEnabled(false);

    textArea.setEditable(false);
    textArea.setBackground(new Color(0, 0, 0));
    textArea.setColumns(20);
    textArea.setFont(new Font("Consolas", 0, 14));
    textArea.setForeground(new Color(255, 255, 255));
    textArea.setLineWrap(true);
    textArea.setRows(5);
    textArea.setText("> Hello world!\n> another line!");
    textArea.setBorder(null);
    textArea.setFocusable(false);
    textArea.setRequestFocusEnabled(false);
    textAreaScroll.setViewportView(textArea);

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
    getContentPane().setLayout(layout);
    layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(textAreaScroll, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
            );
    layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(textAreaScroll, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 214, Short.MAX_VALUE)
            );

    pack();
}

public static void main(String args[]) {
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            new Main().setVisible(true);
        }
    });
}

最佳答案

您的诊断绝对正确:

When you click and just begin to move, the JFrame instantly moves much higher on the screen. The lines in JTextArea, the higher it moves up when you try to move it. I assume that the get*OnScreen() methods are issue because it's all relevant to the JTextArea.

所以要解决这个问题使用GlassPane JFrame 附加 MouseXXXListener 从而我们可以在拖动时获得正确的坐标,此解决方案的主要问题是 glasspane 将消耗事件用于 JFrame 上的其他组件,这可以通过适本地重新调度 MouseEvent 来克服):

  • 创建 JPanel(此 glassPane/JPanel 将通过 setOpaque(false) 透明) , 在此处附上 xxxAdapters

  • 创建自定义监听器类以将 MouseEvent 重新分配给必要的组件(因为 glasspane 会将所有事件消耗到 JTextArea/JScollPane)

  • 通过 JFrame#setGlassPane(..)JPanel 设置为 JFrame 的 GlassPane。

  • set JFrame visible 而不是 set glassPane visible via setVisible(true)(这在一段时间内是 Swing 故障,如果您在框架可见之前将其设置为可见,它不会显示)。

这是你的固定代码:

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.MouseInputAdapter;

public class Main extends JFrame {

    private JTextArea textArea;
    private JScrollPane textAreaScroll;
    private JPanel glassPane;//create variable for glasspane

    public Main() {
        initComponents();
        initListeners();
        for (int i = 0; i < 20; i++) {
            addLine(i + " Hello");
        }
    }

    public void addLine(String line) {
        textArea.append("\n> " + line);
        textArea.setCaretPosition(textArea.getDocument().getLength());
    }

    private void initListeners() {
        GlassPaneListener gpl = new GlassPaneListener(textAreaScroll.getVerticalScrollBar(), this);
        //add the adapters/listeners to the glasspane
        glassPane.addMouseMotionListener(gpl);
        glassPane.addMouseListener(gpl);
    }

    private void initComponents() {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {
        }

        textAreaScroll = new JScrollPane();
        textArea = new JTextArea();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setUndecorated(true);
        setAlwaysOnTop(true);
        setAutoRequestFocus(false);
        setBackground(new Color(130, 210, 255, 130));
        setFocusCycleRoot(false);
        setFocusable(false);
        setFocusableWindowState(false);
        setName("main");
        setOpacity(0.4f);
        setResizable(false);

        textAreaScroll.setBorder(null);
        textAreaScroll.setFocusable(false);
        textAreaScroll.setRequestFocusEnabled(false);

        textArea.setEditable(false);
        textArea.setBackground(new Color(0, 0, 0));
        textArea.setColumns(20);
        textArea.setFont(new Font("Consolas", 0, 14));
        textArea.setForeground(new Color(255, 255, 255));
        textArea.setLineWrap(true);
        textArea.setRows(5);
        textArea.setText("> Hello world!\n> another line!");
        textArea.setBorder(null);
        textArea.setFocusable(false);
        textArea.setRequestFocusEnabled(false);
        textAreaScroll.setViewportView(textArea);
        textAreaScroll.setPreferredSize(new Dimension(200, 200));

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(textAreaScroll, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE));
        layout.setVerticalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(textAreaScroll, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 214, Short.MAX_VALUE));

        //create and make glasspane not opaque
        glassPane = new JPanel();
        glassPane.setOpaque(false);

        //set glasspane as JFrame glassPane
        setGlassPane(glassPane);

        pack();

        setVisible(true);//set JFrame visible

        //glassPane can only be setVisible after JFrame is visible
        glassPane.setVisible(true);
    }

    public static void main(String args[]) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Main();
            }
        });
    }
}

class GlassPaneListener extends MouseInputAdapter {

    private int posX = 0;
    private int posY = 0;
    Toolkit toolkit;
    private final Container contentPane;
    private final Component textAreaScroll;
    private final Component glassPane;
    private final JFrame frame;
    private boolean wasClickOnInterestedComponent = false;

    public GlassPaneListener(Component textAreaScroll, JFrame frame) {
        toolkit = Toolkit.getDefaultToolkit();
        this.textAreaScroll = textAreaScroll;
        this.frame = frame;
        this.glassPane = frame.getGlassPane();
        this.contentPane = frame.getContentPane();
    }

    @Override
    public void mouseDragged(MouseEvent e) {
        if (!redispatchMouseEvent(e)) {
            frame.setLocation(e.getXOnScreen() - posX, e.getYOnScreen() - posY);
        }
    }

    @Override
    public void mousePressed(MouseEvent e) {
        if (!redispatchMouseEvent(e)) {//check if event was redispatched if not its meant for us :)
            posX = e.getX();
            posY = e.getY();
        }
    }

    @Override
    public void mouseReleased(MouseEvent me) {
        wasClickOnInterestedComponent = false;
    }

    private boolean redispatchMouseEvent(MouseEvent e) {
        Point glassPanePoint = e.getPoint();
        Container container = contentPane;
        Point containerPoint = SwingUtilities.convertPoint(glassPane, glassPanePoint, contentPane);

        // The mouse event is probably over the content pane.
        // Find out exactly which component it's over.
        Component component = SwingUtilities.getDeepestComponentAt(container, containerPoint.x,
                containerPoint.y);

        if ((component != null) && (component.equals(textAreaScroll)) || wasClickOnInterestedComponent) {
            wasClickOnInterestedComponent = true;//so that if we drag iur cursor off JScrollBar tghe window wont be moved
            // Forward events over the scrollbar
            Point componentPoint = SwingUtilities.convertPoint(glassPane, glassPanePoint, component);
            component.dispatchEvent(new MouseEvent(component, e.getID(), e.getWhen(), e.getModifiers(),
                    componentPoint.x, componentPoint.y, e.getClickCount(), e.isPopupTrigger()));
            return true;//the event was redispatched
        } else {
            return false;//event was not redispatched
        }
    }
}

关于java - 拖动文本区域,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13785490/

相关文章:

java - NamedJDBCTemplate Parameters 是列表列表

java - (业余程序员)自定义HashMap大小总是比预期小1?

java GUI布局建议

java - 根据 JPanel 内部的组件调整其大小

java - 当前位置的android google maps问题

java - 如何在开始 Activity 后获取新图像(来自数组)?

Java Swing-创建一个函数来向 JScrollPane 添加元素

c# - 无边框的可拖动 WPF 窗口

jquery - float 可拖动框在调整大小时消失

Android:使用 JW Player SDK 实现 float 视频播放器(如 YouTube android 应用程序)