包含HTML文本的JLabel使用可用空间自动包装行。如果将JLabel添加到JSrollPane,他必须将preferredSize设置为一个合适的值,否则它不会包装。所有这些都可以使用LayoutManager在JPanel内的其他组件上正常工作。
原因我需要一个可调整大小的应用程序窗口我扩展了JScrollPane以跟踪调整大小事件并动态更改同步到视口宽度的大小。基本上它可以工作,但有时布局管理器计算首选高度是错误的(值太大或太小)。例如,切割第一行的红色边框的可见性表明高度的计算是错误的。
我无法使用单个包装JLabel重现失败。
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class WrappedLabel implements Runnable {
public static void main( String[] args ){
SwingUtilities.invokeLater( new WrappedLabel() );
}
@Override
public void run(){
final JPanel panel = new JPanel( new GridBagLayout() );
final GridBagConstraints gc = new GridBagConstraints();
gc.fill = GridBagConstraints.BOTH;
gc.weightx = 1.0;
gc.weighty = 1.0;
{
gc.gridx = 0;
gc.gridy = 0;
final JLabel label = new JLabel(
"<html>" + "please add some more text here"
);
label.setBorder( BorderFactory.createLineBorder( Color.RED ) );
panel.add( label, gc );
}
{
gc.gridx = 0;
gc.gridy = 1;
final JLabel label = new JLabel(
"<html>" + "please add some more text here"
);
label.setBorder( BorderFactory.createLineBorder( Color.RED ) );
panel.add( label, gc );
}
final JFrame frame = new JFrame();
frame.add( new ScrollPane( panel ) );
frame.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
frame.setSize( 256, 256 );
frame.setVisible( true );
}
private class ScrollPane extends JScrollPane implements ComponentListener {
ScrollPane( Container view ){
super( view );
this.viewport.addComponentListener( this );
}
@Override
public void componentHidden( ComponentEvent ce ){
}
@Override
public void componentMoved( ComponentEvent ce ){
}
/** calculating required height is a 3 step process
* 1. sync width of client and viewport, set height of client to high value
* 2. let GridbagManager calculate required minimum size
* 3. set preferredSize and revalidate
**/
@Override
public void componentResized( ComponentEvent ce ){
assert( this.viewport == ce.getSource() );
final Container view = (Container) this.viewport.getView();
final int width = this.viewport.getExtentSize().width;
view.setPreferredSize( new Dimension( width, Integer.MAX_VALUE ) );
final int height = view.getLayout().preferredLayoutSize( view ).height;
view.setPreferredSize( new Dimension( width, height ) );
view.revalidate();
}
@Override
public void componentShown( ComponentEvent ce ){
}
}
}
答案 0 :(得分:1)
显然,这可能是GridBagLayout中的错误,或者您正在以开发人员完全出乎意料的方式使用布局引擎。几个带有HTML的多行标签,设置首选尺寸并立即通过后门询问首选尺寸?啊!
我注意到有时在减小窗口大小时布局工作不正确:滚动窗格内的面板不会减少,并且会出现水平滚动条。 (我顺便使用Windows)。
另外,有时候,如果垂直滚动条可见并且面板高度很大,然后我增加了窗口大小,则面板高度仍然过大,标签周围出现间隙:
对我来说,当我减少窗口时,每隔一段时间布局都是错误的;增加效果更好,但如果出错,每隔一段时间也不正确。我尝试调试和打印值到控制台;似乎view.getLayout().preferredLayoutSize( view )
不仅取决于view.setPreferredSize
,还取决于面板和滚动窗格的当前大小。 GridBagLayout的代码太复杂了,无法深入研究。
DIRTY HACK
由于每个其他调整大小都会产生正确的结果,为什么不调整大小两次呢?在ScrollPane.componentResized
处理程序中复制内容是不成功的,可能是因为ScrollPane的大小保持不变。 ScrollPane本身需要调整两次,具有不同的值。为了以最简单的方式测试它,我将JFrame子类化:它监听componentResized
并调整其子窗口的大小两次。第二次调整大小必须通过SwingUtilities.invokeLater
推迟。
替换
行final JFrame frame = new JFrame();
frame.add( scroll );
通过
final MyFrame frame = new MyFrame(scroll);
并添加以下类:
private class MyFrame extends JFrame implements ComponentListener {
private Component child;
public MyFrame(Component child){
this.child=child;
setLayout(null);
getContentPane().add(child);
addComponentListener(this);
}
public void componentResized(ComponentEvent e) {
Dimension size=getContentPane().getSize();
child.setSize(new Dimension(size.width-1,size.height));
validate();
SwingUtilities.invokeLater(new ResizeRunner(size));
}
public void componentMoved(ComponentEvent e) {}
public void componentShown(ComponentEvent e) {}
public void componentHidden(ComponentEvent e) {}
private class ResizeRunner implements Runnable {
private Dimension size;
public ResizeRunner(Dimension size){
this.size=size;
}
public void run() {
child.setSize(size);
validate();
}
}
}
通过子类化布局管理器可以实现同样的目的。
显然,这种方法不够优雅且效率低下,但作为JRE错误的解决方法,如果没有别的帮助......; - )