#topicpath
----
//ここにコンテンツを記述します。
#contents

**TableViewerとは [#vff32b94]
TableViewerとは、JFaceのレイヤが提供するビューワクラスです。いわゆる表形式のデータを表示するためのウィジェットです。SWTのTableクラスのラッパになっていて、データを表示する機能やDrag&Dropやソート機構など、いろいろ便利な機能を提供してくれます。


#ref(pic01.png)

***ViewPartに配置する [#if260bfa]
ビューに配置するには、ViewpartのcreatePartControl(Compsite parent)内で
 Composite container = new Composite(parent, SWT.NONE);
 container.setLayout(new FillLayout());
 viewer = new TableViewer(container, SWT.FULL_SELECTION | SWT.BORDER
                | SWT.H_SCROLL | SWT.V_SCROLL);
とするだけです。

***カラムを設定する [#tb127cc2]
次にTableViewerにカラム(列)を定義していきます。基本的にTableViewerからSWTのTableクラスを取得して
 table = viewer.getTable();
 TableColumn tc1 = new TableColumn(table, SWT.NONE, 0);
 TableColumn tc2 = new TableColumn(table, SWT.NONE, 1);
としていけばよいのですが、Eclipseの[[TaskView:http://www.kickjava.com/src/org/eclipse/ui/views/markers/internal/TaskView.java.htm]]のソースに非常に汎用的なコーディングがあったので、それに沿ってやってみたいと思います。


ViewPartのフィールドに定義
 private final ColumnPixelData[] DEFAULT_COLUMN_LAYOUTS = {
     new ColumnPixelData(200), new ColumnPixelData(200),
     new ColumnPixelData(200) };
 private final IField[] VISIBLE_FIELDS = { new TitleField(),
     new SummaryField(), new URLField() };

ColumnPixelDataはそのカラムがリサイズ可能かとか、大きさはいくつかなどの情報を指定します。ちなみに[[ColumnPixelDataのJavaDocを下に載せときました。>Eclipse/プラグイン開発のTIPS集/TableViewer#e1b3d231]]

IFieldはヘッダ名やヘッダに表示するアイコンの情報、またビューワの各カラムにオブジェクトが渡されたときにオブジェクトを文字列に変換する変換処理、などを備えた(自作の((TaskViewのソースでは、internalなインタフェースでした。だから自分で作っちゃおう)))インタフェースです。後に出てくるので詳細は割愛します。。
 public interface IField {
   String getColumnHeaderText();
   Image getColumnHeaderImage();
   String getValue(Object obj);
   Image getImage(Object obj);
 }
これを各カラムに沿った形でOverrideすればOKです。


次に ViewPart#createPartControlで以下のinitTalbeメソッドを呼び出します。
 private void initTable(Viewer viewer){
  Table table = viewer.getTable();
  TableLayout layout = new TableLayout();
  table.setLayout(layout);
  table.setHeaderVisible(true); // ヘッダは表示する
  table.setLinesVisible(true);  // ラインは表示する
  
  ColumnLayoutData[] columnLayouts = getColumnLayouts();
  
  for (int i = 0; i < columnLayouts.length; i++) {
    layout.addColumnData(columnLayouts[i]);
    TableColumn tc = new TableColumn(table, SWT.NONE, i);
    tc.setText(VISIBLE_FIELDS[i].getColumnHeaderText());
    tc.setImage(VISIBLE_FIELDS[i].getColumnHeaderImage());
  }
 }

 private ColumnLayoutData[] getColumnLayouts() {
   ColumnPixelData[] result = new ColumnPixelData[DEFAULT_COLUMN_LAYOUTS.length];
   for (int i = 0; i < DEFAULT_COLUMN_LAYOUTS.length; i++) {
     int width = DEFAULT_COLUMN_LAYOUTS[i].width;
     //////////// ※ /////////////////
     result[i] = new ColumnPixelData(width);
   }
   return result;
 }

これでTableにTableColumnがセットされました。getColumnLayoutsについてですが、なかで新しいColumnPixelDataを作成して、んで配列ColumnPixelData[]を返しています。このままだとフィールドと同じモノが返るのでわけわかんないのですが、実は※のところに処理があります。いまんところは、あまり気にせずにいきましょう。後述します。


***ドメインオブジェクトと2つのProvider [#pbf78763]
ここまででカラムが3つあるTableViewerが構築されました。さてこのテーブルに値を表示させていくのですが、JFaceレイヤのTableViewerでは
-表示したいデータを持つドメインオブジェクト
-ドメインオブジェクトとTableViewerの橋渡しをするContentProvider
-TableViewerに値を表示するためのLabelProvider

という3種類のオブジェクトが重要です。

たとえば、ドメインオブジェクトを
 public intarface Model{
   getSummary();
   getURL();
   getTitle();
 }
とします。このオブジェクトがたとえば10個(たとえば配列models)あったときに、それをTableViewerで一行に一オブジェクト表示したいときは、直接TableViewer#setInput(models);としてしまえばOKです。後は二つのプロバイダが何とかしてくれます。

ContentProviderについてですが、実はドメインオブジェクトが配列やListの場合に関しては、JFaceでArrayContentProviderというプロバイダを提供してくれています。よってcreatePartControlで
  viewer.setContentProvider(new ArrayContentProvider());
で終わりです。これで各行に一つのModelオブジェクトを割り当ててくれます。


つぎにLabelProviderです。LabelProviderはContentProviderが各行に割り当ててくれたオブジェクト(Modelでした)を、各カラムになんと表示するか指定します。

以下のように定義しました。
 class ViewLabelProvider extends LabelProvider implements
     ITableLabelProvider {
   private final IField[] fields;
   public ViewLabelProvider(IField[] fields) {
     this.fields = fields;
   }↑ViewPartに定義したIFieldです
 
   public String getColumnText(Object obj, int index) {
     if (obj instanceof Model) { ↑indexは左から何番目のカラムか
        ↑ ContentProviderが配列から取り出して渡してくれる
       Modelelement = (Model) obj;
       return fields[index].getValue(element);
     }
     return "";
   }
 
   public Image getColumnImage(Object element, int columnIndex) {
     return null; <-イメージを使いたい場合には何かを返す
   }
 }
このクラスを作っておいて、
 viewer.setLabelProvider(new ViewLabelProvider(VISIBLE_FIELDS));
としておけばOKです。

IFieldの効能についてですが、このようなクラスがないとLabelProviderの中で、一列目だったらmodel#getTitle,二列目だったらmodel#getSummaryなどとswitchが出てきそうですが、それをポリモフィズムで何とかしてるわけです。


まとめると
-フィールドの
 ColumnPixelData[] DEFAULT_COLUMN_LAYOUTS;
 IField[] VISIBLE_FIELDS;

でTableViewerを構築
-TableViewerの行単位のデータを生成するContentProviderを定義(ArrayContentProviderで十分の場合もある)
-行単位のデータを実際に各カラムに表示するためのLabelProviderを定義。その中でIFieldを有効に使う

という感じですね。

参考~
[[プラグイン開発のTIPS集/GooglePlugin/検索結果一覧を表示するビューの作成>Eclipse/プラグイン開発のTIPS集/GooglePlugin/検索結果一覧を表示するビューの作成]]



***IMemmentoを使った、カラム幅を保存してリストアする [#if6ca96a]

[[org.eclipse.ui.IViewPartの 設定の保存とリストア>Eclipse/プラグイン開発のTIPS集/org.eclipse.ui.IViewPart(ビュー)#qcaeab86]] にまとめたのですが、IMementoインタフェースを用いることで、ビューを閉じたときの各カラムのカラム幅を覚えておいて、次回そのビューを開くときに、閉じたときのカラム幅を再現することができます((ちなみにIMementoって変な名前はデザインパタンのメメントパターンのことですね。))。


やってみましょう。EclipseにはあらかじめViewを開いたときに呼ばれる
 public void init(IViewSite site, IMemento memento);
というメソッドがあるので、それを以下のように実装しておきます。
 public void init(IViewSite site, IMemento memento) throws PartInitException {
   this.memento = memento;
   super.init(site, memento);
 }
Viewが開くときに渡されてきた引数のmementoはフィールドに持っておきます。

逆に、ビューを閉じるときには
 public void saveState(IMemento memento);
が呼ばれるので、そのメソッドを以下のように実装しておきます。

 public void saveState(IMemento memento) {
   super.saveState(memento);
   saveColumnWidth(memento);
 }
このメソッドの引数のmementoは、次にこのViewを開いたときにinitで渡されてくるmementoなので、このmementoのなかに次回再現したい情報をセットしておけばよいと言うわけです。上では、savaColumnWidthてメソッドの中で、以下のように実装しておきます。
 private void saveColumnWidth(IMemento memento) {
   TableColumn[] columns = viewer.getTable().getColumns();
   for (int i = 0; i < columns.length; i++) {
     memento.putInteger(TAG_COLUMN_WIDTH + i, columns[i].getWidth());
   }                      ↑適当な文字列           ↑現在のカラム幅
 }
現在の各カラム幅を「TAG_COLUMN_WIDTH + i」というキー値でmementoにセットしていっています。このキー値を使って、次回ビューを開いたときはキー値で取り出される(前回の)カラム幅をTableにセットしていきます。

さて、先ほど後述するからとりあえず消しておく、といっていた、

 private ColumnLayoutData[] getColumnLayouts() {
   ColumnPixelData[] result = new ColumnPixelData[DEFAULT_COLUMN_LAYOUTS.length];
   for (int i = 0; i < DEFAULT_COLUMN_LAYOUTS.length; i++) {
     int width = DEFAULT_COLUMN_LAYOUTS[i].width;
     //////////// ※ /////////////////
     result[i] = new ColumnPixelData(width);
   }
   return result;
 }
に修正をかけていきます。これは、カラム幅を保持したクラス、ColumnLayoutDataを取得するメソッドですが、これを、フィールドのmementoから値がとれる場合はそっちを使用する、という処理を追加していきます。※の箇所を以下のように変更します。
 private ColumnLayoutData[] getColumnLayouts(ColumnPixelData[] data) {
   ColumnPixelData[] result = new ColumnPixelData[data.length];
   for (int i = 0; i < data.length; i++) {
     int width = data[i].width;
     if (memento != null) {
       // メメントから、前回の幅を取得している
       Integer widthInt = memento.getInteger(TAG_COLUMN_WIDTH + i);
       if (widthInt != null && widthInt.intValue() > 0) {
         width = widthInt.intValue();
       }
     }
     result[i] = new ColumnPixelData(width);
   }
   return result;
 }
これで、''あらかじめ、初回のデフォルトのカラム幅はフィールドに定義しておいたけど、ユーザが使用してmementoにカラム幅が保存されているときには、そっちのカラム幅を使用するよ''というコーディングができあがりました。














***ソート処理 [#fb034da8]
***保存処理 [#kf99e51e]
***DnD [#g8954bdb]



**TIP集 [#lb5222ff]
***どのカラムをクリックしてもその行を選択状態にする [#lee5a252]
 viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL
                | SWT.V_SCROLL|SWT.FULL_SELECTION);

SWT.FULL_SELECTION がポイントです((クリックに反応して選択した行を変えようと思って、いろんなリスナを探してしまった))。


***プロパティ値を設定する。 [#nc785d79]
 String[] properties = new String[] { "text", "check", "color", "combo" };
 // カラム・プロパティの設定
 viewer.setColumnProperties(properties);
は
 ICellModifier#getValue(Object element, String property)
のproperty値になります。

ICellModifierなどについては[[編集機能付きテーブルとは>Eclipse/プラグイン開発のTIPS集/編集機能付きテーブル]]をご参照ください。

***参考1 ColumnPixelDataのJavaDocコメント [#e1b3d231]
 /**
  * Creates a resizable column width of the given number of pixels.
  *
  * @param widthInPixels the width of column in pixels
  */
 public ColumnPixelData(int widthInPixels) {
   this(widthInPixels, true, false);
 }
 
 /**
  * Creates a column width of the given number of pixels.
  *
  * @param widthInPixels the width of column in pixels
  * @param resizable <code>true</code> if the column is resizable,
  *   and <code>false</code> if size of the column is fixed
  */
 public ColumnPixelData(int widthInPixels, boolean resizable) {
 this(widthInPixels, resizable, false);
 }
 
 /**
  * Creates a column width of the given number of pixels.
  * 
  * @param widthInPixels
  *      the width of column in pixels
  * @param resizable
  *      <code>true</code> if the column is resizable, and
  *      <code>false</code> if size of the column is fixed
  * @param addTrim
  *      <code>true</code> to allocate extra width to the column to
  *      account for trim taken by the column itself,
  *      <code>false</code> to use the given width exactly
  * @since 3.1
  */
 public ColumnPixelData(int widthInPixels, boolean resizable, boolean addTrim) {
   super(resizable);
   Assert.isTrue(widthInPixels >= 0);
   this.width = widthInPixels;
 this.addTrim = addTrim;
 }


***各カラムの背景色や文字の色、フォントなどを設定する [#x42f5bdc]
いろいろサイトを探して、みつかんねえなあ直接Tableをいじるしかねーかなと思ってたら、Eclipseをのソースを調べていて、見つけました。TableViewerなどにセットするLabelProviderにITableColorProvider、ITableFontProviderをimplementsさせればよいのでした。しかし、これくらいの日本語情報がないとなると、、、、。。がんばんないとね。







----
この記事は
#vote(おもしろかった[19],そうでもない[0])
#vote(おもしろかった[20],そうでもない[0])

#comment
#topicpath


SIZE(10){現在のアクセス:&counter;}


トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS