【问题标题】:How to create a custom JTable?如何创建自定义 JTable?
【发布时间】:2014-08-05 08:44:29
【问题描述】:

我正在用 Java 制作一个聊天应用程序。他们可以添加朋友并与他们聊天。

这是我的加好友JFrameidea:

我试图用谷歌搜索Multi jpanel in one jscrollpane,但我什么也没找到。我最终得到了自定义JTable。我想创建一个自定义JTable,在每个插槽的不同位置有JLabels。用户只需在JTable中选择一个位置,就可以使用下方的JButton与他们聊天。

是否有可能在 Java 中做到这一点。是的,请分享您的想法。谢谢。

【问题讨论】:

  • 阅读TableCellRenderergetTableCellRendererComponent 可以返回任何组件,甚至是容器。
  • 同意@PeterMmm,您需要了解如何为每个单元格自定义渲染。
  • Here 是将ProgressBars 添加到表格单元格的帖子
  • @PeterMmm 包括 JPanel?
  • JPanel 扩展 JComponent,JComponent 扩展容器,容器扩展组件,是的。

标签: java jtable


【解决方案1】:

这是一种完全不同的方法,既不使用表格也不使用列表。它使用面板作为在另一个面板内排列为列表的项目。此解决方案不需要任何进一步修改即可将任何事件委托给底层控件。事件是本地处理的。这个想法来自this post。我现在将逻辑分为两部分。

JPanelList 类

这个类是一个JPanel 并维护一个这样的列表。可以使用addPaneladdPanels 方法添加ATM。

public class JPanelList extends JPanel {
    private static final long serialVersionUID = 1L;
    private JPanel mainList;
    private List<JPanel> panels = new ArrayList<JPanel>();
    public JPanelList() { this(new ArrayList<JPanel>()); }
    public JPanelList(List<JPanel> panels) {
        setLayout(new BorderLayout());
        mainList = new JPanel(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        gbc.weightx = 1;
        gbc.weighty = 1;
        mainList.add(new JPanel(), gbc);
        add(new JScrollPane(mainList));
        addPanels(panels);
    }
    public void addPanels(List<JPanel> panels) {
        for (JPanel panel : panels)
            addPanel(panel);
    }
    public void addPanel(JPanel panel) {
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        gbc.weightx = 1;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        mainList.add(panel, gbc, 0);
        panels.add(panel);
        validate();
        repaint();
    }
}

带有main函数的JListFrame测试类

public class JPanelListFrame {
    private JFrame frame;
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    JPanelListFrame window = new JPanelListFrame();
                    window.frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
    public JPanelListFrame() {
        frame = new JFrame();
        frame.setBounds(100, 100, 210, 192);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        final JPanelList panelList = new JPanelList();
        frame.getContentPane().add(panelList, BorderLayout.CENTER);
        JButton btnAddMypanel = new JButton("Add MyPanel");
        btnAddMypanel.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                // just add a MyPanel with a Value containing a
                // "asdf" and a random boolean
                panelList.addPanel(new MyPanel(new Value("asdf",
                        (int) (2 * Math.random()) % 2 == 0)));
            }
        });
        panelList.add(btnAddMypanel, BorderLayout.SOUTH);
    }
}

此解决方案的一个缺点是丢失了选择机制。选择元素时是一个关键要求,可能是JList可能更合适。另一件事是,这些项目不会被渲染,因为它们会在 JListJTable 中渲染,例如使用花哨的边框。这可以通过在将面板添加到JPanelListmainList 之前以某种方式添加边框装饰器来解决。

【讨论】:

  • 非常感谢!谢谢!
  • @Jeremy 是的,滚动窗格将证明滚动条的合理性。但是,如果您希望内部面板在宽度方面填充整个空间,则需要付出一些额外的努力。当将整个窗口的大小调整为小于内部面板时,我添加了一张实际行为的图片
  • 谢谢 :D(字数限制)
【解决方案2】:

这是一个建议。但它还包含一些缺陷

  • 尺寸传播是固定的(可能是更好的基于客户端或服务器的尺寸)
  • 没有事件委托给底层Component
  • 性能,因为重量级面板实例经常在绘制循环中创建

但这是如何做到的。代码被分成几部分。

一个值类

只是一个简单的类来表示面板内每个单元格的日期。

class Value {
    public final String text;
    public final boolean flag;
    public Value(String text, boolean flag) {
        this.text = text;
        this.flag = flag;
    }
}

我的个人小组课

可以在 gui 编辑器(如 google 的窗口构建器)中对表示进行建模。此面板使用该值并相应地显示它。

public class MyPanel extends JPanel {
    public MyPanel(Value v) {
        JLabel lblNewLabel = new JLabel(v.text);
        add(lblNewLabel);
        JCheckBox chckbxSomeValue = new JCheckBox("some value");
        chckbxSomeValue.setSelected(v.flag);
        add(chckbxSomeValue);
    }
}

表格单元格渲染器类

只返回一些显示所需值的面板实例。

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

class MyPanelCellRenderer implements TableCellRenderer {
    @Override
    public Component getTableCellRendererComponent(JTable table, Object value,
            boolean isSelected, boolean hasFocus, int row, int column) {
        return new MyPanel((Value)value); // maybe performance problem
    }   
}

自定义表格模型

import javax.swing.table.DefaultTableModel;

class MyTableModel extends DefaultTableModel {
    public MyTableModel() {
        super(new Object[][] {
                        new Object[] { 1, new Value("asdf", true) },
                        new Object[] { 2, new Value("qwer", false) } },
                        new String[] {"Id", "MyPanel" });
    }

    Class[] columnTypes = new Class[] { Integer.class, Value.class };

    MyTableModel(Object[][] data, Object[] columnNames) {
        super(data, columnNames);
    }

    public Class getColumnClass(int columnIndex) {
        return columnTypes[columnIndex];
    }
}

一个框架类

import java.awt.BorderLayout;

public class MyFrame {
    JFrame frame;
    private JTable table;

    public MyFrame() {
        frame = new JFrame();
        frame.setBounds(100, 100, 450, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        table = new JTable();
        table.setModel(new MyTableModel());
        table.setFillsViewportHeight(true);
        table.getColumnModel()
            .getColumn(1)
            .setCellRenderer(new MyPanelCellRenderer());
        table.setRowHeight(40); // static sizing

        JScrollPane scrollPane = new JScrollPane();
        frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
        scrollPane.setViewportView(table);
    }
}

主要功能

import java.awt.EventQueue;

public class MyApp {
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    MyFrame window = new MyFrame();
                    window.frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

最终结果

面板MyPanel是使用eclipse和google的window builder创建的

【讨论】:

  • 哇,谢谢。我只需要用我自己的权利替换MyPanel 吗?朋友的名字存储在客户的计算机中。客户端从服务器获取数据后,是否可以设置JLabel的文本?
  • @Jeremy #1 是的,理论上您只需将面板类型 MyPanel 替换为您的面板类型。但正如我所说,该解决方案仍然存在一些缺陷。 #2 您可以修改面板类以采用适当的设置器来设置值。
  • @Jeremy 毕竟我问自己,你真的需要一张桌子吗?难道不是您只有一个与各个面板相关联的项目列表吗?或者您出于某种原因需要表格的列?如果不考虑改用 JList ;)
  • 我希望结果看起来完全像 i.stack.imgur.com/mm1VN.png 。我认为唯一的解决方案是JTable。 :3
  • @Jeremy 但是您没有使用该图片中的任何列。例如。在我的实现中,有两列,一列用于行的“Id”,另一列用于名为“MyPanel”的面板。因此,如果您不需要列,那么它也可以通过列表进行管理,还是我错了??
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-16
  • 2015-12-08
  • 2016-07-10
  • 1970-01-01
相关资源
最近更新 更多