Swing:创建一个显示其组件居中的JScrollPane?

如果创建一个视口大于JScrollPane组件的JScrollPane,它会在左上角显示该组件。

有没有办法改变这种行为,所以它显示组件居中?

以下示例程序。


澄清

我有一个组件有(宽度,高度)=(cw,ch)。

我有一个JScrollPane,其视口有(宽度,高度)=(vw,vh)。

组件可能变得更大或更小。 我想要一种方法来使用滚动条相对于视口的中心点定位组件的中心点,因此如果组件的一个或两个维度小于视口,则组件将显示在视口的中心。

默认的滚动窗格行为将组件的左上角相对于视口的左上角定位。

我要问的是如何改变参考点。 我不确定这对于默认的JScrollPane有多容易,所以如果它不容易,那么我将学习我能做什么,并想到一个不同的方法。


package com.example.test.gui; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.geom.AffineTransform; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSpinner; import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; public class SimpleScroller extends JFrame { static class Thingy extends JPanel { private double size = 20.0; @Override public Dimension getPreferredSize() { int isize = (int) this.size; return new Dimension(isize, isize); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); int[] x = {0, 100, 100, 0, 0, 75, 75, 25, 25, 50}; int[] y = {0, 0, 100, 100, 25, 25, 75, 75, 50, 50}; Graphics2D g2d = (Graphics2D) g; AffineTransform at0 = g2d.getTransform(); g2d.scale(size/100, size/100); g.drawPolyline(x, y, x.length); g2d.setTransform(at0); } public void setThingySize(double size) { this.size = size; revalidate(); repaint(); } public double getThingySize() { return this.size; } } public SimpleScroller(String title) { super(title); final Thingy thingy = new Thingy(); setLayout(new BorderLayout()); add(new JScrollPane(thingy), BorderLayout.CENTER); final SpinnerNumberModel spmodel = new SpinnerNumberModel(thingy.getThingySize(), 10.0, 2000.0, 10.0); spmodel.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { thingy.setThingySize((Double) spmodel.getNumber()); } }); add(new JSpinner(spmodel), BorderLayout.NORTH); } public static void main(String[] args) { new SimpleScroller("simple scroller").start(); } private void start() { setDefaultCloseOperation(EXIT_ON_CLOSE); pack(); setVisible(true); } } 

  1. JPanel放入滚动窗格
  2. 将面板的布局设置为GridBagLayout
  3. 将一个组件放入面板中,没有约束。 它会集中。

这是嵌套布局示例中使用的技术,它将红色/橙色图像放置在父级的中心。

我只是在JScrollPane添加了一个JPanel ,我添加了THINGY JPanel 。 希望这是你想要的:

 import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.geom.AffineTransform; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSpinner; import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; public class SimpleScroller extends JFrame { static class Thingy extends JPanel { private double size = 20.0; @Override public Dimension getPreferredSize() { int isize = (int) this.size; return new Dimension(isize, isize); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); int[] x = {0, 100, 100, 0, 0, 75, 75, 25, 25, 50}; int[] y = {0, 0, 100, 100, 25, 25, 75, 75, 50, 50}; Graphics2D g2d = (Graphics2D) g; AffineTransform at0 = g2d.getTransform(); g2d.scale(size/100, size/100); g.drawPolyline(x, y, x.length); g2d.setTransform(at0); } public void setThingySize(double size) { this.size = size; revalidate(); repaint(); } public double getThingySize() { return this.size; } } public SimpleScroller(String title) { super(title); final Thingy thingy = new Thingy(); setLayout(new BorderLayout()); JPanel panel = new JPanel(); panel.add(thingy); JScrollPane scroll = new JScrollPane(); scroll.setViewportView(panel); add(scroll, BorderLayout.CENTER); final SpinnerNumberModel spmodel = new SpinnerNumberModel(thingy.getThingySize(), 10.0, 2000.0, 10.0); spmodel.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { thingy.setThingySize((Double) spmodel.getNumber()); } }); add(new JSpinner(spmodel), BorderLayout.NORTH); } public static void main(String[] args) { new SimpleScroller("simple scroller").start(); } private void start() { setDefaultCloseOperation(EXIT_ON_CLOSE); pack(); setVisible(true); } } 

这是输出:

简单的滚动器

接受你的两个评论,但是

是的…虽然我真的想随心所欲地定位它

  • 提取或创建自己的JViewport

避免JViewport上闪烁或跳跃的重要方法

JViewport.setScrollMode(JViewport.BLIT_SCROLL_MODE); JViewport.setScrollMode(JViewport.BACKINGSTORE_SCROLL_MODE); JViewport.setScrollMode(JViewport.SIMPLE_SCROLL_MODE);

  • 使用GlassPane (设置为所需的Point或/和Dimmension非常简单)

我不想绘制到JViewport,至少不是直接绘制 – 我的真实代码是JPanel的子组件,如我的例子中所示,但它也有其他GUI元素。

  • 您可以使用JXLayer(Java6)或JXLayer的部分方法直接实现到JLayer(Java7)

  • 可能太难了,但在所有情况下你都可以用JLabel覆盖任何东西

注意

我的所有建议都默认使用MouseEvent,J(X)Layer和JLabel也使用KeyEvents,但是从—>到另一个JComponent的redispatch事件没有问题

编辑

等等:不,我不想在JScrollPane的中心画出螺旋(你称之为“蛇”); 我想绘制我的组件并让我的组件显示在JScrollPane的中心。

  • 默认情况下,您的JPanel可能比JViewport更宽

a)JPanel返回Dimension

b)JViewport返回Dimension

  • 然后没有任何问题,闪烁或冻结

a)你可以将Dimmension从JPanel(Point)居中到JViewport(Point)的中心

b)如果有JComponent,你可以将JViewport移动到任何getBounds(),从JScrollPane中的JPanel返回JComponent或Point

根据Andrew的建议,您不能让视图小于视口(并且仍然居中)。

最干净的方法是直接从布局管理器执行,覆盖ViewportLayout。 源代码中有一个实现:

http://www.randelshofer.ch/multishow

寻找ZoomableViewportLayout类,它完美地完成了这项工作。