调整 JDialog 和 JTable 列的大小时出现问题

Problem resizing JDialog and JTable columns

我正在创建一个带有 JTableJDialog window,并且我正在尝试使其成为 "responsive" 以适应不同的屏幕尺寸。由于 window 和 table 大小可能会改变,我按百分比设置列宽。

目前没有问题。第一次显示 table 时一切看起来都很好。一旦 table 被重新加载(由于修改或其他原因),列的大小看起来不同,但 table 和 window 的大小仍然相同。这种情况仅在第一次重新加载时发生,然后每次重新加载时所有内容都保持相同的大小。

我使用 JOptionPane 消息对话框在每次加载时显示 table 宽度,我发现了问题。 table 第一次加载时使用的是不同的大小(JScrollPane 首选大小),然后下一次使用应该是调整大小操作的大小。

1st time loaded (image)

2nd time loaded (image)

真正的问题是为什么?

我已经尝试将 JDialog 调整大小的代码移动到 initComponents() 方法的不同位置(创建全局维度变量),但同样的事情仍在发生。如果我删除调整大小的 JDialog 代码,一切都很好,但这是胆小鬼的方式,哈哈。

这是我正在使用的代码,至少我认为是相关的。

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumnModel;
public class Test extends JDialog {
  private javax.swing.JButton bUpdate;
  private javax.swing.JPanel jPanel1;
  private javax.swing.JTable tabla;
  private javax.swing.JScrollPane tablaSP;  
  public Test(Dimension d){
    initComponents();
    int w = (int) (Math.round(d.getWidth())/2);
    int h = (int) (Math.round(d.getHeight())/2);
    setSize(w, h);
    setLocationRelativeTo(null);
    bUpdate.addActionListener(new ActionListener(){
      public void actionPerformed(ActionEvent e){
        cargarProductos();
      }
    });
    cargarProductos();
  }
  private void cargarProductos(){
    try{
      String atr[] = {"Código", "Proveedor", "Nombre", "Clasificación", "Descripción", "Tipo", "P. Compra", "P. Venta"};
      DefaultTableModel model = new DefaultTableModel(null, atr){
          @Override
          public boolean isCellEditable(int row, int column) {
              return false;
          }
      };
      //Filling table model
      tabla.setModel(model);
      int tw = tablaSP.getWidth();
      TableColumnModel cm=tabla.getColumnModel();
      cm.getColumn(0).setPreferredWidth((int) (tw*.10));
      cm.getColumn(1).setPreferredWidth((int) (tw*.15));
      cm.getColumn(2).setPreferredWidth((int) (tw*.15));
      cm.getColumn(3).setPreferredWidth((int) (tw*.10));
      cm.getColumn(4).setPreferredWidth((int) (tw*.19));
      cm.getColumn(5).setPreferredWidth((int) (tw*.10));
      cm.getColumn(6).setPreferredWidth((int) (tw*.10));
      cm.getColumn(7).setPreferredWidth((int) (tw*.10));
      tabla.setAutoCreateRowSorter(true);
      JOptionPane.showMessageDialog(null, tw, "Table width", JOptionPane.PLAIN_MESSAGE);
    }catch(Exception e){
      JOptionPane.showMessageDialog(this, e.getMessage(), "Error llenando tabla de productos", JOptionPane.PLAIN_MESSAGE);
    }
  }
  public static void main(String[]args){
    try {
      for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
        if ("Windows".equals(info.getName())) {
          UIManager.setLookAndFeel(info.getClassName());
          break;
        }
      }
    } catch(Exception e) {
      JOptionPane.showMessageDialog(null, e.getMessage());
    } 
    EventQueue.invokeLater(new Runnable() {
      public void run() {
        Test dialog = new Test(new Dimension (Toolkit.getDefaultToolkit().getScreenSize()));
        dialog.setVisible(true);
      }
    });
  }
  private void initComponents() {
    jPanel1 = new javax.swing.JPanel();
    bUpdate = new javax.swing.JButton();
    tablaSP = new javax.swing.JScrollPane();
    tabla = new javax.swing.JTable();

    setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
    setModalityType(java.awt.Dialog.ModalityType.APPLICATION_MODAL);
    setSize(new java.awt.Dimension(0, 0));

    bUpdate.setText("Update");

    javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
    jPanel1.setLayout(jPanel1Layout);
    jPanel1Layout.setHorizontalGroup(
      jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(jPanel1Layout.createSequentialGroup()
        .addContainerGap()
        .addComponent(bUpdate, javax.swing.GroupLayout.DEFAULT_SIZE, 83, Short.MAX_VALUE)
        .addContainerGap())
    );
    jPanel1Layout.setVerticalGroup(
      jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(jPanel1Layout.createSequentialGroup()
        .addContainerGap()
        .addComponent(bUpdate)
        .addContainerGap(242, Short.MAX_VALUE))
    );

    tabla.setModel(new javax.swing.table.DefaultTableModel(
      new Object[][]{ {},{},{},{} },
      new String[]{}
    ));
    tablaSP.setViewportView(tabla);

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
    getContentPane().setLayout(layout);
    layout.setHorizontalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(layout.createSequentialGroup()
        .addContainerGap()
        .addComponent(tablaSP)
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
    );
    layout.setVerticalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
      .addGroup(layout.createSequentialGroup()
        .addContainerGap()
        .addComponent(tablaSP, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
        .addContainerGap())
    );

    pack();
  }
}

我希望它是 JDialog,因为我需要它作为模态 Window,但我还需要让它变大一些 "responsive",因为内容 [=47] =] 可能很大。

最初的问题是您提前调用 initComponents,即在应用您希望它具有的尺寸之前。因此,在 initComponents components 方法中调用 pack 会将您的面板缩小到最小尺寸。

之后您更改了对话框的大小(通过调用 setSize(w,h)), 但这并没有直接影响所涉及的组件。但是,当对话框设置为 visible 时,组件会自动调整以适应定义的大小。这不适用于您的列大小,因为您没有定义会触发此操作的 ComponentListener(请参见下面的第二个示例)。

这导致单击 update 按钮第一次考虑组件的调整大小,因此它们被应用到列。

要解决不同大小的问题,请将构造函数中的方法调用更改为
(下面是构造函数的完整示例):

int w = (int) (Math.round(d.getWidth()) / 2);
int h = (int) (Math.round(d.getHeight()) / 2);
setPreferredSize(new Dimension(w, h));

initComponents();

您可能想从 initComponents() 方法中删除 setSize(new java.awt.Dimension(0, 0));


如果您想在用户手动调整对话框大小时保持列大小,请考虑添加 ComponentListener,再举一个例子,检查此 answer

这也可以用作原始代码的替代解决方案, 但首先正确定义尺寸可能更清晰。

public Test(Dimension d) {

    int w = (int) (Math.round(d.getWidth()) / 2);
    int h = (int) (Math.round(d.getHeight()) / 2);
    setPreferredSize(new Dimension(w, h));

    initComponents();

    setLocationRelativeTo(null);
    bUpdate.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            cargarProductos();
        }
    });

    this.addComponentListener(new ComponentAdapter() {
         @Override
         public void componentResized(ComponentEvent e) {
             cargarProductos();
         }
    });
}