【问题标题】:Swing: creating a JScrollPane that displays its component centered?Swing:创建一个 JScrollPane 显示其组件居中?
【发布时间】:2012-05-16 04:51:20
【问题描述】:

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

有什么办法可以改变这种行为,让它显示组件居中?

下面的示例程序。


澄清

我有一个 (width, height) = (cw,ch) 的组件。

我有一个带有 (width, height) = (vw, vh) 的视口的 JScrollPane。

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

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

我要问的是如何更改参考点。我不确定使用默认的 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);
    }
}

【问题讨论】:

  • 请务必张贴代码,用它来拉扯头发:-)。感谢上帝,因为“我没有拔头发(还)”,示例会很好,可以试一试,将不胜感激:-)
  • 我还没有扯头发,你也不想让我发布我拥有的代码,但我可以举个例子。
  • 你想在 JScrollPane 的中心画这条蛇
  • 是的...虽然我很想知道如何随意放置它。
  • 等等:不,我不想在 JScrollPane 的中心画螺旋线(你称之为“蛇”);我想绘制我的组件并将我的组件显示在 JScrollPane 的中心。

标签: java swing jscrollpane


【解决方案1】:
  1. JPanel 放入滚动窗格
  2. 将面板的布局设置为GridBagLayout
  3. 将一个组件无限制地放入面板中。它将居中。

这是Nested Layout Example 中使用的技术,它将红色/橙色图像置于父级的中心。

【讨论】:

  • 我认为 OP 想要在 JViewport 上绘制一些东西,并可能将其设置为所需的点
  • 我不想画到 JViewport,至少不是直接画——我的真实代码是从 JPanel 子类化的组件,就像在我的示例中一样,但它还包含其他 GUI 元素。
  • @Andrew Thompson (@Jason S) 是常见的 OP,问题太宽泛 :-)
  • 我不知道你刚才说了什么。
  • @Andrew -- 你没有错过这个问题的微妙之处,它是正确的,在我尝试之前它看起来太简单了,无法解决我的问题。
【解决方案2】:

我只是在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);
    }
}

这是输出:

【讨论】:

  • +1... 这正是我希望它在水平维度上工作的方式,而不是在垂直维度上。
【解决方案3】:

接受你的两个 cmets,但是

是的...虽然我真的很想随意放置它

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

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

  • 使用GlassPane(非常简单地将位置设置为所需的点或/和尺寸)

我不想画到 JViewport,至少不想直接画——我的真实代码是一个从 JPanel 子类化的组件,就像在我的示例中一样,但它还包含其他 GUI 元素。

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

  • 可能太难了,但在所有情况下,您都可以使用 JLabel 覆盖任何内容

通知

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

编辑

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

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

a) JPanel 返回维度

b) JViewport 返回维度

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

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

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

【讨论】:

    【解决方案4】:

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

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

    http://www.randelshofer.ch/multishow

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-04
      • 2010-10-26
      • 2012-07-29
      • 2012-01-19
      • 2011-07-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多