通过拖动鼠标滚动JScrollPane(Java swing)

我正在为我正在制作的游戏制作地图编辑器。 JScrollPane中有一个JPanel,用于显示要编辑的地图。 我想要做的是,当用户按住空格键并在JPanel中拖动鼠标时,JScrollPanel将随拖动一起滚动。 这是我到目前为止:

panelMapPanel.addMouseMotionListener(new MouseMotionListener(){ @Override public void mouseDragged(MouseEvent e) { //Gets difference in distance x and y from last time this listener was called int deltaX = mouseX - e.getX(); int deltaY = mouseY - e.getY(); mouseX = e.getX(); mouseY = e.getY(); if(spacePressed){ //Scroll the scrollpane according to the distance travelled scrollPane.getVerticalScrollBar().setValue(scrollPane.getVerticalScrollBar().getValue() + deltaY); scrollPane.getHorizontalScrollBar().setValue(scrollPane.getHorizontalScrollBar().getValue() + deltaX); } } }); 

目前它的工作原理但滚动并不顺畅。 一次移动鼠标很好但是做小拖动会使滚动条变得狂暴。

任何想法如何改善这个?

对于那些喜欢视觉帮助的人,这里是编辑:

地图编辑器

加法笔记(编辑):

  • 我试过scrollPane.getViewport().setViewPosition(new Point(scrollPane.getViewport().getViewPosition().x + deltaX, scrollPane.getViewport().getViewPosition().y + deltaY));
  • 当慢慢移动鼠标时拖动更加烦躁,而大动作则更加平滑
  • 我没有运气就尝试使用scrollRectToVisible

好吧,最后我说的更简单了,不过它会……

首先,不要JViewport ,而是直接在作为JScrollPane内容的组件上使用JComponent#scrollRectToVisibleMouseListener应该附加到该组件上。

以下示例仅计算用户单击的点与拖动量之间的差异。 然后它将此delta应用于JViewportviewRect并使用JComponent#scrollRectToVisible来更新可视区域,简单:)

在此处输入图像描述

 public class Test { public static void main(String[] args) { new Test(); } public Test() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { ex.printStackTrace(); } JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(new TestPane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class TestPane extends JPanel { private JLabel map; public TestPane() { setLayout(new BorderLayout()); try { map = new JLabel(new ImageIcon(ImageIO.read(new File("c:/treasuremap.jpg")))); map.setAutoscrolls(true); add(new JScrollPane(map)); MouseAdapter ma = new MouseAdapter() { private Point origin; @Override public void mousePressed(MouseEvent e) { origin = new Point(e.getPoint()); } @Override public void mouseReleased(MouseEvent e) { } @Override public void mouseDragged(MouseEvent e) { if (origin != null) { JViewport viewPort = (JViewport) SwingUtilities.getAncestorOfClass(JViewport.class, map); if (viewPort != null) { int deltaX = origin.x - e.getX(); int deltaY = origin.y - e.getY(); Rectangle view = viewPort.getViewRect(); view.x += deltaX; view.y += deltaY; map.scrollRectToVisible(view); } } } }; map.addMouseListener(ma); map.addMouseMotionListener(ma); } catch (IOException ex) { ex.printStackTrace(); } } @Override public Dimension getPreferredSize() { return new Dimension(200, 200); } } } 

我现在正在制作地图编辑器。 虽然这是一个非常详细的解决方案,但我已经让鼠标滚动才能在我的网站上顺利运行。

我为鼠标事件编写了两个自定义AWTEventListener,另一个用于鼠标移动事件。 我这样做是因为我的地图是一个自定义的JComponent,因此不会填满整个视口。 这意味着如果光标位于组件上方,则不会检测到滚动窗格鼠标事件。

对我来说这非常流畅,内容与鼠标光标完美锁定。

(我应该提到我使用鼠标滚轮点击而不是空格键,但它很容易改变)。

  Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() { public void eventDispatched(AWTEvent event) { if(event instanceof MouseEvent){ MouseEvent e = (MouseEvent)event; //Begin a scroll if mouse is clicked on our pane if(isMouseInMapPane()){ if(e.getID() == MouseEvent.MOUSE_PRESSED){ if(e.getButton() == MouseEvent.BUTTON2){ mouseWheelDown = true; currentX = MouseInfo.getPointerInfo().getLocation().x; currentY = MouseInfo.getPointerInfo().getLocation().y; } } } //Stop the scroll if mouse is released ANYWHERE if(e.getID() == MouseEvent.MOUSE_RELEASED){ if(e.getButton() == MouseEvent.BUTTON2){ mouseWheelDown = false; } } } } }, AWTEvent.MOUSE_EVENT_MASK); Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() { public void eventDispatched(AWTEvent event) { if(event instanceof MouseEvent){ MouseEvent e = (MouseEvent)event; //Update the scroll based on delta drag value if(e.getID() == MouseEvent.MOUSE_DRAGGED){ if(mouseWheelDown){ int newX = MouseInfo.getPointerInfo().getLocation().x; int newY = MouseInfo.getPointerInfo().getLocation().y; int scrollStepX = (currentX - newX); int scrollStepY = (currentY - newY); currentX = newX; currentY = newY; //mapScroll is the reference to JScrollPane int originalValX = mapScroll.getHorizontalScrollBar().getValue(); mapScroll.getHorizontalScrollBar().setValue(originalValX + scrollStepX); int originalValY = mapScroll.getVerticalScrollBar().getValue(); mapScroll.getVerticalScrollBar().setValue(originalValY + scrollStepY); } } } } }, AWTEvent.MOUSE_MOTION_EVENT_MASK); 

这是isMouseInPane方法:

  private boolean isMouseInMapPane(){ //Note: mapPane does not need to be your scroll pane. //it can be an encapsulating container as long as it is in //the same position and the same width/height as your scrollPane. //For me I used the JPanel containing my scroll pane. Rectangle paneBounds = mapPane.getBounds(); paneBounds.setLocation(mapPane.getLocationOnScreen()); boolean inside = paneBounds.contains(MouseInfo.getPointerInfo().getLocation()); return inside; } 

此代码可以放在您有权访问滚动窗格引用的任何位置,也可以创建自定义滚动窗格类并将其添加到那里。

我希望它有所帮助!