java - 暂时禁用或防止在使用 mouseDrag 滚动时重新绘制 JViewPort

标签 java swing mouselistener java-5 jviewport

我编写了一个如下定义的 MouseListener ,以便我可以移动 JButton 来重新排序 JPanel 内的组件。 JPanel 位于 JScrollPane 内,以便在添加多个组件时可以滚动它们。

我遇到的问题是,当拖动组件并且鼠标移出滚动 Pane /视口(viewport)时,组件将弹回其在 JPanel 中的位置,然后将绘制在正确的位置。我认为此行为是由于当我调用 scrollRectToVisible()

时视口(viewport)调用其子项的重绘所致

有什么办法可以防止这种情况发生吗?

请注意,我仅限于 Java 5

监听器

import java.awt.Component;
import java.awt.Container;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;
import javax.swing.event.MouseInputAdapter;

public class DragListener extends MouseInputAdapter
{
    private Point location;
    private MouseEvent pressed;
    private MouseEvent dragged;
    private MouseEvent dropped;

    @Override
    public void mousePressed(MouseEvent me)
    {
        pressed = me;
    }

    @Override
    public void mouseDragged(MouseEvent me)
    {
        dragged = me;
        Component component = dragged.getComponent();
        Container parent = component.getParent();
        Container superParent = parent.getParent();

        if(superParent instanceof JViewport)
        {
            JViewport vp = (JViewport)superParent;
            Rectangle vpb = vp.getBounds();
            Point pt = MouseInfo.getPointerInfo().getLocation();
            SwingUtilities.convertPointFromScreen(pt, vp);

            if(!vpb.contains(pt))
            {
                int yDiff = (pt.y < vpb.y ) ? pt.y : pt.y - vpb.height;
                vpb.translate(0, yDiff);
                vp.scrollRectToVisible(vpb);
            }
        }

        location = component.getLocation(location);
        int x = location.x - pressed.getX() + me.getX();
        int y = location.y - pressed.getY() + me.getY();
        component.setLocation(x, y);
    }

    // Mouse release omitted
}

Gui(在 NetBeans 中创建)

import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;
import javax.swing.JButton;
import javax.swing.JPanel;

public class DragginTest extends javax.swing.JFrame
{
    public DragginTest()
    {
        initComponents();
        addListeners(jButton1, jButton2, jButton3, jButton4, jButton5, jButton6, jButton7, jButton8, jButton9);
    }

    private void addListeners(JButton... buttons)
    {
        DragListener drag = new DragListener();
        for(JButton b : buttons)
        {
            b.addMouseListener(drag);
            b.addMouseMotionListener(drag); 
        }   
    }

    @SuppressWarnings("unchecked")

    private void initComponents()
    {
        jLayeredPane1 = new javax.swing.JLayeredPane();
        jScrollPane1 = new javax.swing.JScrollPane();
        mainPanel = new javax.swing.JPanel();
        jButton1 = new javax.swing.JButton();
        jButton2 = new javax.swing.JButton();
        jButton3 = new javax.swing.JButton();
        jButton4 = new javax.swing.JButton();
        jButton5 = new javax.swing.JButton();
        jButton6 = new javax.swing.JButton();
        jButton7 = new javax.swing.JButton();
        jButton8 = new javax.swing.JButton();
        jButton9 = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setPreferredSize(new java.awt.Dimension(450, 450));

        mainPanel.setLayout(new java.awt.GridLayout(5, 2, 2, 2));

        // Below Repeated for buttons 1-9 (left out for conciseness)
        jButton1.setFont(new java.awt.Font("Tahoma", 1, 48)); // NOI18N
        jButton1.setForeground(new java.awt.Color(255, 0, 0));
        jButton1.setText("1");
        mainPanel.add(jButton1);
        // End Repeat

        jScrollPane1.setViewportView(mainPanel);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
            .addGap(40, 40, 40)
            .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 205, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addGap(38, 38, 38))
        );
        layout.setVerticalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(layout.createSequentialGroup()
            .addGap(40, 40, 40)
            .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 226, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addContainerGap(53, Short.MAX_VALUE))
        );

        pack();
    }

    public static void main(String args[])
    {
        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                new DragginTest().setVisible(true);
            }
        });
    }

    private javax.swing.JButton jButton1;
    private javax.swing.JButton jButton2;
    private javax.swing.JButton jButton3;
    private javax.swing.JButton jButton4;
    private javax.swing.JButton jButton5;
    private javax.swing.JButton jButton6;
    private javax.swing.JButton jButton7;
    private javax.swing.JButton jButton8;
    private javax.swing.JButton jButton9;
    private javax.swing.JLayeredPane jLayeredPane1;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JPanel mainPanel;
}

最佳答案

我在您的 DragListener 代码中添加了一个 hack。基本上,它会在拖动时删除布局管理器,因此重新验证不会执行任何操作,并且在释放鼠标时会恢复布局管理器:

import java.awt.*;
import java.awt.Container;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;
import javax.swing.event.MouseInputAdapter;

public class DragListener extends MouseInputAdapter
{
    private Point location;
    private MouseEvent pressed;
    private MouseEvent dragged;
    private MouseEvent dropped;
    private LayoutManager layout;

    @Override
    public void mousePressed(MouseEvent me)
    {
        pressed = me;
        Component component = me.getComponent();
        Container parent = component.getParent();
        parent.setPreferredSize(parent.getPreferredSize());
        layout = parent.getLayout();
        parent.setLayout(null);
    }

    @Override
    public void mouseDragged(MouseEvent me)
    {
        dragged = me;
        Component component = dragged.getComponent();
        Container parent = component.getParent();
        Container superParent = parent.getParent();

        if(superParent instanceof JViewport)
        {
            JViewport vp = (JViewport)superParent;
            Rectangle vpb = vp.getBounds();
            Point pt = MouseInfo.getPointerInfo().getLocation();
            SwingUtilities.convertPointFromScreen(pt, vp);

            if(!vpb.contains(pt))
            {
                int yDiff = (pt.y < vpb.y ) ? pt.y : pt.y - vpb.height;
                vpb.translate(0, yDiff);
                vp.scrollRectToVisible(vpb);
            }
        }

        location = component.getLocation(location);
        int x = location.x - pressed.getX() + me.getX();
        int y = location.y - pressed.getY() + me.getY();
        component.setLocation(x, y);
    }

    // Mouse release omitted
    @Override
    public void mouseReleased(MouseEvent me)
    {
        Component component = me.getComponent();
        Container parent = component.getParent();
        parent.setPreferredSize( null );
        parent.setLayout(layout);
        parent.validate();
        parent.repaint();
    }
}

当然,我假设您真正的 mouseReleased 代码将具有将按钮插入容器中适当位置的逻辑,以便 GridLayout 可以维护其真实位置,否则组件将返回到其原始位置。

编辑:

这是一个在释放鼠标按钮时将按钮移动到新位置的版本。有点复杂,因为你需要担心 ZOrder。也就是把一个组件往下拉就可以了。但是,如果您尝试向上拖动组件,那么它会被绘制在其他按钮下方。暂时重置 ZOrder 可解决此问题。

天啊,代码开始变成一个大黑客了:) 临时空布局和临时 ZOrder。

无论如何,这是代码:

import java.awt.*;
import java.awt.Container;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import javax.swing.*;
import javax.swing.SwingUtilities;
import javax.swing.event.MouseInputAdapter;

public class DragListener extends MouseInputAdapter
{
    private Point location;
    private MouseEvent pressed;
    private MouseEvent dragged;
    private MouseEvent dropped;
    private LayoutManager layout;
    private Rectangle originalBounds;
    private int originalZOrder;

    @Override
    public void mousePressed(MouseEvent me)
    {
        pressed = me;
        Component component = me.getComponent();
        Container parent = component.getParent();
        originalBounds = component.getBounds();
        originalZOrder = parent.getComponentZOrder(component);
        parent.setPreferredSize(parent.getPreferredSize());
        layout = parent.getLayout();
        parent.setLayout(null);
        parent.setComponentZOrder(component, 0);
    }

    @Override
    public void mouseDragged(MouseEvent me)
    {
        JComponent source = (JComponent) me.getComponent();
        JComponent parent = (JComponent) source.getParent();

        Point p = me.getPoint();
        p = SwingUtilities.convertPoint(source, p, parent);

        Rectangle bounds = source.getBounds();
        bounds.setLocation(p);

        bounds.x -= pressed.getX();
        bounds.y -= pressed.getY();
        source.setLocation(0, bounds.y);
        parent.scrollRectToVisible(bounds);
    }

    @Override
    public void mouseReleased(MouseEvent me)
    {
        boolean moved = false;
        Component component = me.getComponent();
        Container parent = component.getParent();
        Point location = component.getLocation();

        if (location.y < 0)
        {
            parent.add(component, 0);
            moved = true;
        }
        else
        {
            for (int i = 0; i < parent.getComponentCount(); i++)
            {
                Component c = parent.getComponent(i);
                Rectangle bounds = c.getBounds();

                if (c == component)
                    bounds = originalBounds;

                //  Component is released in the space originally occupied
                //  by the component or over an existing component

                if (bounds.contains(0, location.y))
                {
                    if (c == component)
                    {
                        parent.setComponentZOrder(component, originalZOrder);
                    }
                    else
                    {
                        parent.add(component, i);
                    }

                    moved = true;
                    break;
                }
            }
        }

        //  Component is positioned below all components in the container

        if (!moved)
        {
            parent.add(component, parent.getComponentCount() - 1);
        }

        //  Restore layout manager

        parent.setPreferredSize( null );
        parent.setLayout(layout);
        parent.validate();
        parent.repaint();
        component.requestFocusInWindow();
    }

    private static void createAndShowGUI()
    {
        JPanel panel = new JPanel( new GridLayout(0, 1) );
        DragListener drag = new DragListener();

        for (int i = 0; i <10; i++)
        {
            JButton button = new JButton("" + i);
            button.setFont(new java.awt.Font("Tahoma", 1, 48));
            button.setForeground(new java.awt.Color(255, 0, 0));
            button.addMouseListener(drag);
            button.addMouseMotionListener(drag);
            panel.add( button );
        }

        JFrame frame = new JFrame("SSCCE");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add( new JScrollPane(panel) );
        frame.setLocationByPlatform( true );
        frame.setSize(200, 400);
        frame.setVisible( true );
    }

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

关于java - 暂时禁用或防止在使用 mouseDrag 滚动时重新绘制 JViewPort,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30429648/

相关文章:

java - 向 Jframe 添加控制台

java - 如何识别结果来自哪个索引?

java - 绑定(bind)数据触发更改后 Swing/JTable 不更新

java - 鼠标监听器不会引发错误,但不会触发(java)

java - 如何在 JPanel GridLayout 上实现鼠标进入和鼠标退出?

java - JSON-lib 转义/保留字符串

java - 在运行时更改屏幕方向,但禁用基于传感器的更改

java - 类转换异常 - 鼠标事件

java - "Exception in thread "main"java.lang.NoClassDefFoundError : javafx/embed/swing/JFXPanel"mean and how do I fix it? 是什么意思

java - 错误的鼠标点检测?