Java GUI 创建 JTables table 的最佳方式?

Java GUI Best way to create a table of JTables?

[更新!!!!] 这是成品:

:)

所以我正在开发一个程序来帮助我整理和打印晚餐座位。我有一个学生 class,其实例变量为 FirstName、LastName 等。我还有一个 DinnerTable Class,其中包含 Student 列表和 String teacherName。我创建了一个 GUI,以便更容易分配不同的座位。我已经在 J​​TabbedPane 上有一个选项卡,用于组织学生信息(如果它们可用或不可用)并随机分配他们坐的桌子。现在,为了更好地可视化表格并更容易地将每个学生分配到特定的座位,我需要做这样的事情: 我想在 TableModel 中使用实际的 DinnerTable 对象,这样每当我在此框架上编辑某些内容时,更改就会转换为对象。但是,我不确定该怎么做。我应该:

1.Create 表(每个 DinnerTable 一个 JTable)嵌套在更大的 JTable 的单元格中?但是我怎样才能执行诸如在 DinnerTable 之间滑动学生之类的操作呢?要么 2.Align GridLayout 或 GridBagLayout 中的 JTables?但同样,我如何交换学生?

谢谢! 包晚餐清单;

  public class Student 
  {
  private String lastName;
  private String firstName;
  private int grade;
  private int table;
  private boolean gender;//Male=true, female=false;
  private boolean available; //true=available;

public Student()
{
    lastName="";
    firstName="";
    grade=0;
    table=0;
    gender=true;
    available=true;
}
public Student(String l, String f, int i, boolean g, boolean a)
{

    lastName=l;
    firstName=f;
    grade=i;
    gender=g;
    available=a;
    //table is not written back to the txt.
    table=0;
}
//Getters

  public String getLastName()
   {
    return lastName;
  }
  public String getFirstName()
{
    return firstName;
}
public int getGrade()
{
    return grade;
}       
public int getTable()
{
    return table;
}
  public boolean getGender()
{
    return gender;
}
public boolean getAvailable()
{
    return available;
}

//Setters
public void setLastName(String s)
{
    this.lastName=s;
}
public void setFirstName(String s)
{
    firstName=s;
}
public void setGrade(int i)

{
    grade=i;
}
public void setTable(int hiahia)
{
    table=hiahia;
}
public void setGender(boolean b)
{
    gender=b;
}
public void setAvailable(boolean b)
{
    available=b;
}

//Miscellaneous
  public String toString()
  {
    String a="";

     a=lastName+","+firstName+","+Integer.toString(grade)+","+Boolean.toString(gender)+","+Boolean.toString(available);
    return a;
    }
 }

/////////////////////////////////

 package DinnerList;

   import java.util.ArrayList;
   import java.util.List;

  public class DinnerTable 
{
    private List<Student> members= new ArrayList<Student>();
    private int tableNumber=0;
    private int capacity=0;
    private String teacherName="";
    private boolean available=true;

public DinnerTable(int a, int b, String c, boolean d)
{
    tableNumber=a;
    capacity=b;
    teacherName=c;
    available=d;
}

public void setTableNumber(int a) {tableNumber=a;}
public void setCapacity(int a) {capacity=a;}
public void setTeacherName(String a) {teacherName=a;}
public void setAvailable(boolean b) {available=b;}
public void add(Student s)
{
    if(available&&(members.size()<capacity))
    { this.members.add(s); }
    else if(!available)
    { System.out.println("Adding Student failed, table "+tableNumber+" not available");}
    else
    { System.out.println("Adding Student failed, table "+tableNumber+" is full");}
}

public int getTableNumber() 
{return tableNumber;}
public int getCapacity() {return this.capacity;}
public String getTeacherName() {return teacherName;}
public boolean getAvailable() {return available;}
public List<Student> getMembers(){return members;}
public void remove(Student s) 
{
    if(members.contains(s))
    {
        members.remove(s);
    }
    else
    {
        System.out.println("Failed to remove student from table because it wasn't there");
    }
  }
}

////////////////////

package DinnerList;

import java.awt.Component;

import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;

public class DinnerTableCellRenderer extends DefaultTableCellRenderer implements TableCellRenderer {

@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
          boolean hasFocus, int row, int column)
{
    Object huahua =table.getModel().getValueAt(row, column);
    String ppp="";
    if(huahua!=null)
    {
        if(huahua instanceof Student)
        {
            ppp=((Student) huahua).getLastName()+", "+((Student)huahua).getFirstName();
        }
        else if(huahua instanceof String)
        {
            ppp=(String)huahua;
        }
        else
        {
            System.out.println("Error: DinnerTableCellRenderer intakes unknown data type");
        }
    }
    else
    {
        ppp="";
    }
    JLabel label = (JLabel)super.getTableCellRendererComponent(table, ppp,isSelected, hasFocus,row, column);
    return label;
    }

}

//////////

package DinnerList;

import java.util.ArrayList;
import java.util.List;

import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;

public class DinnerTableModel extends AbstractTableModel implements TableModel
 {
    private final String[] columnNames={"","","",""};
    private List<DinnerTable> tableCollection= new ArrayList<DinnerTable>();


public DinnerTableModel(List<DinnerTable> huhu)
{
    tableCollection.addAll(huhu);
}
public int getColumnCount() 
{
    return columnNames.length;
}

public int getRowCount() 
{
        if(tableCollection.size()%4==0)
        {
            return tableCollection.size()/4;
        }
        else
        {
            return (int)(tableCollection.size()/4)+1;
        }
}

public String getColumnName(int col) 
{
    return columnNames[col];
}

public DinnerTable getTableAt(int row, int column)
{
        if(tableCollection.size()>=(Integer)((row)/8)+column+1)
        {
            return tableCollection.get((Integer)((row)/8)+column);
        }
        else
        {
            return null;
        }

}
public Object getValueAt(int rowIndex, int colIndex) 
{
    DinnerTable dd= this.getTableAt(rowIndex, colIndex);
    String ss= "";
    if(dd==null)
    {
            return "";
    }
    else if(rowIndex%8==0)
    {
            return (dd.getTableNumber()+". "+dd.getTeacherName());
    }
    else if(dd.getMembers().size()>=rowIndex%8)
    {
            return dd.getMembers().get(rowIndex%8);
    }
    else
    {
            return "";
    }
}


public Class getColumnClass(int c) 
{
    return getValueAt(0, c).getClass();
}
public boolean isCellEditable(int row, int col) 
{
        if(this.getValueAt(row, col) instanceof Student)
        {
            return true;
        }
        else
        {
            return false;
        }
}
    public void setValueAt(Object aValue, int rowIndex, int columnIndex)
    {

  if(aValue instanceof Student)
  {
        if(rowIndex%8!=0)
        {
            if(null!=this.getTableAt(rowIndex,columnIndex))
            {
                this.getTableAt(rowIndex,columnIndex).getMembers().set(rowIndex%8-1, (Student)aValue);
            }
            else
            {
                System.out.println("error: Attempting to put student in nonexistent table in table list gui");
            }
        }
        else
        {
            System.out.println("error: Attempting to put student in a string in table list gui");
        }
  }
  else if(aValue instanceof String)
  {
        System.out.println("error: Attempting to change teacher name in tablelist gui");
  }
  else
  {
        System.out.println("error: Attempting to set unknown object type in tablelist gui");
  }
}

}

////****************////

JTable tableTable= new JTable(new DinnerTableModel(tables));
for(int hihihi=0;hihihi<tableTable.getColumnCount();hihihi++)
{
  tableTable.getColumnModel().getColumn(hihihi).setCellRenderer(new   DinnerTableCellRenderer());
}
JScrollPane scrollpaneB1= new JScrollPane();
scrollpaneB1.add(tableTable);
panelB.add(scrollpaneB1);

I am new to programming and have a lot of questions. It is very nice of you to be so patient! : ) However, I have another JTable that intakes another TableModel class. I ran tests and it seems that the model does change whenever the original objects change, even though I didn't use any propertychangelisteners. This is very confusing to me...

JTable 是 API 中更复杂的组件之一(JTree 和文本组件是下一个),如果你能成为 comfortable它,你将在 API.

的整体上变得舒适 table 还有很长的路要走

您可以通过多种方式通知 TableModel 对象已更改,但最好的方式之一是以解耦的方式进行通知,这样您就不必维护在 Student 对象可能更改的每个地方引用 TableModel

这表明 Student 对象本身应该生成某种更改事件,感兴趣的各方(如 TabelModel)可以侦听并在发生时采取行动。

通过这种方式,您可以在代码中的任何位置更改 Student 对象,而无需关心谁可能需要知道该对象已更改。

现在,这基本上可以归结为观察者模式,Swing 在其侦听器中实现了这种功能,ChangeListener 可能是最坦诚的想法,但 PropertyChangeListener 是一个更强大的选项,因为它会根据已更改的 属性 生成事件。

在某些情况下,您可能不会关心 属性 发生了什么变化,但在 TableModel 的情况下,它非常有用。

这是概念的简单演示,使用两个 table。您可以在 table 中编辑任何值,并且在提交值时将更新相反的 table 。现在,您还可以在代码中修改 Student 的实例,您会得到相同的结果,但这是创建一个简单的示例。

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.AbstractTableModel;

public class Test {

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

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                Student student = new Student("Skywalker", "Luke", 0, true, true);
                StudentTabelModel leftModel = new StudentTabelModel(student);
                StudentTabelModel rightModel = new StudentTabelModel(student);

                JTable leftTable = new JTable(leftModel);
                leftTable.setGridColor(Color.GRAY);
                JTable rightTable = new JTable(rightModel);
                rightTable.setGridColor(Color.GRAY);

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new GridLayout(0, 2));
                frame.add(new JScrollPane(leftTable));
                frame.add(new JScrollPane(rightTable));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class StudentTabelModel extends AbstractTableModel {

        private List<Student> students = new ArrayList<>(25);
        private PropertyChangeListener propertyChangeListener;

        public StudentTabelModel(Student... students) {
            propertyChangeListener = new StudentPropertyChangeListener();
            add(students);
        }

        public void add(Student... students) {
            if (students != null && students.length > 0) {
                int startRow = this.students.size();
                for (Student student : students) {
                    student.addPropertyChangeListener(propertyChangeListener);
                    this.students.add(student);
                }
                fireTableRowsInserted(startRow, this.students.size() - 1);
            }
        }

        public void remove(Student ...students) {
            if (students != null && students.length > 0) {
                for (Student student : students) {
                    int index = this.students.indexOf(student);
                    if (index != -1) {
                        student.removePropertyChangeListener(propertyChangeListener);
                        this.students.remove(student);
                        fireTableRowsDeleted(index, index);
                    }
                }
            }
        }

        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return true;
        }

        @Override
        public int getRowCount() {
            return students.size();
        }

        @Override
        public int getColumnCount() {
            return 6;
        }

        @Override
        public Class<?> getColumnClass(int columnIndex) {
            switch (columnIndex) {
                case 0:
                case 1:
                    return String.class;
                case 2:
                case 3:
                    return Integer.class;
                case 4:
                case 5:
                    return Boolean.class;
            }
            return Object.class;
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            Student student = students.get(rowIndex);
            switch (columnIndex) {
                case 0:
                    return student.getLastName();
                case 1:
                    return student.getFirstName();
                case 2:
                    return student.getGrade();
                case 3:
                    return student.getTable();
                case 4:
                    return student.getGender();
                case 5:
                    return student.getAvailable();
            }
            return "??";
        }

        @Override
        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
            Student student = students.get(rowIndex);
            switch (columnIndex) {
                case 0:
                    student.setLastName(aValue == null ? null : aValue.toString());
                    break;
                case 1:
                    student.setFirstName(aValue == null ? null : aValue.toString());
                    break;
                case 2:
                    if (aValue instanceof Integer) {
                        student.setGrade((Integer) aValue);
                    }
                    break;
                case 3:
                    if (aValue instanceof Integer) {
                        student.setTable((Integer) aValue);
                    }
                    break;
                case 4:
                    if (aValue instanceof Boolean) {
                        student.setGender((Boolean) aValue);
                    }
                    break;
                case 5:
                    if (aValue instanceof Boolean) {
                        student.setAvailable((Boolean) aValue);
                    }
                    break;
            }
        }

        protected class StudentPropertyChangeListener implements PropertyChangeListener {

            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                if (evt.getSource() instanceof Student) {
                    Student student = (Student) evt.getSource();
                    int row = students.indexOf(student);
                    if (row != -1) {
                        switch (evt.getPropertyName()) {
                            case "lastName":
                                fireTableCellUpdated(row, 0);
                                break;
                            case "firstName":
                                fireTableCellUpdated(row, 1);
                                break;
                            case "grade":
                                fireTableCellUpdated(row, 2);
                                break;
                            case "table":
                                fireTableCellUpdated(row, 3);
                                break;
                            case "gender":
                                fireTableCellUpdated(row, 4);
                                break;
                            case "avaliable":
                                fireTableCellUpdated(row, 5);
                                break;
                        }
                    }
                }
            }

        }

    }

    public class Student {

        private String lastName;
        private String firstName;
        private int grade;
        private int table;
        private boolean gender;//Male=true, female=false;
        private boolean available; //true=available;

        private PropertyChangeSupport propertyChangeSupport;

        public Student() {
            this("", "", 0, false, true);
        }

        public Student(String l, String f, int i, boolean g, boolean a) {

            lastName = l;
            firstName = f;
            grade = i;
            gender = g;
            available = a;
            //table is not written back to the txt.
            table = 0;
            propertyChangeSupport = new PropertyChangeSupport(this);
        }

        public void addPropertyChangeListener(PropertyChangeListener listener) {
            propertyChangeSupport.addPropertyChangeListener(listener);
        }

        public void removePropertyChangeListener(PropertyChangeListener listener) {
            propertyChangeSupport.removePropertyChangeListener(listener);
        }
//Getters

        public String getLastName() {
            return lastName;
        }

        public String getFirstName() {
            return firstName;
        }

        public int getGrade() {
            return grade;
        }

        public int getTable() {
            return table;
        }

        public boolean getGender() {
            return gender;
        }

        public boolean getAvailable() {
            return available;
        }

//Setters
        public void setLastName(String s) {
            String old = lastName;
            this.lastName = s;
            propertyChangeSupport.firePropertyChange("lastName", old, lastName);
        }

        public void setFirstName(String s) {
            String old = firstName;
            firstName = s;
            propertyChangeSupport.firePropertyChange("firstName", old, firstName);
        }

        public void setGrade(int i) {
            int old = grade;
            grade = i;
            propertyChangeSupport.firePropertyChange("grade", old, grade);
        }

        public void setTable(int hiahia) {
            int old = table;
            table = hiahia;
            propertyChangeSupport.firePropertyChange("table", old, table);
        }

        public void setGender(boolean b) {
            boolean old = gender;
            gender = b;
            propertyChangeSupport.firePropertyChange("gender", old, gender);
        }

        public void setAvailable(boolean b) {
            boolean old = available;
            available = b;
            propertyChangeSupport.firePropertyChange("available", old, available);
        }

//Miscellaneous
        @Override
        public String toString() {
            String a = "";

            a = lastName + "," + firstName + "," + Integer.toString(grade) + "," + Boolean.toString(gender) + "," + Boolean.toString(available);
            return a;
        }
    }
}

请记住,这依赖于共享的 Student 实例,如果您创建 Student 对象的两个不同实例并期望一个实例中的更改将反映在另一个实例中,那你会失望的