2012-07-30 56 views
4

我有一個具有一些子元素(包括JTextField)的自定義swing組件。我想爲容器提供與JTextField相同的基線,所以我重寫了容器中的getBaseline方法。但是,當爲容器調用getBaseline時,並不總是設置JTextField的位置。我試圖在容器的getBaseline方法中添加對doLayout的調用,但這沒有幫助。我想是這樣的:在父容器中使用子組件的getBaseline(int w,int h)

public int getBaseline(int w, int h) { 
    Dimension size = textField.getPreferredSize(); 
    int textBaseline = textField.getBaseline(size.width, size.height); 
    int textY = textField.getY(); 
    return textY + textBaseline; 
} 

在上面的代碼textY處等於0

我已經寫了一些代碼來說明問題。點擊第一次「添加」,基準線是錯誤的。第二次是正確的。

import java.awt.Color; 
import java.awt.Component; 
import java.awt.Dimension; 
import java.awt.GridBagConstraints; 
import java.awt.GridBagLayout; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.util.ArrayList; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JTextField; 

class CustomComponent extends JPanel { 

    public CustomComponent() { 
     setLayout(new GridBagLayout()); 
     textField.setColumns(8); 
     layoutElements(); 
    } 

    public void addComponent() { 
     JPanel comp = new JPanel(); 
     comp.setPreferredSize(new Dimension(50, 200)); 
     comp.setBackground(Color.red); 
     otherComponents.add(comp); 
     layoutElements(); 
    } 

    public int getBaseline(int w, int h) { 
     Dimension size = textField.getPreferredSize(); 
     return textField.getY() + textField.getBaseline(size.width, size.height); 
    } 

    public Component.BaselineResizeBehavior getBaselineResizeBehavior() { 
     return Component.BaselineResizeBehavior.CONSTANT_DESCENT; 
    } 

    private void layoutElements() { 
     removeAll(); 
     GridBagConstraints constraints = new GridBagConstraints(); 
     constraints.anchor = GridBagConstraints.SOUTH; 
     add(textField, constraints); 

     for (JPanel comp : otherComponents) 
      add(comp, new GridBagConstraints()); 
     if (getParent() != null) 
      getParent().validate(); 
    } 

    private JTextField textField = new JTextField(); 
    private ArrayList<JPanel> otherComponents = new ArrayList<JPanel>(); 
} 

public class Main { 

    public static void main(String[] args) { 
     JFrame frame = new JFrame(); 
     JPanel panel = new JPanel(); 
     panel.setLayout(new GridBagLayout()); 
     frame.getContentPane().add(panel); 

     JButton addComponent = new JButton("Add"); 
     GridBagConstraints constraints = new GridBagConstraints(); 
     constraints.anchor = GridBagConstraints.BASELINE; 
     panel.add(addComponent, constraints); 

     final CustomComponent customComp = new CustomComponent(); 
     panel.add(customComp, constraints); 

     addComponent.addActionListener(new ActionListener() { 
      public void actionPerformed(ActionEvent ae) { 
       customComp.addComponent(); 
      } 
     }); 

     frame.setSize(400, 300); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.setVisible(true); 
    } 
} 
+0

也許我還應該提到組件在運行時被添加到容器。我也嘗試添加validate()到getBaseline方法和添加新組件後的容器容器。 – gerrit 2012-07-30 19:37:00

+0

嗯...不能在快速測試中複製(簡單的JPanel返回其第一個孩子的基線) - 最好顯示證明問題的SSCCE – kleopatra 2012-07-31 08:08:21

回答

1

初步意見,只是一個確認:

能否證實與外佈局或者網格包(您確切的例子)或MigLayout問題。如果外部佈局是FlowLayout,可以繞過。在這種情況下計算基準線之前迫使佈局不夠好

@Override 
public int getBaseline(int w, int h) { 
    // helps with simple managers like FlowLayout 
    // detoriates with powerful managers like GridBag or Mig 
    doLayout(); 
    Dimension size = textField.getPreferredSize(); 
    return textField.getY() + textField.getBaseline(size.width, size.height); 
} 

在外板主:

FlowLayout flow = new FlowLayout(); 
flow.setAlignOnBaseline(true); 
panel.setLayout(flow); 

不多,但也許你可以從這裏進一步向下挖了一下.. 。

編輯暫定溶液(?或黑客)和冥想作爲可能的原因

看起來像它需要一個FO兩步走佈局過程:

private void layoutElements() { 
    removeAll(); 
    GridBagConstraints constraints = new GridBagConstraints(); 
    constraints.anchor = GridBagConstraints.SOUTH; 
    add(textField, constraints); 

    for (JPanel comp : otherComponents) 
     add(comp, new GridBagConstraints()); 
    if (getParent() != null) { 
     getParent().validate(); 
     getParent().revalidate(); 
    } 
} 

請注意驗證與重新驗證的序列 - 由於某些原因,兩者都是需要的。不知道至於確切的機制,只是隨機猜測:

  • 佈局發生了自上而下的,所以如果父取決於孩子已經奠定了所有的孫子可以解釋兩通的需要。與大孩子相關的基線將是這樣的一個條件:
  • 第一遍發生在更廣泛的上下文中,即在第一次驗證結束時並非所有狀態都完全完成。然後,重新驗證確保在所有正在進行的更改完成後第二次傳遞發生
+0

謝謝,您的第二個解決方案工作。我無法在我的項目中使用FlowLayout。 – gerrit 2012-07-31 12:55:06

+0

第一個伎倆爲我工作。我們在不同的地方使用GroupLayout和FlowLayout,這似乎使它們適用於兩者。 – Trejkaz 2014-03-06 03:36:09