JPanel高度不会立即更新

时间:2013-12-04 18:41:34

标签: java swing

我正在使用以下代码将图像添加到JScrollPane内部的JPanel中。我希望滚动窗格滚动到底部,但它似乎没有意识到图像已经添加,直到一段时间后,即使我在SwingUtilities包装器中调用它。

private void addImage(final String image){
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            imagePanel.addImage(image);
            final JScrollBar vScroll = imageScrollPane.getVerticalScrollBar();
            vScroll.setValue(vScroll.getMaximum());
        }
    });
}

imagePanel中的addImage代码如下所示:

public void addImage(String image){
        System.out.println("Pre-height: " + getHeight());
        add(createImageLabel(image));
        System.out.println("After-height: " + getHeight());
        revalidate();
        System.out.println("Revalidate-height: " + getHeight());
    }

看起来这会产生以下结果,例如:

Pre-height: 690
After-height: 690
Revalidate-height: 690

如果我在通话结束后进入循环状态,则实际更改高度似乎需要大约200毫秒。

我在这里的代码总是在滚动时结束1张图片。滚动到底部的最佳方式是什么,以至于我实际知道底部在哪里?我对Swing约定只有一点熟悉,但我认为,因为一切都发生在SwingUtilities调用中,它会知道它在做什么。

完整可执行代码演示此问题的示例:

package com.foo.bar

import java.awt.Dimension;
import java.awt.Image;
import java.io.IOException;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;

import net.miginfocom.swing.MigLayout;

public class ScrollImageExampleFrame extends JFrame{

    private ScrollImageExamplePanel siePanel;

    public ScrollImageExampleFrame(){
        setSize(1024, 768);
        siePanel = new ScrollImageExamplePanel();
        getContentPane().add(siePanel);
    }

    class ScrollImageExamplePanel extends JPanel {
        ImageViewExamplePanel imagePanel = new ImageViewExamplePanel();
        JScrollPane imageScrollPane = new JScrollPane(imagePanel, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);

        public ScrollImageExamplePanel(){
            setLayout(new MigLayout("nogrid"));
            imageScrollPane.getVerticalScrollBar().setPreferredSize(new Dimension(30, 0));
            add(imageScrollPane, "east, width 300!, height 100%");
        }

        private void addImageOuter(final String image){
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    imagePanel.addImageInner(image);
                    imagePanel.revalidate();
                    final JScrollBar vScroll = imageScrollPane.getVerticalScrollBar();
                    vScroll.setValue(vScroll.getMaximum());
                }
            });
        }
    }

    class ImageViewExamplePanel extends JPanel {
        public ImageViewExamplePanel() {
            setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
        }

        public void addImageInner(String image){
            URL imageUrl = ImageViewExamplePanel.class.getResource("/com/foo/bar/" + image);
            ImageIcon ii = resizeImage(imageUrl, 360, 270);
            JLabel label = new JLabel();
            label.setIcon(ii);
            add(label);
            revalidate();
        }

        private ImageIcon resizeImage(URL url, int x, int y){
            Image image = null;
            try{
                image = ImageIO.read(url);
                Image newImage = image.getScaledInstance(x, y, Image.SCALE_SMOOTH);
                return new ImageIcon(newImage);
            } catch (IOException ioe){
                ioe.printStackTrace();
                return null;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ScrollImageExampleFrame frame = new ScrollImageExampleFrame();
        frame.setVisible(true);

        frame.siePanel.addImageOuter("foo.png");
        Thread.sleep(1000);
        frame.siePanel.addImageOuter("foo.png");
        Thread.sleep(1000);
        frame.siePanel.addImageOuter("foo.png");
        Thread.sleep(1000);
        frame.siePanel.addImageOuter("foo.png");        
    }

}

3 个答案:

答案 0 :(得分:1)

为此目的使用下一个技巧:

private void addImage(final String image){
    imagePanel.addImage(image);
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            final JScrollBar vScroll = imageScrollPane.getVerticalScrollBar();
            vScroll.setValue(vScroll.getMaximum());
        }
    });
}

在这种情况下,将添加,验证和绘制所有组件,然后在invokeLater中,您可以更改JScrollBar的位置。它可以帮助你。

答案 1 :(得分:0)

@ alex2410确定了一个解决方案 - 通过在包含的JFrame上调用revalidate(),我需要时正确计算了我的组件大小。我能够通过替换addImageOuter方法来修复我的示例代码,如下所示:

    private void addImageOuter(final String image){
        final JFrame frame = (JFrame) SwingUtilities.getWindowAncestor(this);
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                imagePanel.addImageInner(image);
                frame.revalidate();
                final JScrollBar vScroll = imageScrollPane.getVerticalScrollBar();
                vScroll.setValue(vScroll.getMaximum());
            }
        });
    }

答案 2 :(得分:-1)

也许试试这个......

imagePanel.updateUI();