Entitätsübersicht erstellen

Listen-Klasse

Genaugenommen ist die Übersichtsklasse kein View, sondern ein Viewcontroller, aber wir wollen ja nicht päpstlicher sein, als der Papst - schließlich bleiben die "großen" Benutzeraktionen der Kontrollerklasse (der Anwendung) vorbehalten. Grundlage für eine Übersichtsanzeige ist die Klasse AbstractTableView. Wir erstellen als erstes eine neue Klasse, die von AbstractTableView erbt:

public class AddressTableView extends AbstractTableView<Addresses> {
    protected static final String    PREFIX = AddressesTableView.class.getSimpleName();
    protected static RendererFactory rf;

    public AddressTableView(List<Addresses> list,
                            int selectionMode,
                            List<Addresses> initialSelection) {
        super(list, selectionMode, initialSelection);
        setTableFormat(new AddressTableFormat());
        setSelectionMode(selectionMode);
    }
    
    @Override
    protected JComponent buildPanel() {
        return createTable();
    }
} 

Für eine einfache Übersicht reicht es, bei buildPanel den Rückgabewert von createTable weiter zu reichen.

Wer weiterführende Beispiele sucht, möge sich bitte das Projekt VdrAssistant anschauen. Dort gibt es weitere Übersichten und komplexerer Filter (z.B. EpgManager).

Format

Wie der obige Konstruktor schon andeutete, braucht selbst die einfachste Variante einer Übersicht zumindest ein Tabellenformat. Mit dem Format werden die Spalten der Übersicht konfiguriert. Erweiterte Übersichten können noch Filter und vieles mehr enthalten. Doch dazu später. Eine solche Formatklasse könnte wie folgt aussehen:

protected class AddressTableFormat extends AbstractTableFormat {
     private final String[] columnNames  = { "type", "city", "street",
                                             "zip", "state", "country" };
     private final int[]    width        = { 20, 200, 200, 90, 150, 150 };

     public AddressTableFormat() {
         super(PREFIX);
     }

     @Override
     protected String[] getColumnNames() {
         return columnNames;
     }

     @Override
     protected int getColumnWidth(int idx) {
         if (idx < width.length) return width[idx];
         return 0;
     }

     @Override
     protected int getMaxColWidth(int idx) {
         if (idx < width.length) return width[idx];
         return 0;
     }

     @Override
     protected void setColumnRenderer(TableColumn tc, int colIndex) {
        if (colIndex == 0)
           tc.setCellRenderer(rf.getEnumRenderer(JLabel.RIGHT));
     }

     @Override
     protected void setTypeRenderers(JTable table) {
         // ask for factory service here
         rf = (RendererFactory) 
              ApplicationServiceProvider.getService(RendererFactory.class);
     }

     @Override
     public Object getColumnValue(Addresses instance, int idx) {
        switch (idx) {
        case 0: return instance.getType();
        case 1: return instance.getCity();
        case 2: return instance.getStreet();
        case 3: return instance.getZip();
        case 4: return instance.getState();
        case 5: return instance.getCountry();        
        }
        return null;
     }
} 
Konstruktor
Der Konstruktor muss der Elternklasse einen Namen übergeben. Dieser gilt u.a. als Prefix für die Textersetzung der Spaltenüberschriften.
getColumnNames
gibt die Spalten-Bezeichner zurück. Diese Werte, zusammen mit dem Bezeichner der Übersicht ergeben den Schlüssel für die Sprachtexte.
getColumnWidth
gibt die Spaltenbreite für den übergebenen Index zurück. Diese Spaltenbreite ist die bevorzugte Breite, die während der Laufzeit verändert werden kann.
getMaxColWidth
mit diesem Rückgabewert kann die maximale Spaltenbreite eingestellt werden. Der Wert kann dem Standardwert entsprechen, muss aber nicht
getMinColWidth (optional)
Hier kann die Mindestbreite einer Spalte festgelegt werden (Vorgabe ist 0)
setColumnRenderer
Hier kann ein eigener Renderer eingesetzt werden, wenn der Datentyp vom Standard nicht zufriedenstellend wieder gegeben wird.
setTypeRenderers
wird vor der Konfiguration der Übersicht aufgerufen, kann also auch für Initialisierungen verwendet werden.

Filter

Filter dienen dazu, die angezeigten Sätze der Übersicht zu reduzieren, ohne dafür auf die Datenbank zuzugreifen. Glazedlists bringt diese Funktionalität von Haus aus mit, d.h. der entsprechende Aufwand ist vernachlässigbar.

Ein Filter besteht aus einem Eingabe-Element, über das der Anwender die Filterkriterien verändern kann (selbstverständlich setzen wir hier wieder auf JGoodies). Dann natürlich aus der Klasse, die den Filter auf die Datensätze der Liste anwendet - zu guter Letzt muss alles noch aktiviert werden.

Fangen wir mit dem Eingabe-Element an:

public class AddressesFilter implements ListFilterComponent<Addresses> {
   private JTextField                            filter;
   private TextComponentMatcherEditor<Addresses> me;


   public RecordingFilter() {
      filter = new JTextField(20);
      me     = new TextComponentMatcherEditor<Addresses>(
                              filter, new AddressesFilterator());
   }


   public JComponent getFilterPane() {
      FormLayout layout = new FormLayout("right:max(25dlu;pref), 3dlu, default:grow");
      DefaultFormBuilder builder = new DefaultFormBuilder(layout);
      MessageSource msgSource = (MessageSource) 
                                ApplicationServiceProvider.getService(MessageSource.class);

      builder.setDefaultDialogBorder();
      builder.append(msgSource.getMessage(PREFIX + "filter", 
                                          null, 
                                          PREFIX + "filter", 
                                          null),
                     filter,
                     true);

      return builder.getPanel();
   }


   public MatcherEditor<Addresses> getMatcherEditor() {
      return me;
   }
} 

Fehlt noch die Klasse, die den Filter auf die Liste der Übersicht anwendet. Bei der Funktion getFilterStrings muss nur angegeben werden, auf welche Attribute der Filter angewendet werden soll.

protected class AddressesFilterator implements TextFilterator<Addresses> {
   public void getFilterStrings(List baseList, Addresses instance) {
      baseList.add(item.getCity());
      baseList.add(item.getStreet());
      baseList.add(item.getState());
      baseList.add(item.getCountry());
   }
} 

Um den Filter zu verwenden, müssen wir die Übersichtsklasse etwas erweitern:

public class AddressTableView extends AbstractTableView<Addresses> {
    protected static final String    PREFIX = AddressesTableView.class.getSimpleName();
    protected static RendererFactory rf;
    private AddressesFilter          filter;

    public AddressTableView(List list,
                            int selectionMode,
                            List initialSelection) {
        super(list, selectionMode, initialSelection);
        setTableFormat(new AddressTableFormat());
        setSelectionMode(selectionMode);
        filter = new AddressesFilter();
        addFilter(filter);
    }
    
    @Override
    protected JComponent buildPanel() {
       JPanel rv = new JPanel();

       rv.setLayout(new BorderLayout());
       rv.add(filter.getFilterPane(), BorderLayout.NORTH);
       rv.add(createTable(), BorderLayout.CENTER);

       return rv;
    }
} 

Zusammenfassung

Hier nochmal die gesamte Übersichtklasse komplett:

public class AddressTableView extends AbstractTableView<Addresses> {
   public class AddressesFilter implements ListFilterComponent<Addresses> {
      private JTextField                            filter;
      private TextComponentMatcherEditor<Addresses> me;


      public RecordingFilter() {
         filter = new JTextField(20);
         me     = new TextComponentMatcherEditor<Addresses>(
                                 filter, new AddressesFilterator());
      }


      public JComponent getFilterPane() {
         FormLayout layout = new FormLayout("right:max(25dlu;pref), 3dlu, default:grow");
         DefaultFormBuilder builder = new DefaultFormBuilder(layout);
         MessageSource msgSource = (MessageSource) 
                                   ApplicationServiceProvider.getService(MessageSource.class);

         builder.setDefaultDialogBorder();
         builder.append(msgSource.getMessage(PREFIX + "filter", 
                                             null, 
                                             PREFIX + "filter", 
                                             null),
                        filter,
                        true);

         return builder.getPanel();
      }


      public MatcherEditor<Addresses> getMatcherEditor() {
         return me;
      }
   }
   protected class AddressesFilterator implements TextFilterator<Addresses> {
      public void getFilterStrings(List baseList, Addresses instance) {
         baseList.add(item.getCity());
         baseList.add(item.getStreet());
         baseList.add(item.getState());
         baseList.add(item.getCountry());
      }
   }
   protected class AddressTableFormat extends AbstractTableFormat {
       private final String[] columnNames  = { "type", "city", "street",
                                               "zip", "state", "country" };
       private final int[]    width        = { 20, 200, 200, 90, 150, 150 };

       public AddressTableFormat() {
           super(PREFIX);
       }

       @Override
       protected String[] getColumnNames() {
           return columnNames;
       }

       @Override
       protected int getColumnWidth(int idx) {
           if (idx < width.length) return width[idx];
           return 0;
       }

       @Override
       protected int getMaxColWidth(int idx) {
           if (idx < width.length) return width[idx];
           return 0;
       }

       @Override
       protected void setColumnRenderer(TableColumn tc, int colIndex) {
          if (colIndex == 0)
             tc.setCellRenderer(rf.getEnumRenderer(JLabel.RIGHT));
       }

       @Override
       protected void setTypeRenderers(JTable table) {
           // ask for factory service here
           rf = (RendererFactory) 
                ApplicationServiceProvider.getService(RendererFactory.class);
       }

       @Override
       public Object getColumnValue(Addresses instance, int idx) {
          switch (idx) {
          case 0: return instance.getType();
          case 1: return instance.getCity();
          case 2: return instance.getStreet();
          case 3: return instance.getZip();
          case 4: return instance.getState();
          case 5: return instance.getCountry();        
          }
          return null;
       }
   }
   protected static final String    PREFIX = AddressesTableView.class.getSimpleName();
   protected static RendererFactory rf;
   private AddressesFilter          filter;

   public AddressTableView(List<Addresses> list,
                           int selectionMode,
                           List<Addresses> initialSelection) {
      super(list, selectionMode, initialSelection);
      setTableFormat(new AddressTableFormat());
      setSelectionMode(selectionMode);
      filter = new AddressesFilter();
      addFilter(filter);
   }
    
   @Override
   protected JComponent buildPanel() {
      JPanel rv = new JPanel();

      rv.setLayout(new BorderLayout());
      rv.add(filter.getFilterPane(), BorderLayout.NORTH);
      rv.add(createTable(), BorderLayout.CENTER);

      return rv;
   }
}