预先计算屏幕外组件尺寸?

时间:2015-04-01 20:22:05

标签: java swing boxlayout

我有一个JScrollPane,其自定义Scrollable视图与其宽度相匹配,允许/仅需要垂直滚动。它装有不同数量的JLabel,由于它们的长度,它几乎总是自动换行。我正在使用BoxLayout来确保它们始终是全宽的,垂直布局,并且它大部分都有效。

主要设计要求包括准确了解这些JLabel中的每一个,以便滚动条可以通过编程方式移动到其中心。如果您使用过NetBeans,那么您应该熟悉这个想法;在Chrome中查找,虽然它没有跳到那里,但也指出了事情的位置。

我遇到的问题是,在某些时候,布局只是放弃了。您可以通过滚动条在滚动期间跳转的方式判断它尚未实际计算所有内容。因此,报告的getY值无用。

我添加了以下一些调试代码;

this.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() {
        public void adjustmentValueChanged(AdjustmentEvent evt) {
            //getVerseBlock grabs a component from the view, with sanity checks:
            System.out.println(ChapterTab.this.getVerseBlock(255).getY());
            System.out.println(ChapterTab.this.getVerseBlock(255).getHeight());
        }
    });

正如预期的那样 - 但不是所希望的 - 随着面板的滚动,最后一个(或任何其他)元素的Y位置继续增加,因为越来越多的JLabel正确地计算它们的大小。 getHeight()检查确实显示它们被设置在单行的高度,不知道它们需要自动换行,直到它们接近可见。

整个面板滚动完毕后,很高兴......暂时。当这些JLabel退回视野时,它不会计算出来。但是当调整窗口大小时, 也不会重新 - 计算,导致值再次变得无用。 (而且,顺便说一句,打破数学;组件太高高于渲染区域也会报告不良高度,可能会增加远大于其容器的新高度!)

因此,我非常简单地寻求确保此容器中的每个组件始终具有完全准确且当前的尺寸和位置值的方法。尽管这似乎只是事情应该运作的方式 - 如何不专业是一个紧张的滚动条? - 我没有找到任何有关实现这一目标的资源。

编辑:让我感到困扰的是,那些不知道答案的人认为这只是因为他们看不到代码。没关系!你不可能知道一切;如果您之前没有遇到并解决过这个问题,那么这并不会让您成为一名编码人员,而且我不希望您回答。但是,我已经设法将问题削减到一个非常基本的单一文件:

package jscrolltest;


import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;

public class JScrollTest {

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

    private static void createAndShowGUI() {

        JFrame frame = new JFrame("JScrollTest");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().setLayout(new BorderLayout());

        ScrollableView view = new ScrollableView();
        view.setLayout(new BoxLayout(view, BoxLayout.Y_AXIS));

        JScrollPane pnlScroll = new JScrollPane(view);
        pnlScroll.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        pnlScroll.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

        view.add(new JLabel("<html>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc convallis sapien sed tempor scelerisque."));
        view.add(new JLabel("<html>Donec ut leo nec ligula tempus eleifend. Proin in porttitor velit."));
        view.add(new JLabel("<html>Sed a facilisis orci. Nunc a diam feugiat, suscipit nunc nec, porttitor velit."));
        view.add(new JLabel("<html>Maecenas sagittis, est et bibendum luctus, tortor orci hendrerit enim, vitae rhoncus augue libero sit amet est. Phasellus a neque et magna gravida euismod dignissim ac elit."));
        view.add(new JLabel("<html>Ut neque urna, ultrices fermentum quam et, tristique tempus nulla. Curabitur non feugiat leo."));
        view.add(new JLabel("<html>Aenean eu viverra ligula, eu tempor turpis. Suspendisse eu nunc ac urna blandit egestas quis id augue. "));
        view.add(new JLabel("<html>Suspendisse a pulvinar est. Maecenas id congue neque. Donec eleifend nisi quis nisl faucibus sollicitudin."));
        view.add(new JLabel("<html>Aliquam rutrum nulla neque, sit amet sollicitudin massa ultrices quis. Interdum et malesuada fames ac ante ipsum primis in faucibus."));
        view.add(new JLabel("<html>Praesent ut auctor nisl, eget convallis neque. Quisque et vestibulum massa."));
        view.add(new JLabel("<html>Quisque consectetur ex cursus risus interdum, tristique imperdiet ante viverra. Sed et nulla eget sem dapibus fringilla."));

        frame.getContentPane().add(pnlScroll, BorderLayout.CENTER);

        pnlScroll.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() {
            public void adjustmentValueChanged(AdjustmentEvent evt) {
                System.out.println(view.getComponent(9).getY());
                System.out.println(view.getComponent(9).getHeight());
            }
        });

        frame.setSize(200, 100);
        frame.setVisible(true);
    }

    public static class ScrollableView extends JPanel implements Scrollable {

        public Dimension getPreferredScrollableViewportSize() {
            return getPreferredSize();
        }

        public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
            return 10;
        }

        public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
            return ((orientation == SwingConstants.VERTICAL) ? visibleRect.height : visibleRect.width) - 10;
        }

        public boolean getScrollableTracksViewportWidth() {
            return true;
        }

        public boolean getScrollableTracksViewportHeight() {
            return false;
        }
    }
}

正如你所看到的,没有什么特别的东西;字面意思是制作一个框架所需的最低限度,删除一个JScrollPane,使视图适合宽度,并通过BoxLayout使一些JLabel垂直排列。正如所描述的那样,没有特别的伎俩。

1 个答案:

答案 0 :(得分:1)

  

在这种情况下,屏幕外的JLabel要么没有宽度,要么没有给出实际导致自动换行的paint()调用(设计中已知的缺陷;它们不会运行这些计算)直到画,

你说这是一个设计问题所以我能想到的唯一解决方案就是充其量只是一个黑客。似乎适用于您的SSCCE:

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;

public class JScrollTest {

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

    private static void createAndShowGUI() {

        JFrame frame = new JFrame("JScrollTest");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().setLayout(new BorderLayout());

        final ScrollableView view = new ScrollableView();
        view.setLayout(new BoxLayout(view, BoxLayout.Y_AXIS));

        JScrollPane pnlScroll = new JScrollPane(view);
        pnlScroll.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        pnlScroll.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

        view.add(new JLabel("<html>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc convallis sapien sed tempor scelerisque."));
        view.add(new JLabel("<html>Donec ut leo nec ligula tempus eleifend. Proin in porttitor velit."));
        view.add(new JLabel("<html>Sed a facilisis orci. Nunc a diam feugiat, suscipit nunc nec, porttitor velit."));
        view.add(new JLabel("<html>Maecenas sagittis, est et bibendum luctus, tortor orci hendrerit enim, vitae rhoncus augue libero sit amet est. Phasellus a neque et magna gravida euismod dignissim ac elit."));
        view.add(new JLabel("<html>Ut neque urna, ultrices fermentum quam et, tristique tempus nulla. Curabitur non feugiat leo."));
        view.add(new JLabel("<html>Aenean eu viverra ligula, eu tempor turpis. Suspendisse eu nunc ac urna blandit egestas quis id augue. "));
        view.add(new JLabel("<html>Suspendisse a pulvinar est. Maecenas id congue neque. Donec eleifend nisi quis nisl faucibus sollicitudin."));
        view.add(new JLabel("<html>Sed a facilisis orci. Nunc a diam feugiat, suscipit nunc nec, porttitor velit."));
        view.add(new JLabel("<html>Maecenas sagittis, est et bibendum luctus, tortor orci hendrerit enim, vitae rhoncus augue libero sit amet est. Phasellus a neque et magna gravida euismod dignissim ac elit."));
        view.add(new JLabel("<html>Ut neque urna, ultrices fermentum quam et, tristique tempus nulla. Curabitur non feugiat leo."));
        view.add(new JLabel("<html>Aenean eu viverra ligula, eu tempor turpis. Suspendisse eu nunc ac urna blandit egestas quis id augue. "));
        view.add(new JLabel("<html>Suspendisse a pulvinar est. Maecenas id congue neque. Donec eleifend nisi quis nisl faucibus sollicitudin."));
        view.add(new JLabel("<html>Aliquam rutrum nulla neque, sit amet sollicitudin massa ultrices quis. Interdum et malesuada fames ac ante ipsum primis in faucibus."));
        view.add(new JLabel("<html>Praesent ut auctor nisl, eget convallis neque. Quisque et vestibulum massa."));
        view.add(new JLabel("<html>123Quisque consectetur ex cursus risus interdum, tristique imperdiet ante viverra. Sed et nulla eget sem dapibus fringilla."));

        frame.getContentPane().add(pnlScroll, BorderLayout.CENTER);

        pnlScroll.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() {
            public void adjustmentValueChanged(AdjustmentEvent evt) {
                System.out.println(view.getComponent(14).getY());
                System.out.println(view.getComponent(14).getHeight());
            }
        });

        frame.setSize(300, 200);
        frame.setVisible(true);
    }

    public static class ScrollableView extends JPanel implements Scrollable, ComponentListener {

        public ScrollableView()
        {
            addComponentListener( this );
        }

        public Dimension getPreferredScrollableViewportSize() {
            return getPreferredSize();
        }

        public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
            return 10;
        }

        public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
            return ((orientation == SwingConstants.VERTICAL) ? visibleRect.height : visibleRect.width) - 10;
        }

        public boolean getScrollableTracksViewportWidth() {
            return true;
        }

        public boolean getScrollableTracksViewportHeight() {
            return false;
        }

        //  Implement ComponentListener

        public void componentResized(ComponentEvent e)
        {
            SwingUtilities.invokeLater(new Runnable()
            {
                public void run()
                {
                    int width = getParent().getSize().width;

                    if (width == 0) return;

                    BufferedImage bi = new BufferedImage(width, 600, BufferedImage.TYPE_INT_RGB);
                    Graphics g = bi.getGraphics();

                    for (Component c: getComponents())
                    {
                        c.paint(g);
                    }

                    revalidate();
                    repaint();
                }
            });
        }

        public void componentHidden(ComponentEvent e) {}

        public void componentMoved(ComponentEvent e) {}

        public void componentShown(ComponentEvent e) {}

    }
}

不知道是否需要invokeLater(),revalidate()和repaint(),我只是通过它们。另外,我只是为BufferedImage选择了一个随机高度。据我所知,它甚至可以在1的高度工作。