2015-01-06 33 views
6

在Java中沿着y軸對齊某些字符與BoxLayout的中心似乎存在問題。我不知道是什麼原因造成的,&我已經創建了一個SSCCE來演示效果。在這個例子中,我只使用字符'a',我沿着每個JPanel的直接中間劃一條線來展示每個案例離中心有多遠。粗體文本似乎排列良好,但正常格式的&斜體都嚴重偏離中心,儘管使用了setAlignmentX & setHorizo​​ntalAlignment。理解這種效果的任何幫助表示讚賞。Y軸上的Java BoxLayout中的單個字符對齊偏離中心

在某種程度上問題出現在我的特定計算機上的Java問題的情況下,這是當我運行SSCCE時顯示在屏幕上的圖像,它將沿着y軸加載三個不同的JPanel與BoxLayouts放在一起中心的JLabel只有在每個「一」字: enter image description here

&這裏是爲SSCCE代碼:

import javax.swing.*; 
import java.awt.*; 
import javax.swing.border.*; 

public class AlignmentTest extends JPanel 
{ 
    public AlignmentTest(char label, int style) 
    { 
     JLabel l = new JLabel(Character.toString(label)); 
     setBorder(BorderFactory.createLineBorder(Color.BLACK,1)); 
     setBackground(Color.WHITE); 
     setLayout(new BoxLayout(this,BoxLayout.Y_AXIS)); 
     setPreferredSize(new Dimension(300,50)); 
     add(Box.createVerticalGlue()); 
     add(l); 
      l.setFont(l.getFont().deriveFont(style)); 
      l.setAlignmentX(CENTER_ALIGNMENT); 
      l.setHorizontalAlignment(JLabel.CENTER); 
     add(Box.createVerticalGlue()); 
    } 
    public static void main(String[] args) 
    { 
     JFrame f = new JFrame("Alignment Test"); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     f.setLayout(new GridLayout(1,0,5,5)); 
     f.add(new AlignmentTest('a',Font.PLAIN)); 
     f.add(new AlignmentTest('a',Font.BOLD)); 
     f.add(new AlignmentTest('a',Font.ITALIC)); 
     f.pack(); 
     f.setVisible(true); 
    } 
    public void paintComponent(Graphics g) 
    { 
     super.paintComponent(g); 
     g.drawLine(getWidth()/2,0,getWidth()/2,getHeight()); 
    } 
} 
+1

爲了清楚起見,用'style' [名稱](http://docs.oracle.com/javase/8/docs/api/constant-values.html#java。 awt.Font.BOLD)。 – trashgod

+0

好點,我沒有想到這一點。我已經將代碼更改爲現在使用'style'名稱。謝謝你的反饋。 –

回答

4

在Windows 7上沒有一個字的使用JDK7中心對齊。

我做了一些更改來顯示一個JTextField,我玩JTextField的列(1,3,5)。隨着專欄的增加,中心對齊情況得到改善,並且在第5欄及以上的欄位合理。 所以這個問題在某種程度上與組件的寬度有關。

我猜想佈局中有一些奇怪的舍入誤差。這對我來說似乎是一個錯誤。

如果您對提供與BoxLayout類似功能的佈局感興趣,可以查看Relative Layout。您的示例中的改變是次要的:

import javax.swing.*; 
import java.awt.*; 
import javax.swing.border.*; 

public class AlignmentTest extends JPanel 
{ 
    public AlignmentTest(char label, int style) 
    { 
     JLabel l = new JLabel(Character.toString(label)); 
     setBorder(BorderFactory.createLineBorder(Color.BLACK,1)); 
     setBackground(Color.WHITE); 
//  setLayout(new BoxLayout(this,BoxLayout.Y_AXIS)); 
     setLayout(new RelativeLayout(RelativeLayout.Y_AXIS)); 
     setPreferredSize(new Dimension(300,50)); 
//  add(Box.createVerticalGlue()); 
     add(Box.createVerticalGlue(), new Float(1)); 
     add(l); 
      l.setFont(l.getFont().deriveFont(style)); 
      l.setAlignmentX(CENTER_ALIGNMENT); 
      l.setHorizontalAlignment(JLabel.CENTER); 
//  add(Box.createVerticalGlue()); 
     add(Box.createVerticalGlue(), new Float(1)); 
    } 
    public static void main(String[] args) 
    { 
     JFrame f = new JFrame("Alignment Test"); 
     JScrollPane scroller = new JScrollPane(); 
      JPanel panel = new JPanel(new GridLayout(1,0,5,5)); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     f.setLayout(new GridLayout(1,0,5,5)); 
     f.add(new AlignmentTest('a',Font.PLAIN)); 
     f.add(new AlignmentTest('a',Font.BOLD)); 
     f.add(new AlignmentTest('a',Font.ITALIC)); 
     f.pack(); 
     f.setVisible(true); 
    } 
    public void paintComponent(Graphics g) 
    { 
     super.paintComponent(g); 
     g.drawLine(getWidth()/2,0,getWidth()/2,getHeight()); 
    } 
} 
+0

'RelativeLayout'絕對是一個適當的解決方法。我以前沒有聽說過,但這是我可以在這裏和未來的應用中使用的東西。雖然,我不得不上線並將「RelativeLayout」的源代碼複製到我的項目中以便使用它。我有Java版本1.7.0_25。我是否需要更新以便讓無需我手動複製源代碼的Java內置的RelativeLayout類? –

+0

@ScottHetrick,RelativeLayout只是我編寫的一些(不受支持的)代碼,用於提供鏈接中所述的功能。沒有jar文件或任何東西,所以是的,你使用它就像你自己寫的任何類。該類不依賴於版本。 – camickr

+0

哦,我陷入了困境。這很讓人佩服。感謝您的建議,我將來可能會使用它。 –

4

你觀察到的效果似乎是方式BoxLayout作品的假象。從How to Use BoxLayout: Box Layout Features插值,「當BoxLayout從左到右佈局組件時,...任何額外的空間都出現在容器的右側。」如果封裝容器的初始尺寸是標籤(固定)尺寸的一小部分,如下所示,異常情況很少;水平拉伸框架以查看其生長情況。一種解決方法是儘可能減小封裝容器的優選尺寸被人爲放大的程度。

image

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

public class AlignmentTest extends JPanel { 
    private final JLabel l; 
    public AlignmentTest(String label, int style) { 
     setBorder(BorderFactory.createLineBorder(Color.BLACK, 1)); 
     setBackground(Color.WHITE); 
     setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 
     l = new JLabel(label, JLabel.CENTER); 
     l.setFont(l.getFont().deriveFont(style)); 
     l.setAlignmentX(CENTER_ALIGNMENT); 
     l.setOpaque(true); 
     l.setBackground(Color.cyan); 
     add(Box.createVerticalGlue()); 
     add(l); 
     add(Box.createVerticalGlue()); 
    } 

    @Override 
    public Dimension getPreferredSize() { 
     int w = l.getPreferredSize().width; 
     int h = l.getPreferredSize().height; 
     return new Dimension(w * 3, h * 3); 
    } 

    public static void main(String[] args) { 
     JFrame f = new JFrame("Alignment Test"); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     f.setLayout(new GridLayout(1, 0, 5, 5)); 
     f.add(new AlignmentTest("aMa", Font.PLAIN)); 
     f.add(new AlignmentTest("aMa", Font.BOLD)); 
     f.add(new AlignmentTest("aMa", Font.ITALIC)); 
     f.pack(); 
     f.setVisible(true); 
    } 

    public void paintComponent(Graphics g) { 
     super.paintComponent(g); 
     g.drawLine(getWidth()/2, 0, getWidth()/2, getHeight()); 
    } 
} 
+0

這是一個可行的解決方法,但爲了完美「解決」問題,需要更直接的解決方案。 –

5

另一種方式,以避免「框佈局特點:...任何額外的空間出現在容器中的權利」,你需要重寫JLabel#getMinimumSize()方法返回相同的DimensionJLabel#getPreferredSize()

對不起,我誤解了。

正如@camickr已經表示,

我想有一個佈局一些奇怪的舍入誤差。這對我來說似乎是一個錯誤。

是非常正確的。

固定例如:

//MinimumSize checkbox 
//selected true: set min width = 100px 
//selected false: set min width = 7px(default "a" width) 
//Here's my attempt(I am running JDK 1.7.0_72 on Windows 7): 
import java.awt.*; 
import java.awt.event.*; 
import java.util.*; 
import javax.swing.*; 

public class AlignmentTest4 extends JPanel { 
    private static boolean FLAG = false; 
    @Override public void paintComponent(Graphics g) { 
    super.paintComponent(g); 
    g.drawLine(getWidth()/2, 0, getWidth()/2, getHeight()); 
    } 
    @Override public Dimension getPreferredSize() { 
    return new Dimension(300, 80); 
    } 
    public static JLabel makeLabel(String label, int style) { 
    JLabel l = new JLabel(label) { 
     @Override public Dimension getPreferredSize() { 
     return new Dimension(120, 30); 
     } 
     @Override public Dimension getMinimumSize() { 
     Dimension d = super.getMinimumSize(); 
     if (FLAG) { 
      d.width = 100; 
     } else { 
      d.width = 7; 
     } 
     return d; 
     //if (FLAG) { 
     // return this.getPreferredSize(); 
     //} else { 
     // return super.getMinimumSize(); 
     //} 
     } 
    }; 
    l.setOpaque(true); 
    l.setBackground(Color.ORANGE); 
    l.setFont(l.getFont().deriveFont(style)); 
    l.setAlignmentX(Component.CENTER_ALIGNMENT); 
    l.setAlignmentY(Component.CENTER_ALIGNMENT); 
    l.setVerticalAlignment(SwingConstants.CENTER); 
    l.setVerticalTextPosition(SwingConstants.CENTER); 
    l.setHorizontalAlignment(SwingConstants.CENTER); 
    l.setHorizontalTextPosition(SwingConstants.CENTER); 
    return l; 
    } 
    public static JComponent makePanel() { 
    JPanel p = new JPanel(new GridLayout(0, 1, 5, 5)); 

    JPanel p1 = new AlignmentTest4(); 
    p1.setBorder(BorderFactory.createTitledBorder("BoxLayout.X_AXIS")); 
    p1.setLayout(new BoxLayout(p1, BoxLayout.X_AXIS)); 
    p1.add(Box.createHorizontalGlue()); 
    p1.add(makeLabel("a", Font.PLAIN)); 
    p1.add(Box.createHorizontalGlue()); 

    JPanel p2 = new AlignmentTest4(); 
    p2.setBorder(BorderFactory.createTitledBorder("BoxLayout.Y_AXIS")); 
    p2.setLayout(new BoxLayout(p2, BoxLayout.Y_AXIS)); 
    p2.add(Box.createVerticalGlue()); 
    p2.add(makeLabel("a", Font.PLAIN)); 
    p2.add(Box.createVerticalGlue()); 

    for (JPanel c : Arrays.asList(p1, p2)) { 
     c.setBackground(Color.WHITE); 
     p.add(c); 
    } 
    return p; 
    } 
    public static JComponent makeUI() { 
    final JPanel p = new JPanel(new BorderLayout()); 
    p.add(makePanel()); 
    p.add(new JCheckBox(new AbstractAction("MinimumSize") { 
     @Override public void actionPerformed(ActionEvent e) { 
     FLAG = ((JCheckBox) e.getSource()).isSelected(); 
     SwingUtilities.updateComponentTreeUI(p); 
     } 
    }), BorderLayout.SOUTH); 
    return p; 
    } 
    public static void main(String[] args) { 
    EventQueue.invokeLater(new Runnable() { 
     @Override public void run() { 
     createAndShowGUI(); 
     } 
    }); 
    } 
    public static void createAndShowGUI() { 
    JFrame f = new JFrame("Alignment Test"); 
    f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 
    f.getContentPane().add(makeUI()); 
    f.pack(); 
    f.setLocationRelativeTo(null); 
    f.setVisible(true); 
    } 
} 
+0

非常有趣。這很好,但是它給我帶來了另一個問題:我不明白爲什麼Java使用'JPanel'的getMinimumSize()方法來弄清楚如何使'JLabel'開頭。它不應該使用'JPanel'的當前大小,而不是最小大小? –

+0

在這種情況下,'JPanel'的'getMinimumSize()'方法是無關緊要的。 「this」關鍵字引用匿名內部類('JLabel')本身。 '@Override public Dimension getMinimumSize(){System.out.println(this instanceof JLabel);/* ture * /返回this.getPreferredSize(); }' – aterai

+0

啊,好吧。我想在這種情況下,修改後的問題是:爲什麼使用'JLabel'的getMinimumSize()方法?爲了確定對中測量,我會想象使用當前尺寸,而不是最小尺寸。 –