【问题标题】:Best way to display Components in a JTable?在 JTable 中显示组件的最佳方式?
【发布时间】:2012-02-16 14:28:23
【问题描述】:

我不是问如何在 JTable 中显示组件,因为网上有很多教程和示例。但是,我想知道解决此问题的最佳方法是什么。

例如,我遇到的大多数教程都有创建单独类的示例(主类,一个扩展JTable,一个扩展TableModel,一个扩展TableCellRenderer,等等)。但是,我发现你不能只在一个类中做到这一点,而是一种方法,只需使用以下方法:

示例代码 (SSCCE)


主要

public class Main
{
  public static void main(String[] args)
  {
    javax.swing.JFrame jf = new javax.swing.JFrame("A table with components");
    jf.setLayout(new java.awt.BorderLayout());
    jf.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
    jf.add(new TableWithCompsPanel(), java.awt.BorderLayout.CENTER);
    jf.setVisible(true);
  }
}

TableWithComps

public class TableWithCompsPanel extends java.awt.Container
{
  private Class<?> tableColumnClassArray[];
  private javax.swing.JTable jTableWithComps;
  private Object tableContentsArray[][];

  public TableWithCompsPanel()
  {
    tableContentsArray = new Object[][]
      {
        {"This is plain text",                                            new javax.swing.JButton("This is a button")    },
        {new javax.swing.JLabel("This is an improperly rendered label!"), new javax.swing.JCheckBox("This is a checkbox")}
      };
    tableColumnClassArray = new Class<?>[]{String.class, java.awt.Component.class};
    initGUI();
  }

  private void initGUI()
  {
    setLayout(new java.awt.BorderLayout());
    jTableWithComps = new javax.swing.JTable(new javax.swing.table.AbstractTableModel()
      {
        @Override public int getRowCount()
        {
          return tableContentsArray.length;
        }

        @Override public int getColumnCount()
        {
          return tableContentsArray[0].length;
        }

        @Override public Object getValueAt(int rowIndex, int columnIndex)
        {
          return tableContentsArray[rowIndex][columnIndex];
        }

        @Override public Class<?> getColumnClass(int columnIndex)
        {
          return tableColumnClassArray[columnIndex];
        }
      });
    jTableWithComps.setDefaultRenderer(java.awt.Component.class, new javax.swing.table.TableCellRenderer()
    {
      @Override public java.awt.Component getTableCellRendererComponent(javax.swing.JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
      {
        return value instanceof java.awt.Component ? (java.awt.Component)value : new javax.swing.table.DefaultTableCellRenderer();
      }
    });
    add(jTableWithComps, java.awt.BorderLayout.CENTER);
  }
}

问题


我想知道的是,如果可以在这么短的代码中完成,为什么示例会不遗余力地将其分成三个甚至更多类?我的代码在运行时是否效率较低?我可以理解将主类和具有示例 GUI 的类分开,但不明白为什么要将示例 GUI 分成几个类。

编辑:我看到很多人给出了这段代码不切实际的充分理由。如果您提供替代方案,我将更加感谢您的回答!

【问题讨论】:

  • 永远不要在 TableModel 中存储组件
  • 如果我想要 iTunes 之类的东西,其中有一个对象(歌曲)表,并且您希望用户使用复选框选择多个项目,该怎么办?最好的解决方案不是简单地将第一列单元格设为复选框吗?
  • 另外,它们没有存储在 TableModel 中,只是显示在那里。
  • 从技术上讲,您仍在使用该代码创建多个类,因为您有一个 anonymous inner class。你的new TableCellRenderer(){} 最终会被编译为Main$1.class 或类似的东西。
  • 不,第一列将是由复选框呈现和编辑的布尔值。不知道 you 的“刚刚显示”与“存储”是什么意思 - 对于 me,检查 'if (value instanceof Component)' 没有任何意义对于前者 :-) 请务必阅读并理解 @mKorbel 在第 3 条中链接到的教程章节

标签: java swing jtable jcomponent performance


【解决方案1】:

为了提高内存效率,TableModel 以尽可能简单的方式对您要跟踪的数据进行建模。 TableCellRenderer 定义了如何在表格单元格中显示该数据。

在您的 iTunes 复选框示例中,对复选框中的信息进行建模的最简单方法是布尔值 (true/false)。存储 10,000 个 boolean 对象的集合比存储 10,000 个 JCheckBox 对象的内存效率高得多。

TableCellRenderer 然后可以存储单个JCheckBox 对象,当它被要求使用组件来绘制单元格时,它可以根据值选中/取消选中复选框并每次返回相同的组件.这样,您就不会在用户滚动表格时一遍又一遍地创建数千个 UI 组件。

【讨论】:

    【解决方案2】:

    一般来说,这么多交互组件的划分是因为设计。这个设计试图应用好的原则,比如关注点分离。您可以构建一件可以完成所有工作的大件,或者负责任地确定每个较小的部分。在后一种情况下,您的代码更适合更改,因为每个类只做一件事并且更改很多时间意味着在不破坏解决方案的一般架构的情况下涉及一两个职责。

    特别是 Swing 应用的 MVC 模式有点冗长,但试图掌握很多好的设计原则。我知道维护并不总是最简单的事情,但我必须认识到责任是非常分离的。

    样本可能很短。但他们必须坚持 Swing 的理念和架构。这就是为什么它们独立于代码大小实现相同的角色。

    这个(恕我直言)的经验法则是为每个部门找出进行部门的原因。

    性能: 如果您的代码被拆分为多个类,请不要担心。它不影响性能。其他事情(如时间复杂度)会。或者可能是对组件的一些不正确使用,但如果所有组件都按预期使用,一切都会好起来的。

    编辑:希望这个答案有用!如您所见,它根本不是面向摆动的......

    【讨论】:

    • 我明白这一点,这是我的第一个想法……但是单独创建两个或多个类来指定单个组件的行为似乎太多了,而且可能会占用更多驱动器空间
    • 是的。但是桌子是一个非常复杂的组件,它有很多不同的特性(所以它的设计有很多可互换的部分)。另一种解决方案是用大量的 if 和分叉来污染设计,以便在实现之间做出决定。说:我同意它可能会更简单。或者至少,有一些默认的、预构建的解决方案来满足常见需求。无论如何,由于我对 Swing 的了解有限,我无法以实际的方式支持我所说的话。 :) 只是不要担心磁盘空间或内存空间,除非该空间以二次或指数增长:)
    【解决方案3】:

    需要考虑的事项:

    • 从您的示例看来,单元格值是一个组件。这不是要为每个重要的表占用大量内存吗?
    • 在选择行时,组件涂料是否正常,是焦点等? Li>
    • 除非您的组件非常智能,否则您可能无法使单元格可编辑。

    【讨论】:

    • 1:否,因为如果您要放入 JTable 的对象不是组件,则使用默认渲染器。此外,无论如何,JTable 单元格已经是组件。 2:是 3:在这种情况下,它将是一个复选框、按钮、进度条等,无论如何,这些都是你不想编辑的东西。
    • 在某些操作系统(例如 Win7)上,当您将鼠标悬停在复选框、按钮等组件上时,它们会生成动画。为这些单元格实现一个编辑器可能会很好,这样当您将鼠标悬停在它们上时它们就像“真实”组件一样。
    • @Supuhstar 据我了解,传递给方法的值对象应该是单元格中的值而不是单元格组件。如果您使用按钮填充每个单元格,您的代码将起作用,但我怀疑它会扩展很多。
    • @Supuhstar 另外,无论如何,JTable 单元格已经是组件 那是 the-wrong-thing-to-do 2. 不在 sn -p 你在你的问题中显示
    • @kleopatra 对错,所有JTable 单元格都是Components;就是这样。 2:很抱歉,它只适用于显示组件......你说得对
    【解决方案4】:
    1. Renderer 仅用于在视觉上装饰单元格的内容,例如setFont、setForeground、setBackground、isEnable、isVisible等

    2. 不要在运行时创建、设置、更改JComponents 类型的Renderer;这是TableModel 的工作。

    3. 如果可能,使用DefaultTableModel 并利用types known to JTable 的现有渲染器。

    4. 您已经知道Object/JComponent 中的哪些type 存在于JTable 的单元格中。

    【讨论】:

    • 我还是不明白 2、3 或 4 :
    • 很抱歉延迟返回此答案。我试图解释这些重要的观点。这是一个相关的example;如果您查看编辑历史记录,您会看到我在覆盖JTable 方法而不是TableModel 方法时如何使类似错误永久存在。请注意“C3”列中的不同类型。
    猜你喜欢
    • 2017-05-26
    • 2011-12-25
    • 1970-01-01
    • 1970-01-01
    • 2021-04-28
    • 2011-09-15
    • 2010-11-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多