Java JList格式与JTable的功能

时间:2014-08-05 22:44:59

标签: java jtable jlist defaulttablemodel defaultlistmodel

我正在尝试以这样的方式显示患者数据列表,即名称位于我的JList的左侧,并且标识号全部整齐地排列在右侧。 我已经查看过甚至为此任务实现了JTable的使用,因为它确实提供了我希望在这个项目中实现的整洁的列结构,但是有一些功能我要么不知道如何实现,要么我觉得缺乏JTable。

  1. 无法根据字母数字键转换我的列表。这是最终用户快速搜索列表的主要方式,因为填充时它可以超过800行。 (例如:输入' s以转换到以' s开头的列表部分)

  2. 从视觉上来说,我不喜欢JTables的行选择;使用两列结构显然两列之间存在中断,与JList不同,这是一个很好的平滑无缝突出显示选择。所有网格线都已关闭,以及使用单元格编辑作为FYI。

  3. 我已经包含了我认为的相关代码。我希望能有一些方向,如果我要求的是用JList完成的,还是有办法更好地操作JTable?我在JTable API和JList API中都找不到任何内容。我觉得我在这个问题上回答了我的问题。

      package mosaiqToCTWorklist;
    
    import java.awt.Font;
    import java.awt.Label;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Map.Entry;
    
    import javax.swing.ButtonGroup;
    import javax.swing.DefaultListModel;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JList;
    import javax.swing.JPanel;
    import javax.swing.JRadioButton;
    import javax.swing.JScrollPane;
    import javax.swing.border.EmptyBorder;
    
    public class PatientSelectionWindow extends JFrame implements ActionListener {
    
        private static final long serialVersionUID = 1L;
        private JPanel contentPane;
        private JRadioButton rdbtnActivePatients;
        private JRadioButton rdbtnInactivePatients;
        private JRadioButton rdbtnAllPatients;
        private JButton btnSendSelection;
        private JList<String> patients;
        private JScrollPane scrollBar;
        private ButtonGroup btngroup;
        private DefaultListModel<String> model = new DefaultListModel<String>();
        private List<PatientData> ptList;
        //  private HashMap<String,PatientData> ptRecords;
        private Label lblPatientTotal;
        private Label lblDOB;
        private Label lblAge;
        private Label lblGender;
        private Label label_4;
    
    
        public PatientSelectionWindow(List<PatientData> ptList) {
            this.ptList = ptList;
            activePatients();
            createWindow();
        }
    
        public void createWindow() {
            setResizable(false);
            setTitle("Mosaiq to CT Worklist");
            setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            setBounds(100, 100, 534, 378);
            contentPane = new JPanel();
            contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
            setContentPane(contentPane);
            contentPane.setLayout(null);
    
            patients = new JList<String>(model);
            scrollBar = new JScrollPane(patients);
            scrollBar.setBounds(10, 11, 376, 328);
            scrollBar.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
            contentPane.add(scrollBar);
    
            rdbtnActivePatients = new JRadioButton("Active Patients");
            rdbtnActivePatients.addActionListener(this);
            rdbtnActivePatients.setSelected(true);
            rdbtnActivePatients.setFont(new Font("Tahoma", Font.PLAIN, 13));
            rdbtnActivePatients.setBounds(392, 35, 130, 23);
            contentPane.add(rdbtnActivePatients);
    
            rdbtnInactivePatients = new JRadioButton("Inactive Patients");
            rdbtnInactivePatients.addActionListener(this);
            rdbtnInactivePatients.setFont(new Font("Tahoma", Font.PLAIN, 13));
            rdbtnInactivePatients.setBounds(392, 61, 130, 23);
            contentPane.add(rdbtnInactivePatients);
    
            rdbtnAllPatients = new JRadioButton("All Patients");
            rdbtnAllPatients.addActionListener(this);
            rdbtnAllPatients.setFont(new Font("Tahoma", Font.PLAIN, 13));
            rdbtnAllPatients.setBounds(392, 86, 130, 23);
            contentPane.add(rdbtnAllPatients);
    
            btngroup = new ButtonGroup();
            btngroup.add(rdbtnActivePatients);
            btngroup.add(rdbtnInactivePatients);
            btngroup.add(rdbtnAllPatients);
    
            btnSendSelection = new JButton("Send Selected");
            btnSendSelection.addActionListener(new DicomTransfer());
            btnSendSelection.setFont(new Font("Tahoma", Font.PLAIN, 13));
            btnSendSelection.setBounds(396, 154, 126, 38);
            contentPane.add(btnSendSelection);
    
            Label label = new Label("Show");
            label.setFont(new Font("Dialog", Font.BOLD, 11));
            label.setBounds(392, 10, 62, 22);
            contentPane.add(label);
    
            lblPatientTotal = new Label("");
            setPatientCount();
            lblPatientTotal.setBounds(392, 115, 130, 29);
            contentPane.add(lblPatientTotal);
    
            Label label_1 = new Label("D.O.B:");
            label_1.setBounds(396, 252, 38, 22);
            contentPane.add(label_1);
    
            Label label_2 = new Label("Age:");
            label_2.setBounds(396, 226, 30, 22);
            contentPane.add(label_2);
    
            Label label_3 = new Label("Gender:");
            label_3.setBounds(396, 280, 46, 22);
            contentPane.add(label_3);
    
            lblDOB = new Label("1984-10-10");
            lblDOB.setBounds(434, 252, 88, 22);
            contentPane.add(lblDOB);
    
            lblAge = new Label("64");
            lblAge.setBounds(426, 226, 38, 22);
            contentPane.add(lblAge);
    
            lblGender = new Label("male");
            lblGender.setBounds(444, 280, 62, 22);
            contentPane.add(lblGender);
    
            label_4 = new Label("Currently Selected Pt.");
            label_4.setFont(new Font("Dialog", Font.BOLD, 11));
            label_4.setBounds(392, 198, 130, 22);
            contentPane.add(label_4);
        }
    
        private void setPatientCount() {
            Integer patientTotal = model.size();
            lblPatientTotal.setText("Total Patients: " + patientTotal.toString());
        }
    
        private void activePatients() {
            model.clear();
            for(PatientData pt : ptList){
                if (pt.getActiveStatus() == 1) {
                    model.addElement(pt.getLastName() + ", " + pt.getFirstName() + " "
                            + pt.getMiddleName() + " " + pt.getMHN() );
                }
            }
        }
    
        private void inActivePatients() {
            model.clear();
            for(PatientData pt : ptList){
                if (pt.getActiveStatus() == 0) {
                    model.addElement(pt.getLastName() + ", " + pt.getFirstName() + " "
                            + pt.getMiddleName() + " " + pt.getMHN() );
                }
            }
        }
    
        public void allPatients() {
            model.clear();
            for(PatientData pt : ptList){
                model.addElement(pt.getLastName() + ", " + pt.getFirstName() + " "
                        + pt.getMiddleName() + " " + pt.getMHN() );
            }
        }
    
        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == rdbtnActivePatients) {
                activePatients();
            }
            if (e.getSource() == rdbtnAllPatients) {
                allPatients();
            }
            if (e.getSource() == rdbtnInactivePatients) {
                inActivePatients();
            }
    
            patients.setModel(model);
            setPatientCount();
        }
        }
    

    (是的,我知道我正在使用水果......我很饿)我得到的例子与我希望的更多内容。它只是让最终用户更容易阅读。

    enter image description here enter image description here

1 个答案:

答案 0 :(得分:6)

让我们从模型和观点与他们的责任之间的区别开始。

模型对&#34;模型&#34;的责任。数据以某种方式。这通常意味着提供有关模型大小(其中包含的项目数)和以某种方式访问​​数据(getter)的信息。

视图的责任是决定如何最好地显示此数据。

这一切对你的问题有什么帮助?事实上,每件事。该模型不应该尝试预先格式化数据,而应该包含原始数据。决定如何显示数据是观点的责任。

例如,您可能有一个JList{mhn} {first name} {last name}的形式显示患者数据,而另一个JList则需要{lastname}, {first name} {givenname} ...

您不希望每次都创建新模型,相反,您需要一个仅包含原始数据的简单模型,这些模型可以传递给其中任何一个视图,并允许他们决定如何显示信息。在你告诉我&#34;这不会发生&#34; ...只是知道,它会,因为那是用户的方式......

所以,而不是......

private DefaultListModel<String> model = new DefaultListModel<String>();
private List<String> ptList;

你想要使用更像......

的东西
private DefaultListModel<PatientData> model = new DefaultListModel<PatientData>();
private List<PatientData> ptList;

现在,这将引发下一个问题&#34;如何格式化原始数据?&#34;。 Swing提供了渲染器(在某些情况下是编辑器)的概念,它提供了一个可扩展和可插拔的API,为您提供了一种方法,您可以通过它来提供格式化器&#34;到JListJTableJTree等课程。这是一个重要的概念,因为它在Swing中被广泛使用。

首先看一下How to use Lists, Writing a Custom Cell Renderer

现在,因为我们正在处理结构化数据,所以事情变得有点困难,因为JList并不是为处理这种性质的结构化数据而设计的。请记住,每个单元格/行都是单独渲染的,没有任何其他单元格或行的上下文,这使得使用JList非常难以布置复杂的结构,例如......

一种方法是从sratch创建我们自己的ListCellRenderer。这是有问题的,因为单元格的大部分格式都来自DefaultListCellRenderer,因此我们需要自己提供所有格式,并且因为我们使用多个组件,这会加剧问题。

以下渲染器将生成以下输出...

Render by panel

public class NameFirstWithMNHListCellRenderer extends JPanel implements ListCellRenderer<PatientData> {

    private static final Border SAFE_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1);
private static final Border DEFAULT_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1);
protected static Border noFocusBorder = DEFAULT_NO_FOCUS_BORDER;

    private JLabel name;
    private JLabel mhn;

    public NameFirstWithMNHListCellRenderer() {
        name = new JLabel();
        mhn = new JLabel();
        setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.weightx = 1;
        gbc.anchor = GridBagConstraints.WEST;
        add(name, gbc);
        gbc.gridx++;
        gbc.weightx = 0;
        gbc.anchor = GridBagConstraints.EAST;
        add(mhn, gbc);
    }

    @Override
    public Component getListCellRendererComponent(JList<? extends PatientData> list, PatientData value, int index, boolean isSelected, boolean cellHasFocus) {
        name.setText(value.getFirstName() + " " + value.getLastName());
        mhn.setText(value.getMHN());
        formatBackground(name, list, index, isSelected, cellHasFocus);
        formatBackground(mhn, list, index, isSelected, cellHasFocus);
        formatBackground(this, list, index, isSelected, cellHasFocus);
        formatForeground(name, list, index, isSelected, cellHasFocus);
        formatForeground(mhn, list, index, isSelected, cellHasFocus);
        formatForeground(this, list, index, isSelected, cellHasFocus);

        formatBorder(this, list, index, isSelected, cellHasFocus);

        return this;
    }

    private Border getNoFocusBorder() {
    Border border = UIManager.getBorder("List.cellNoFocusBorder");
    if (System.getSecurityManager() != null) {
        if (border != null) return border;
        return SAFE_NO_FOCUS_BORDER;
    } else {
        if (border != null &&
                (noFocusBorder == null ||
                noFocusBorder == DEFAULT_NO_FOCUS_BORDER)) {
            return border;
        }
        return noFocusBorder;
    }
}

    protected void formatBorder(JComponent comp, JList<? extends PatientData> list, int index, boolean isSelected, boolean cellHasFocus) {
        Border border = null;
        if (cellHasFocus) {
            if (isSelected) {
                border = UIManager.getBorder("List.focusSelectedCellHighlightBorder");
            }
            if (border == null) {
                border = UIManager.getBorder("List.focusCellHighlightBorder");
            }
        } else {
            border = getNoFocusBorder();
        }
        comp.setBorder(border);
    }

    protected void formatBackground(JComponent comp, JList<? extends PatientData> list, int index, boolean isSelected, boolean cellHasFocus) {
        Color bg = null;

        JList.DropLocation dropLocation = list.getDropLocation();
        if (dropLocation != null
                && !dropLocation.isInsert()
                && dropLocation.getIndex() == index) {

            bg = UIManager.getColor("List.dropCellBackground");

            isSelected = true;
        }

        if (isSelected) {
            comp.setBackground(bg == null ? list.getSelectionBackground() : bg);
        } else {
            comp.setBackground(list.getBackground());
        }

        comp.setOpaque(isSelected);

        setEnabled(list.isEnabled());
    }

    protected void formatForeground(Component comp, JList<? extends PatientData> list, int index, boolean isSelected, boolean cellHasFocus) {
        Color fg = null;

        JList.DropLocation dropLocation = list.getDropLocation();
        if (dropLocation != null
                && !dropLocation.isInsert()
                && dropLocation.getIndex() == index) {

            fg = UIManager.getColor("List.dropCellForeground");

            isSelected = true;
        }

        if (isSelected) {
            comp.setForeground(fg == null ? list.getSelectionForeground() : fg);
        } else {
            comp.setForeground(list.getForeground());
        }

        comp.setEnabled(list.isEnabled());
        comp.setFont(list.getFont());

    }
}

现在,在你从房间里跑来跑去之前,这只是一个可能的解决方案的演示。还有其他一些方法可以达到相同的结果,这些方法稍微复杂一点,但是会引入自己的问题......

总而言之,JList根本就没有设计成以这种方式支持结构化数据,这就是我们JTable ...

的原因 另一方面,JTable非常适合显示结构化数据,并且可以通过一些简单的调整来显示&#34;出现&#34;无缝...

让我们从TableModel ...

开始
public static class PatientTableModel extends AbstractTableModel {

    protected static final String[] COLUMN_NAMES = {
        "First name",
        "Last name",
        "MHN"
    };

    private List<PatientData> rowData;

    public PatientTableModel() {
        rowData = new ArrayList<>(25);
    }

    public void add(PatientData... pd) {
        add(Arrays.asList(pd));
    }

    public void add(List<PatientData> pd) {
        rowData.addAll(pd);
        fireTableDataChanged();
    }

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

    @Override
    public int getColumnCount() {
        return COLUMN_NAMES.length;
    }

    @Override
    public String getColumnName(int column) {
        return COLUMN_NAMES[column];
    }

    public PatientData getPatientDataAt(int row) {
        return rowData.get(row);
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        PatientData pd = getPatientDataAt(rowIndex);
        Object value = null;
        switch (columnIndex) {
            case 0:
                value = pd.getFirstName();
                break;
            case 1:
                value = pd.getLastName();
                break;
            case 2:
                value = pd.getMHN();
                break;
        }
        return value;
    }

}

这是负责定义行和列结构,这里我打破了名字,姓氏和MHN在不同的列上,这是个人选择,如果你愿意,你可以组合名称列。

接下来,我们需要准备JTable ...

patients = new JTable(model);
patients.setShowGrid(false);
patients.setShowHorizontalLines(false);
patients.setShowVerticalLines(false);
patients.setRowMargin(0);
patients.setIntercellSpacing(new Dimension(0, 0));
patients.setFillsViewportHeight(true);
TableRowSorter<PatientTableModel> sorter = new TableRowSorter<>(model);
patients.setRowSorter(sorter);

这会设置表格,使其不会显示水平或垂直网格线,并将单元格之间的填充减少到0,使其显示为无缝的数据线...... < / p>

接下来,我们需要准备模型......

public PatientSelectionWindow(List<PatientData> ptList) {
    this.ptList = ptList;
    model.add(ptList);
    createWindow();
    activePatients();
}

您将在一分钟内看到,您不再需要ptList ......

接下来,我们需要改变过滤......

private void activePatients() {
    RowFilter<PatientTableModel, Integer> rf = new RowFilter<PatientTableModel, Integer>() {

        @Override
        public boolean include(RowFilter.Entry<? extends PatientTableModel, ? extends Integer> entry) {
            int row = entry.getIdentifier();
            PatientData pd = entry.getModel().getPatientDataAt(row);
            return pd.getActiveStatus() == 1;
        }

    };
    ((TableRowSorter) patients.getRowSorter()).setRowFilter(rf);
    setPatientCount();
}

private void inActivePatients() {
    RowFilter<PatientTableModel, Integer> rf = new RowFilter<PatientTableModel, Integer>() {

        @Override
        public boolean include(RowFilter.Entry<? extends PatientTableModel, ? extends Integer> entry) {
            int row = entry.getIdentifier();
            PatientData pd = entry.getModel().getPatientDataAt(row);
            return pd.getActiveStatus() != 1;
        }

    };
    ((TableRowSorter) patients.getRowSorter()).setRowFilter(rf);
    setPatientCount();
}

public void allPatients() {
    ((TableRowSorter) patients.getRowSorter()).setRowFilter(null);
    setPatientCount();
}

这三种方法基本上利用了RowFilter API中提供的JTable支持,这意味着您无需亲自触摸模型或更改模型或单独管理数据,这一切都得到了照顾...

然后我们最终得到以下结果......

enter image description here

看看:

了解更多详情