Eclipse Plug-in Development:Beginner's Guide(Second Edition)
上QQ阅读APP看书,第一时间看更新

Time for action – viewing time zones in tables

To display the time zones in tabular form, a new view will be created called Time Zone Table View.

  1. Right-click on the com.packtpub.e4.clock.ui project and navigate to Plug-in Tools | Open Manifest. Open the Extensions tab and right-click on the org.eclipse.ui.views, followed by navigating to New | e4view and filling in the following:
    1. ID: com.packtpub.e4.clock.ui.views.TimeZoneTableView
    2. Name: Time Zone Table View
    3. Class: com.packtpub.e4.clock.ui.views.TimeZoneTableView
    4. Category: com.packtpub.e4.clock.ui
    5. Icon: icons/sample.gif
  2. The plugin.xml should now contain:
    <e4view
     category="com.packtpub.e4.clock.ui"
     class="com.packtpub.e4.clock.ui.views.TimeZoneTableView"
     icon="icons/sample.gif"
     id="com.packtpub.e4.clock.ui.views.TimeZoneTableView"
     name="Time Zone Table View"
     restorable="true">
    </e4view>
  3. Create the class using the editor's short-cuts to create a new class TimeZoneTableView in the com.packtpub.e4.clock.ui.views package, or with the new class wizard. Once the view is created, add an empty TableViewer, and use an ArrayContentProvider with the set of available ZoneIds:
    package com.packtpub.e4.clock.ui.views;
    import javax.annotation.PostConstruct;
    import org.eclipse.e4.ui.di.Focus;
    public class TimeZoneTableView {
      private TableViewer tableViewer;
      @PostConstruct
      public void create(Composite parent) {
        tableViewer = new TableViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL);
        tableViewer.getTable().setHeaderVisible(true);
        tableViewer.setContentProvider(
         ArrayContentProvider.getInstance());
        tableViewer.setInput(ZoneId.getAvailableZoneIds());
      }
      @Focus
      public void focus() {
        tableViewer.getControl().setFocus();
      }
    }
  4. Run the Eclipse instance, and a one-dimensional list of time zones will be shown in the Time Zone Table View:
  5. Convert the array of String instances to an array of ZoneId instances and set that as the input:
    // tableViewer.setInput(ZoneId.getAvailableZoneIds());
    tableViewer.setInput(ZoneId.getAvailableZoneIds() // get ids
      .stream().map(ZoneId::of).toArray());
  6. The table shows a list of the ZoneId objects. That's because there is no LabelProvider, so they're just being rendered with their toString() representation. Because a table has multiple columns, a TableViewer has multiple TableViewerColumn instances. Each one represents a column in the Table, and each has its own size, title, and label provider. Creating a new column often involves setting up standard features (such as the width) as well as hooking in the required fields to display. To make it easy to reuse, create an abstract subclass of ColumnLabelProvider called TimeZoneColumn (in the com.packtpub.e4.clock.ui.internal package) with abstract getText and getTitle methods, and concrete getWidth and getAlignment methods:
    public abstract class TimeZoneColumn extends ColumnLabelProvider {
      public abstract String getText(Object element);
      public abstract String getTitle();
      public int getWidth() {
        return 250;
      }
      public int getAlignment() {
        return SWT.LEFT;
      }
    }
  7. Add a helper method addColumnTo to the TimeZoneColumn class, which makes it easier to add it to a viewer:
    public TableViewerColumn addColumnTo(TableViewer viewer) {
      TableViewerColumn tableViewerColumn =
       new TableViewerColumn(viewer, SWT.NONE);
      TableColumn column = tableViewerColumn.getColumn();
      column.setMoveable(true);
      column.setResizable(true);
      column.setText(getTitle());
      column.setWidth(getWidth());
      column.setAlignment(getAlignment());
      tableViewerColumn.setLabelProvider(this);
      return tableViewerColumn;
    }
  8. Now create a custom subclass TimeZoneIDColumn in the same package that returns the ID column for a TimeZone:
    public class TimeZoneIDColumn extends TimeZoneColumn {
      public String getText(Object element) {
        if (element instanceof ZoneId) {
          return ((ZoneId) element).getId();
        } else {
          return "";
        }
      }
      public String getTitle() {
       return "ID";
      }
    }
  9. Modify the TimeZoneTableView class, and at the end of the create method, instantiate the column and call the addColumnTo method, above the call to the setInput method:
    new TimeZoneIDColumn().addColumnTo(tableViewer);
    tableViewer.setInput(ZoneId.getAvailableZoneIds()...);

    Note

    Note that the columns need to be created before the setInput() call, as otherwise they won't display properly.

  10. Run the Eclipse instance, and show the Time Zone Table View. The ID column should be displayed on its own.
  11. To add more columns, copy the TimeZoneIDColumn class, modifying the title returned and the returned property of the associated time zone. For example, create a copy of the TimeZoneIDColumn called TimeZoneDisplayNameColumn, and modify the get method and title:
    // return ((ZoneId) element).getId();
    return ((ZoneId) element).getDisplayName(TextStyle.FULL, Locale.getDefault());
    // return "ID"; 
    return "Display Name"; 
  12. Optionally, do the same with the other properties of TimeZone, such as the offset (with getOffset()), and whether it's in daylight savings time or not (with useDaylightTime()). The columns can then be added to the table:
    new TimeZoneDisplayNameColumn().addColumnTo(tableViewer);
    new TimeZoneOffsetColumn().addColumnTo(tableViewer);
    new TimeZoneSummerTimeColumn().addColumnTo(tableViewer);
  13. Run the Eclipse instance, go to the Time Zone Table View, and the additional column(s) should be seen:

What just happened?

A TableViewer was created and multiple ColumnLabelProvider instances were added to it for displaying individual fields of an object. Subclassing ColumnLabelProvider avoids the need to use anonymous inner classes, and it gives a helper function which can be used to create and wire in the column (with specified title and width) while delegating those properties to the concrete subclasses of TimeZoneColumn. This avoids the need for tracking columns by ID.

For specific customizations of the columns, the underlying SWT Column is used to set functionality required by the application, including allowing the column to be movable with setMovable(true) and resizable with setResizable(true). Similarly, table-wide operations (such as showing the header) are done by manipulating the underlying SWT Table and invoking setHeaderVisible(true).

It's important to note that the columns of the tree viewer are calculated when the setInput method is called, so columns that are added after this line may not show properly. Generally the setInput should be left until the end of the table's construction.

Pop quiz: understanding tables

Q1. How are a column's headers enabled in a TableViewer?

Q2. What is a TableViewerColumn for?

Q3. What standard content provider can be used with a TableViewer?

Q4. What's the difference between a TableViewerColumn and a TableColumn?