Лучший способ отображения компонентов в JTable?

Я не спрашиваю, КАК отобразить компонент в 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);
  }
}

Таблица с композициями

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);
  }
}

Вопрос


Что мне интересно, так это то, если это может быть выполнено в таком небольшом количестве кода, почему примеры изо всех сил стараются разделить его на три, а иногда и больше классов? Является ли мой код менее эффективным во время выполнения? Я могу понять разделение основного класса и класса, который имеет пример графического интерфейса, но не понимаю, почему вы должны разделить пример графического интерфейса на несколько классов.

Редактировать: я вижу много людей, приводящих веские причины, почему этот код непрактичен. Я был бы признателен за ваши ответы больше, если бы вы предоставили альтернативу!


person Ky Leggiero    schedule 16.02.2012    source источник
comment
никогда не храните компоненты в вашей TableModel   -  person kleopatra    schedule 16.02.2012
comment
Что, если я хочу иметь что-то вроде iTunes, где есть таблица объектов (песен) и вы хотите, чтобы пользователь выбирал несколько элементов флажками? Не будет ли лучшим решением просто установить флажки в первом столбце ячеек?   -  person Ky Leggiero    schedule 16.02.2012
comment
кроме того, они не ХРАНЯТСЯ в TableModel, а просто отображаются там.   -  person Ky Leggiero    schedule 16.02.2012
comment
Технически вы по-прежнему создаете несколько классов с этим кодом, поскольку у вас есть анонимный внутренний класс. . Ваш new TableCellRenderer(){} в конечном итоге будет скомпилирован как Main$1.class или что-то в этом роде.   -  person Kevin K    schedule 16.02.2012
comment
нет, первый столбец будет логическим, который отображается и редактируется флажками. Не знаю, что вы имеете в виду под просто отображаемым или сохраненным - для меня проверка «if (value instanceof Component)» не имеет никакого смысла для первого: -) Обязательно прочтите и поймите главу руководства, на которую ссылается @mKorbel в пункте 3.   -  person kleopatra    schedule 17.02.2012
comment
@kleopatra Под просто отображаемыми и хранимыми я имею в виду, что я храню компоненты в массиве переменных класса и просто возвращаю их с помощью TableModel   -  person Ky Leggiero    schedule 17.02.2012
comment
@KevinK Я знаю, но вопрос больше о коде, чем о компиляции   -  person Ky Leggiero    schedule 17.02.2012
comment
задача TableModel не состоит в том, чтобы предоставлять компоненты, с которыми она хочет быть отображена, а в том, чтобы смешивать проблемы, особенно делая это против ветров фреймворка.   -  person kleopatra    schedule 18.02.2012
comment
@kleopatra э-э .... Не могли бы вы сказать это менее ... загадочно?   -  person Ky Leggiero    schedule 18.02.2012
comment
криптика была введена моей клавиатурой - означала плохую идею :-) Почему вы настаиваете на том, чтобы не следовать дизайну Swing, а вместо этого бороться с ним? (вы читали учебник, не так ли?)   -  person kleopatra    schedule 18.02.2012
comment
@kleopatra Этот вопрос не о том, что такое дизайн Swing; Я склонен следовать этому в полных приложениях. Наоборот, это лучший способ проиллюстрировать, как сделать JTable визуализацию Components.   -  person Ky Leggiero    schedule 18.02.2012
comment
@Supuhstar Спасибо за полный пример. Как видите, в этой таблице трудно добиться сортировки/фильтрации. Также это излишне создает компоненты, которые могут быть проблемой, когда у вас большое количество строк. Кстати, если вы просто хотите показать компоненты в табличной форме, почему бы не использовать GridLayout?   -  person Ashwinee K Jha    schedule 18.02.2012
comment
лучше вообще этого не делать ;-)   -  person kleopatra    schedule 18.02.2012
comment
@AKJ В итоге я сделал этот XP   -  person Ky Leggiero    schedule 19.02.2012


Ответы (4)


TableModel моделирует данные, которые вы хотите отслеживать, самым простым способом для экономии памяти. TableCellRenderer определяет, как отображать эти данные в ячейке таблицы.

В вашем примере с флажком iTunes самый простой способ смоделировать информацию из флажка — это логическое значение (true/false). Гораздо эффективнее хранить набор из 10 000 объектов boolean, чем 10 000 объектов JCheckBox.

Затем TableCellRenderer может хранить один объект JCheckBox, и когда его просят указать компонент для рисования ячейки, он может установить/снять флажок в зависимости от значения и каждый раз возвращать один и тот же компонент. Таким образом, вы не будете создавать тысячи компонентов пользовательского интерфейса снова и снова, пока пользователь прокручивает таблицу.

person Kevin K    schedule 16.02.2012

В общем, разделение на такое количество взаимодействующих компонентов связано с конструкцией. Этот дизайн пытается применить хорошие принципы, такие как разделение задач. Вы можете построить одну большую вещь, которая делает все, или определить более мелкие части, каждая из которых отвечает за свою ответственность. В последнем случае ваш код больше подходит для изменения, потому что каждый класс делает только одну вещь, а изменения много времени подразумевают касание одной или двух обязанностей без нарушения общей архитектуры решения.

В частности, Swing применяет шаблон MVC, который немного многословен, но пытается уловить множество хороших принципов проектирования. Я понимаю, что это не всегда самая простая вещь в обслуживании, но я должен признать, что обязанности очень разделены.

Примеры могут быть короткими. Но они должны придерживаться философии и архитектуры Swing. Вот почему они реализуют одни и те же роли независимо от размера кода.

Эмпирическое правило для этого (ИМХО) состоит в том, чтобы найти для каждого разделения причину, по которой это разделение было сделано.

Производительность: не беспокойтесь, если ваш код разбит на несколько классов. Это не влияет на производительность. Другая вещь (например, временная сложность) делает. Или, может быть, какое-то неправильное использование компонентов, но если все используется по назначению, все будет хорошо.

Изменить: надеюсь, что этот ответ будет полезен! Как видите, он совсем не ориентирован на свинг...

person helios    schedule 16.02.2012
comment
Я понимаю это, и это была моя первая мысль... но создание отдельных двух или более классов для определения поведения одного компонента кажется слишком большим, и, вероятно, это занимает больше места на диске. - person Ky Leggiero; 16.02.2012
comment
Ага. Но стол — это очень сложный компонент и у него много разных особенностей (поэтому в его конструкции много взаимозаменяемых частей). Другим решением было бы загрязнить дизайн большим количеством «если» и бифуркаций для выбора между реализациями. Сказал, что: я согласен, это, вероятно, может быть проще. Или, по крайней мере, иметь готовые решения по умолчанию для общих нужд. Во всяком случае, я не могу поддержать то, что я говорю, на практике из-за моих ограниченных знаний о Swing. :) Просто не беспокойтесь о дисковом пространстве или пространстве памяти, если только это пространство не растет квадратично или экспоненциально :) - person helios; 17.02.2012

Что следует учитывать:

  • Из вашего примера видно, что значение ячейки является компонентом. Разве это не потребует огромной памяти для каждой нетривиальной таблицы?
  • Правильно ли отображается компонент, когда, скажем, выбрана строка, находится в фокусе и т. д.?
  • Если ваш компонент не очень умный, у вас могут возникнуть проблемы с редактируемой ячейкой.
person Ashwinee K Jha    schedule 16.02.2012
comment
1: Нет, потому что если объект, который вы хотите поместить в JTable, НЕ является компонентом, то используется средство визуализации по умолчанию. Кроме того, ячейки JTable в любом случае уже являются компонентами. 2: Да 3: В этом случае это будет что-то вроде флажка, кнопки, индикатора выполнения и т. д., которые вы в любом случае не хотите редактировать. - person Ky Leggiero; 16.02.2012
comment
В некоторых операционных системах (например, Win7) такие компоненты, как флажки, кнопки и т. д., будут анимироваться при наведении на них указателя мыши. Было бы неплохо реализовать редактор для этих ячеек, чтобы они вели себя как настоящие компоненты, когда вы наводите на них указатель мыши. - person Kevin K; 16.02.2012
comment
@Supuhstar Насколько я понимаю, объект значения, передаваемый методу, должен быть значением в ячейке, а не в компоненте ячейки. Если вы заполняете каждую ячейку, скажем, кнопкой, ваш код будет работать, но я сомневаюсь, что он сильно масштабируется. - person Ashwinee K Jha; 16.02.2012
comment
@Supuhstar Кроме того, ячейки JTable в любом случае уже являются компонентами Это неправильно 2. не во фрагменте, который вы показываете в своем вопросе - person kleopatra; 17.02.2012
comment
@kleopatra неправильно или правильно, все JTable ячейки равны Components; так оно и есть. 2: Мне жаль говорить, что это работает только для отображения компонента... Вы правы в этом вопросе - person Ky Leggiero; 17.02.2012
comment
@KevinK Было бы очень здорово! У вас есть пример кода? - person Ky Leggiero; 17.02.2012
comment
@Supuhstar Можете ли вы добавить полный пример JTable, где значение является компонентом? - person Ashwinee K Jha; 17.02.2012
comment
@Supuhstar У меня нет под рукой примера, но основную стратегию довольно просто объяснить. Во-первых, установите TableCellEditor в ячейку, которая возвращает компонент того же типа, что и средство визуализации. Затем установите MouseMotionListener на JTable и при перемещении мыши проверьте, находится ли он над ячейкой. Если это так, вызовите JTable.editCellAt(), но только если вы еще не редактируете ячейку. Если он не над ячейкой, отмените редактирование ячейки. - person Kevin K; 17.02.2012
comment
(проклятая клавиатура, борюсь со мной - так что пробуй снова) @Supuhstar все ячейки JTable являются компонентами; так оно и есть нет, просто вы сделали это неправильно ;-) - person kleopatra; 18.02.2012

  1. Renderer предназначен только для визуального оформления содержимого ячейки, например. setFont, setForeground, setBackground, isEnable, isVisible и т. д.

  2. Не создавайте, не устанавливайте, не изменяйте тип JComponents в Renderer во время выполнения; это работа для TableModel.

  3. Если возможно, используйте DefaultTableModel и используйте существующие средства визуализации для типов, известных JTable.

  4. Вы знаете, какой тип Object/JComponent присутствует в Камера JTable.

person mKorbel    schedule 16.02.2012
comment
Я до сих пор не понимаю 2, 3 или 4 :‹ - person Ky Leggiero; 16.02.2012
comment
Извините за задержку с возвращением к этому ответу. Я попытался объяснить эти важные моменты. Вот связанный пример; если вы просмотрите историю редактирования, вы увидите, как я повторял подобные ошибки при переопределении методов JTable вместо методов TableModel. Обратите внимание на разные типы из столбца C3. - person trashgod; 18.02.2012