11

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

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

下面的示例程序。


澄清

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

我有一个带有(宽度,高度)=(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);
    }
}
4

4 回答 4

12
  1. 将 aJPanel放入滚动窗格
  2. 将面板的布局设置为GridBagLayout.
  3. 将一个组件无约束地放入面板中。它将居中。

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

于 2012-05-07T17:35:54.830 回答
4

我只是JPanelJScrollPane其中添加了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);
    }
}

这是输出:

简单的滚动条

于 2012-05-07T17:49:42.117 回答
3

接受你的两个评论,但是

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

避免在 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 返回 Dimension

b) JViewport 返回维度

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

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

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

于 2012-05-07T17:50:10.423 回答
3

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

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

http://www.randelshofer.ch/multishow

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

于 2013-06-18T10:37:46.753 回答