BoxLayout里的组件高度被拉长

问题描述

为了实现聊天内容展示功能,可以考虑使用一个JPanel,使用BoxLayout布局,axis(轴)设置成Y_AXIS,就可以实现聊天内容从上到下的一个展示效果,每个聊天内容占一行。
但如果Panel本身高度较大,前期添加Component的时候,Component的高度会被拉伸到JPanel的剩余可用高度。
假设使用BoxLayout的JPanel高度为700,放置在一个JScrollPane中。

  • 新加一个高度为200的组件,会被拉长到700。
  • 新增一个高度为300的组件,第一个组件高度被压缩成350,第二个组件高度为350
  • 新增一个高度为200的组件,第一个组件高度被压缩成200,第二个组件高度被压缩成300,第三个组件高度为200 ,开始出现垂直滚动条。(此时开始所有组件显示高度都变正常)

原因分析

BoxLayout在排列组件的时候,会比较Container内的组件高度与Container自身的高度,如果Container自身的高度比组件的高度大,则会拉伸组件,否则会挤压组件。

// BoxLayout.class target为使用BoxLayout的Container
public void layoutContainer(Container target) {
	// 计算container内所有组件请求的宽度和高度(min, prefered, max)
	checkRequests();
	if (absoluteAxis == X_AXIS) {
	} else {
		// 使用垂直轴方向的布局时,走这个逻辑
		SizeRequirements.calculateAlignedPositions(alloc.width, xTotal, xChildren, xOffsets,xSpans, ltr);
		// 计算平铺方式的位置,计算出来的ySpans数据就是每个组件的展示高度
		SizeRequirements.calculateTiledPositions(alloc.height, yTotal, yChildren, yOffsets,ySpans);
	}
}

// SizeRequirements.class
public static void calculateTiledPositions(...) {
	// 计算container内所有组件请求的大小之和,这里传进来的是高度
	long min = 0;
    long pref = 0;
    long max = 0;
    for (int i = 0; i < children.length; i++) {
        min += children[i].minimum;
        pref += children[i].preferred;
        max += children[i].maximum;
    }
    // 如果container目前的高度大于所有子组件的prefered高度,这里就调用expandTile方法去拉伸组件的高度了
    if (allocated >= pref) {
       expandedTile(allocated, min, pref, max, children, offsets, spans, forward);
    } else {
       compressedTile(allocated, min, pref, max, children, offsets, spans, forward);
    }
}
private static void expandedTile(...) {
	float totalPlay = Math.min(allocated - pref, max - pref);
	// 得到乘积因子
    float factor = (max - pref == 0) ? 0.0f : totalPlay / (max - pref);
	for (int i = 0; i < spans.length; i++) {
		// 拉伸的距离为:乘积因子 * (组件的最大高度 - 组件的prefered)
		int play = (int)(factor * (req.maximum - req.preferred));
        spans[i] = (int) Math.min((long) req.preferred + (long) play, Integer.MAX_VALUE);
	}
}

可以看到,只要组件的最大高度大于当前组件的prefered,且container还有剩余空间时,就会被拉伸。

解决方案

临时方案
由于具体拉伸时,乘积因子用的是组件的max - prefered,只要我们重写要添加的组件的getMaximumSize方法,让它返回的高度跟prefered一致,就不会被拉伸。

public class XxxComponent {
	@Overide
	public Dimension getMaximumSize() {
		return new Dimension(Short.MAX_VALUE, getPreferredSize().height);
	}
}

推荐方案
修改BoxLayout的checkRequests方法,获取组件对应的SizeRequirements对象时,将maximum设置成prefered

void checkRequests() {
	xChildren = new SizeRequirements[n];
	yChildren = new SizeRequirements[n];
	for (int i = 0; i < n; i++) {
		...
		Dimension min = c.getMinimumSize();
        Dimension typ = c.getPreferredSize();
        Dimension max = c.getMaximumSize();
//		yChildren[i] = new SizeRequirements(min.height, typ.height, max.height, c.getAlignmentY());
        yChildren[i] = new SizeRequirements(min.height, typ.height, typ.height, c.getAlignmentY());
	}
}

由于BoxLayout中checkRequests方法修饰符为default,无法通过继承的方式重写改方法,只能新建一个类,复制BoxLayout的实现,并修改指定位置的代码。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值