2016-08-10 1 views
1

昨日私がJTableと(JButtonを含む)をJScrollPaneに追加したときにエラーが発生しました。 JButtonはテーブルの下部に固定され、クリックするとJTableに行が追加されました。なぜsetPreferredSize()がsetMinimumSize()より先になるのはなぜですか?

テーブルがJScrollPaneよりも大きくなった場合は、JTableの下部までスクロールすることができます。あなたはもうJButtonに行くことができませんでした。今日、私はMCVEに手伝ってもらうようにしましたが、まず最初に私はそれに猿をつけて問題を解決しましたが、回答よりも多くの質問を残しました...私が準備したMCVEは、

import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.GridBagConstraints; 
import java.awt.GridBagLayout; 
import java.awt.event.*; 
import java.util.*; 

import javax.swing.*; 
import javax.swing.table.DefaultTableModel; 

public class MCVE extends JFrame{ 

    private JButton addRow; 
    private MCVEModel tableModel; 
    private JTable table; 
    private JScrollPane pane; 
    private JPanel scrollPanel, panel; 

    public static void main (String[] args) { 
     new MCVE(); 
    } 

    public MCVE() { 
     initialize(); 
    } 

    public void initialize() { 
     this.setTitle("Halp"); 
     this.setDefaultCloseOperation(EXIT_ON_CLOSE); 
     this.setBounds(50, 50, 500, 300); 
     this.setResizable(false); 

     JPanel mainPanel = new JPanel(new GridBagLayout()); 

     /** The JPanel everything is put into **/ 
     scrollPanel = new JPanel(); 
     scrollPanel.setLayout(new BoxLayout(scrollPanel, BoxLayout.Y_AXIS)); 

     /** The JScrollPane we're using **/ 
     pane = new JScrollPane(scrollPanel); 
     pane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); 
     pane.getVerticalScrollBar().setUnitIncrement(10); 

     /** The button which keeps getting cut off.... **/ 
     addRow = new JButton("..."); 
     addRow.setBackground(Color.WHITE); 
     addRow.setMnemonic('R'); 
     addRow.setFocusable(false); 
     addRow.addActionListener(new ActionListener() { 
      public void actionPerformed(ActionEvent e) { 
       addNewRow(); 
      } 
     }); 

     /** I wrap the button into this panel so I can affix it to the left **/ 
     panel = new JPanel(); 
     panel.setMinimumSize(new Dimension(0, 20)); 
     panel.setLayout(null); 
     addRow.setBounds(0, 0, 35, 15); 
     panel.add(addRow); 

     /** Faking some data to get the table to populate **/ 
     ArrayList<List<String>> allData = new ArrayList<List<String>>(); 
     ArrayList<String> fakeData = new ArrayList<String>(); 
     fakeData.addAll(Arrays.asList(
        new String[]{"this", "is", "just", "sample", "data"})); 
     for(int i = 0; i < 5; i++) 
      allData.add(fakeData); 

     List<String> columnNames = Arrays.asList(new String[] {"", "", "", "", ""}); 
     tableModel = new MCVEModel(columnNames, allData); 

     table = new JTable(); 
     table.setModel(tableModel); 

     /** Adding it all together **/ 
     GridBagConstraints c = new GridBagConstraints(); 
     c.fill = GridBagConstraints.BOTH; 
     c.weightx = 1; 
     c.weighty = 1; 
     scrollPanel.add(table); 
     scrollPanel.add(panel); 
     mainPanel.add(pane, c); 
     this.add(mainPanel); 

     this.setVisible(true); 
    } 

    public void addNewRow() { 
     tableModel.addRow(tableModel.getRowCount(), 
       new String[]{"true", "", "", "false", "false"}); 
     tableModel.fireTableRowsInserted(
       tableModel.getRowCount(), tableModel.getRowCount()); 
    } 
} 
/** 
* Just here to keep things compilable. Seriously cut back for the MCVE, 
* but still replicates the problem without any errors. 
* Nothing below here should be relevant to the issue. 
*/ 
class MCVEModel extends DefaultTableModel { 

    private static final long serialVersionUID = -6598574844380686148L; 
    private List<String> columnNames; 
    private List<List<String>> values; 

    public MCVEModel (List<String> columnNames, List<List<String>> strings) { 
     this.columnNames = columnNames; 
     this.values = strings; 
    } 

    public int getColumnCount() { 
     return columnNames.size(); 
    } 

    public int getRowCount() { 
     return values == null || values.size() == 0 ? 0 : values.get(0).size(); 
    } 

    public String getColumnName(int col) { 
     return columnNames.get(col); 
    } 

    public Object getValueAt(int row, int col) { 
     return values.get(col).get(row); 
    } 

    @SuppressWarnings({ "unchecked", "rawtypes" }) 
    public Class getColumnClass(int c) { 
     return String.class; 
    } 

    public boolean isCellEditable(int row, int col) { 
     return true; 
    } 

    public void setValueAt(Object value, int row, int col) { 
     values.get(col).set(row, (String) value); 
     fireTableCellUpdated(row, col); 
    } 

    public void removeRow(int row) { 
     for(int i = 0; i < values.size(); i++) 
      values.get(i).remove(row); 
     this.fireTableRowsDeleted(row, row); 
    } 

    public void addRow(int row, String[] strings) { 
     for(int i = 0; i < values.size(); i++) 
      values.get(i).add(row, strings[i]); 
     fireTableRowsInserted(row, row); 
    } 
} 

問題は、この行を次のとおりです。

panel.setMinimumSize(new Dimension(0, 20)); 

はより正確には、単語 "最小" でです。これを次のように変更します:

panel.setPreferredSize(new Dimension(0, 20)); 

私は必要な機能を正確に取得しました。テーブルがJScrollPanelのために大きすぎると、今でもスクロールしてJButtonを見ることができます。もはや切断されていない。

これは、JPanelの親がその最小寸法を守っておらず、好ましい寸法を守ったと仮定しています。どうしてこれなの? setPreferredSize()setMinimumSize()setMaximumSize()は、「私はこの大きなものが好きですが、自分の最小値よりも小さくても、最大値よりも小さくてもいけません」と考えていましたが、これはそうではないようです場合。私はこれらの方法のどれも頻繁に使用されるべきではないことを知っていますが、setMinimumSize()setPreferredSize()以上に使う必要がありますか?

+1

nullレイアウトまたはsetPreferredSize()を使用しないでください。パネルの好ましいサイズを動的に決定するのは、レイアウトマネージャーの仕事です。 'fireTableRowsInserted'を使わないでください。その方法を呼び出すのはテーブルモデルの仕事なので、2回行う必要はありません。 – camickr

+0

私は 'fireTableRowsInserted'を認めます。なぜなら、コードはそれがなくても同じように動くからです。しかし、私は 'JButton'にJavaのレイアウトマネージャのいずれかでこの動作をさせることができませんでした。もし私が新しいものを書いたのであれば、この1つのボタンを除いて' BoxLayout'と同じように動作します。私が完全にコントロールしていた 'JPanel'にボタンをラップし、そのパネルを' JScrollPane'に入れて、私の現在のレイアウトマネージャを動かす必要はないでしょう。 nullレイアウトやsetPreferredSize()を使用せずにJButtonを動作させる別の方法はありますか?' –

+1

'JButtonにこの動作を持たせることができませんでした」 - どのような振る舞いですか?余分なパネルは必要ありません。 BoxLayoutを使用してテーブルとボタンをパネルに追加すれば、すべてうまく動作します。ヌルレイアウトを使用したり、getPreferredSize()メソッドで再生したりするときにのみ問題が発生します。このアドバイスに従うと、スクロールが適切に機能します。 – camickr

答えて

3

魔法はこの行です:だから

scrollPanel.add(panel); 

scrollPanelは、このパネルが含まれています。次に、JScrollPanepreferredSize -sを尊重します。スクロールバーを使用することで、その目的が含まれているコンポーネントのための十分なスペースを確保するため、意味があります。つまり、JScrollPane -sは、minimumSize -sを無視します。

更新:

他の角度から、JScrollPane -sソースコードは、その子のpreferredSizeをチェックしますが、ないminimumSize。ここに深い哲学はありません。JScrollPaneがこのように実装されています。

+1

なぜ、コンポーネントが少なくとも最小サイズであることをsetMinimumSizeが保証しないのですか?それはOPが求めているものであり、あなたはその質問に答えることはできません。 –

+1

私が書きましたが、scrollPanelは 'preferredSize'-sを尊重しています。それでおしまい。 'miniumumSize'を無視します。この理由は、Javaソースコードです。 –

+2

Yepp、答えを更新しました。 –

関連する問題