我在让MatteBorder使用从右向左的组件方向时遇到问题。下面的代码说明了问题。
我将一个JPanel放在另一个JPanel中。两个JPanel都有一个MatteBorder。内边框的厚度为10,10,10,10,而外边框的厚度为20,20,20,10(注意外边框是不对称的)。
如果JPanels的组件方向为LEFT_TO_RIGHT,那么一切看起来都不错;但如果方向为RIGHT_TO_LEFT,则边框重叠。
请考虑以下代码:
import java.awt.*;
import javax.swing.*;
public class Xyzzy extends JFrame{
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
Xyzzy frame = new Xyzzy();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout(FlowLayout.RIGHT, 0, 5));
frame.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
{
JPanel outer = new JPanel();
outer.setLayout(new FlowLayout(FlowLayout.RIGHT, 0, 5));
outer.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
outer.setBorder(BorderFactory.createMatteBorder(20, 20, 20, 10, Color.black));
JPanel inner = new JPanel();
inner.setLayout(new FlowLayout(FlowLayout.RIGHT, 0, 5));
inner.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
inner.setBorder(BorderFactory.createMatteBorder(10, 10, 10, 10, Color.red));
inner.add(Box.createRigidArea(new Dimension(10,10)));
outer.add(inner);
frame.add(outer);
}
{
JPanel outer = new JPanel();
outer.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 5));
outer.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
outer.setBorder(BorderFactory.createMatteBorder(20, 20, 20, 10, Color.blue));
JPanel inner = new JPanel();
inner.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 5));
inner.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
inner.setBorder(BorderFactory.createMatteBorder(10, 10, 10, 10, Color.yellow));
inner.add(Box.createRigidArea(new Dimension(10,10)));
outer.add(inner);
frame.add(outer);
}
frame.setSize(600, 600);
frame.setVisible(true);
}
});
}
}
代码的前半部分生成带有黑色和红色边框的RIGHT_TO_LEFT JPanels,后半部分生成带黄色和蓝色边框的LEFT_TO_RIGHT JPanels。如果您运行该程序,您将在蓝色边框内看到黄色边框,但红色边框与黑色边框重叠。
为什么?
答案 0 :(得分:1)
注意:这是不答案 - 只是带代码的扩展评论: - )
从OPs示例中删除一点,专注于FlowLayout-with-asym-border-in-RToL - 添加标签而不是刚性区域更好(对我来说:-)看看它在哪里位于。布局简直就是疯了......
public void run() {
MatteBorderCO frame = new MatteBorderCO();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
frame.add(createRToLPanel(50, 100, true));
frame.add(createRToLPanel(100, 100, true));
frame.add(createRToLPanel(100, 50, true));
frame.setSize(600, 600);
frame.setVisible(true);
}
private JPanel createRToLPanel(int leftInset, int rightInset, boolean matte) {
JPanel outer = new JPanel();
Border asymBorder = matte ?
BorderFactory.createMatteBorder(20, leftInset, 20, rightInset, Color.black) :
BorderFactory.createEmptyBorder(20, leftInset, 20, rightInset) ;
outer.setBorder(BorderFactory.createCompoundBorder(
asymBorder, BorderFactory.createLineBorder(Color.RED)
));
JPanel inner = new JPanel();
inner.setBackground(Color.YELLOW);
inner.setBorder(BorderFactory.createLineBorder(Color.BLUE));
inner.add(new JLabel("RToL"));
outer.add(inner);
outer.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
return outer;
}
修改强>
Culprit是flowLayout.moveComponents中位置的错误计算:
if (ltr) {
m.setLocation(x, cy);
} else { // RtoL orientation
// here's the error: location is adjusted relative to the target width
// without taking the insets into account ...
m.setLocation(target.width - x - m.width, cy);
}
修改2
删除了假定的"简单"修复 - 不够好; - )
编辑3
无法抗拒,这里有一个FixedFlowLayout(正式未经测试,只是在上面的示例中使用,看起来很好用!)
public static class FixedFlowLayout extends FlowLayout {
/**
* C&p mostly - RToL border fix implemented.
*/
protected int moveComponents(Container target, int x, int y, int width,
int height, int rowStart, int rowEnd, boolean ltr,
boolean useBaseline, int[] ascent, int[] descent) {
switch (getAlignment()) {
case LEFT:
x += ltr ? 0 : width;
break;
case CENTER:
x += width / 2;
break;
case RIGHT:
x += ltr ? width : 0;
break;
case LEADING:
break;
case TRAILING:
x += width;
break;
}
int maxAscent = 0;
int nonbaselineHeight = 0;
int baselineOffset = 0;
if (useBaseline) {
int maxDescent = 0;
for (int i = rowStart; i < rowEnd; i++) {
Component m = target.getComponent(i);
if (m.isVisible()) {
if (ascent[i] >= 0) {
maxAscent = Math.max(maxAscent, ascent[i]);
maxDescent = Math.max(maxDescent, descent[i]);
} else {
nonbaselineHeight = Math.max(m.getHeight(),
nonbaselineHeight);
}
}
}
height = Math.max(maxAscent + maxDescent, nonbaselineHeight);
baselineOffset = (height - maxAscent - maxDescent) / 2;
}
int right = target.getWidth() - target.getInsets().right - getHgap();
for (int i = rowStart; i < rowEnd; i++) {
Component m = target.getComponent(i);
if (m.isVisible()) {
int cy;
if (useBaseline && ascent[i] >= 0) {
cy = y + baselineOffset + maxAscent - ascent[i];
} else {
cy = y + (height - m.getHeight()) / 2;
}
if (ltr) {
m.setLocation(x, cy);
x += m.getWidth() + getHgap();
} else {
m.setLocation(right - m.getWidth(), cy);
right -= m.getWidth() + getHgap();
}
}
}
return height;
}
/**
* C&p, to be able to call the fixed moveComponent.
*/
@Override
public void layoutContainer(Container target) {
synchronized (target.getTreeLock()) {
Insets insets = target.getInsets();
int maxwidth = target.getWidth()
- (insets.left + insets.right + getHgap() * 2);
int nmembers = target.getComponentCount();
int x = 0, y = insets.top + getVgap();
int rowh = 0, start = 0;
boolean ltr = target.getComponentOrientation().isLeftToRight();
boolean useBaseline = getAlignOnBaseline();
int[] ascent = null;
int[] descent = null;
if (useBaseline) {
ascent = new int[nmembers];
descent = new int[nmembers];
}
for (int i = 0; i < nmembers; i++) {
Component m = target.getComponent(i);
if (m.isVisible()) {
Dimension d = m.getPreferredSize();
m.setSize(d.width, d.height);
if (useBaseline) {
int baseline = m.getBaseline(d.width, d.height);
if (baseline >= 0) {
ascent[i] = baseline;
descent[i] = d.height - baseline;
} else {
ascent[i] = -1;
}
}
if ((x == 0) || ((x + d.width) <= maxwidth)) {
if (x > 0) {
x += getHgap();
}
x += d.width;
rowh = Math.max(rowh, d.height);
} else {
rowh = moveComponents(target, insets.left + getHgap(),
y, maxwidth - x, rowh, start, i, ltr,
useBaseline, ascent, descent);
x = d.width;
y += getVgap() + rowh;
rowh = d.height;
start = i;
}
}
}
moveComponents(target, insets.left + getHgap(), y, maxwidth - x,
rowh, start, nmembers, ltr, useBaseline, ascent,
descent);
}
}
public FixedFlowLayout() {
}
public FixedFlowLayout(int align, int hgap, int vgap) {
super(align, hgap, vgap);
}
public FixedFlowLayout(int align) {
super(align);
}
}
答案 1 :(得分:0)
我想我找到了解决问题的方法。 (我的基础是kleopatra的修改代码。)
诀窍是在外部和内部JPanel之间插入一个中间JPanel。保持外部JPanel L-to-R并使中间和内部JPanels R-to-L。您还需要在外部面板的布局上指定零宽度间隙。
import java.awt.*;
import javax.swing.*;
import javax.swing.border.Border;
public class Xyzzy extends JFrame{
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
Xyzzy frame = new Xyzzy();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
frame.add(createRToLPanel(50, 100, true));
frame.add(createRToLPanel(100, 100, true));
frame.add(createRToLPanel(100, 50, true));
frame.setSize(600, 600);
frame.setVisible(true);
}
private JPanel createRToLPanel(int leftInset, int rightInset, boolean matte) {
JPanel outer = new JPanel();
Border asymBorder = matte ?
BorderFactory.createMatteBorder(20, leftInset, 20, rightInset, Color.black) :
BorderFactory.createEmptyBorder(20, leftInset, 20, rightInset) ;
outer.setBorder(BorderFactory.createCompoundBorder(
asymBorder, BorderFactory.createLineBorder(Color.RED)
));
JPanel intermediate = new JPanel();
outer.setLayout(new FlowLayout(FlowLayout.LEADING,0,0));
outer.add(intermediate);
JPanel inner = new JPanel();
inner.setBackground(Color.YELLOW);
inner.setBorder(BorderFactory.createLineBorder(Color.BLUE));
inner.add(new JLabel("RToL1"));
inner.add(new JLabel("RToL2"));
intermediate.add(inner);
intermediate.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
return outer;
}
});
}
}
在这段代码中,我将两个JLabel添加到内部JPanel,以证明方向确实是R-to-L。
傻,傻。但如果这真的 是一个错误,我可以用这种方式解决它。