// 下階層用テンプレート
#topicpath
----
//ここにコンテンツを記述します。
#contents

いよいよ検索結果一覧を表示するビューを作成します。とりあえず
 nu.mine.kino.plugin.google.ui.views.GoogleResultView
としました。
**概要 [#z69fd4c1]
さて前回はGoogleの検索機能を呼び出したり、スペルミスを変更するダイアログだったりを作成しました。今回はその検索結果を表示するためのビューを作成していきたいと思います。プロジェクトは引き続き「nu.mine.kino.plugin.google.ui」を使用します。

***plugin.xml [#mb316f89]
plugin.xml(抜粋)は以下のようになりました。
 <extension
    point="org.eclipse.ui.views">
   <category
     name="Googleプラグイン"
     id="nu.mine.kino.plugin.google"/>
   <view
     class="nu.mine.kino.plugin.google.ui.views.GoogleResultView"
     icon="icons/icon.gif"
     category="nu.mine.kino.plugin.google"
     name="Google検索結果"
     id="nu.mine.kino.plugin.google.ui.views.GoogleResultView"/>
 </extension>
**plugin.xml、MANIFEST.MFを記述する [#wc09e01d]

「META-INF/MANIFEST.MF」をマニフェストエディタで開き、[拡張]タブを選択して[追加]をクリックします。すると、拡張ポイントを選択するダイアログが開くので、「org.eclipse.ui.views」を選択し[終了]をクリックします。

***ウィジェットを配置していく [#m237c183]
どんどんウィジェットを配置していきます。とりあえず、こんな感じになりました。
#ref(view.gif)

#ref(prototype.png)
次に、この「org.eclipse.ui.views」を右クリックして[新規]−[view]を選択します。下に要素が追加されました。右側にはidやクラス名を指定する画面が表示されているので、以下のように指定します。

***ColumnLayoutDataでカラムの設定をする [#ad2187b5]
SWTだけではなくて、JFaceも使うときはカラムの制御を[[ColumnLayoutData:http://www.eclipse.org/documentation/html/plugins/org.eclipse.platform.doc.isv/doc/reference/api/org/eclipse/jface/viewers/ColumnLayoutData.html]]でやるのがスマートみたいです((とりあえずEclipseのMarkerViewなどの実装はそうなってた))。実際それに習って変更します。[[ColumnLayoutData:http://www.eclipse.org/documentation/html/plugins/org.eclipse.platform.doc.isv/doc/reference/api/org/eclipse/jface/viewers/ColumnLayoutData.html]]はフィールドの幅などの情報を表現するクラスです。
|LEFT:id|LEFT:nu.mine.kino.plugin.google.ui.views.GoogleResultView|
|LEFT:name|LEFT:Google検索結果|
|LEFT:class|LEFT:nu.mine.kino.plugin.google.ui.views.GoogleResultView|
|LEFT:category|LEFT:nu.mine.kino.plugin.google|
|LEFT:icon|LEFT:icons/icon.png|

ポイントは
-フィールドにあらかじめ[[ColumnLayoutData:http://www.eclipse.org/documentation/html/plugins/org.eclipse.platform.doc.isv/doc/reference/api/org/eclipse/jface/viewers/ColumnLayoutData.html]]で「初期設定」をセットしておく
-ヘッダ名やヘッダに表示するアイコンの情報、またビューワの各カラムにオブジェクトが渡されたときにオブジェクトを文字列に変換する変換処理、などを備えた自作のクラス[[IField:http://kino.mine.nu/cgi-bin/viewvc.cgi/nu.mine.kino.plugin.google.ui/source/nu/mine/kino/plugin/google/ui/views/fields/IField.java]]を作成し、それをフィールドに保持しておく
-画面表示時に、mementoを参照し、mementoから表示幅を取得してみる、取得できなかったらフィールドの「初期設定」を使う、

というところですね。わけがわからないと思うので、もっと具体的に、コーディングはこんな感じになります。

-フィールド:
 // TableLayoutと一緒になって使う。各カラムの表示幅など情報を設定する。
 private final ColumnLayoutData[] DEFAULT_COLUMN_LAYOUTS = {
         new ColumnWeightData(100), new ColumnWeightData(100),
         new ColumnWeightData(100) };
 
 private final IField[] VISIBLE_FIELDS = { new TitleField(),
            new SummaryField(), new URLField() };
カテゴリーなども追加して結局plugin.xmlには以下のコードが追加されました。

-テーブルの構築:
 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()); <-後述2
     tc.setImage(VISIBLE_FIELDS[i].getColumnHeaderImage()); <-後述2
     // tc.addSelectionListener(headerListener);
 }
   <extension
         point="org.eclipse.ui.views">
      <view
            category="nu.mine.kino.plugin.google"
            class="nu.mine.kino.plugin.google.ui.views.GoogleResultView"
            icon="icons/icon.png"
            id="nu.mine.kino.plugin.google.ui.views.GoogleResultView"
            name="Google検索結果"/>
      <category
            id="nu.mine.kino.plugin.google"
            name="Googleプラグイン"/>
   </extension>

-カラムの設定情報を、mementoかフィールドから取得するロジック(後述)
 private ColumnLayoutData[] getColumnLayouts() {
   if (memento == null) {
     return DEFAULT_COLUMN_LAYOUTS;
   }
   // ↓実際はmementoからColumnLayoutData[]を構築して返す
   return DEFAULT_COLUMN_LAYOUTS;
 }
**ビューのクラスを追加する [#w2233d70]
先のマニフェストエディタでidやクラス名を指定しましたが、classのラベルはクリックできるようになっています。

-自前のIFieldクラスからデータを生成する(後述2)
 package nu.mine.kino.plugin.google.ui.views.fields;
 
 import org.eclipse.swt.graphics.Image;
 
 /**
  * テーブル形式のビューワのカラム情報を扱うインターフェースです。
  * 
  * @author Masatomi KINO
  * @version $Revision$
  */
 public interface IField {
   /**
    * @return The text to be displayed in the column header for this field.
    */
   String getColumnHeaderText();
 
   /**
    * @return The image to be displayed in the column header for this field or
    *     <code>null<code>.
    */
   Image getColumnHeaderImage();
 
   /**
    * @param obj
    * @return The String value of the object for this particular field to be
    *     displayed to the user.
    */
   String getValue(Object obj);
 
   /**
    * @param obj
    * @return The image value of the object for this particular field to be
    *     displayed to the user or <code>null<code>.
    */
   Image getImage(Object obj);
 }
その実装クラス
 public class TitleField implements IField {
 
   public String getColumnHeaderText() {
     return "Title";
   }
 
   public Image getColumnHeaderImage() {
     // TODO 自動生成されたメソッド・スタブ
     return null;
   }
 
   public String getValue(Object obj) {
     // TODO 自動生成されたメソッド・スタブ
     return obj.toString();
   }
 
   public Image getImage(Object obj) {
     // TODO 自動生成されたメソッド・スタブ
     return null;
   }
 }
#ref(label.gif)


これをクリックするとビューのクラスを作成するウィザードが起動します。そのまま終了をクリックすると「GoogleResultView」のソースコードが作成されます。

となるような感じです。

こうすることで、
-mementoから前回の設定値などのリストアがやりやすくなる
-後に作成する、LabelProviderの実装クラスにもIFieldを渡してやることで、いやなswitch分をポリモフィズムで削除できそう
-ソートロジックなどもIFieldに実装すればポリモフィズム云々
では、そのソースコードに色々追加していこうと思います。

などの効果が得られそうです。
**ビューの本体にテーブルビューワを配置する [#jd8459ae]
まずはビューの本体を構築していきます。ビューの構築は
 public void createPartControl(Composite parent);
をOverrideすることで行います。引数のCompositeに色々ウィジェットを配置していくわけですね。

***ソースコード [#k1062fb8]
最後にソースはこんな感じです。
//$Id$
 //作成日: 2005/05/21
 package nu.mine.kino.plugin.google.ui.views;
 
 import nu.mine.kino.plugin.google.ui.views.fields.IField;
 import nu.mine.kino.plugin.google.ui.views.fields.SummaryField;
 import nu.mine.kino.plugin.google.ui.views.fields.TitleField;
 import nu.mine.kino.plugin.google.ui.views.fields.URLField;
 
 import org.eclipse.jface.action.Action;
 import org.eclipse.jface.action.IMenuManager;
 import org.eclipse.jface.action.IToolBarManager;
 import org.eclipse.jface.viewers.ArrayContentProvider;
 import org.eclipse.jface.viewers.ColumnLayoutData;
 import org.eclipse.jface.viewers.ColumnWeightData;
 import org.eclipse.jface.viewers.TableLayout;
 import org.eclipse.jface.viewers.TableViewer;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.layout.FillLayout;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Table;
 import org.eclipse.swt.widgets.TableColumn;
 import org.eclipse.ui.IMemento;
 import org.eclipse.ui.IViewSite;
 import org.eclipse.ui.PartInitException;
 import org.eclipse.ui.part.ViewPart;
 
 public class GoogleResultView extends ViewPart {
 
   // TableLayoutと一緒になって使う。各カラムの表示幅など情報を設定する。
   private final ColumnLayoutData[] DEFAULT_COLUMN_LAYOUTS = {
       new ColumnWeightData(200), new ColumnWeightData(200),
       new ColumnWeightData(200) };
 
   private final IField[] VISIBLE_FIELDS = { new TitleField(),
       new SummaryField(), new URLField() };
 
   private Table table;
 
   private Action doubleClickAction;
 
   private Action openBrowserAction;
 
   private IMemento memento;
 
   public void init(IViewSite site, IMemento memento) throws PartInitException {
     super.init(site, memento);
     this.memento = memento;
   }
 
   public void createPartControl(Composite parent) {

さて今回は下記のとおりビュー全体にTableViewerを配置することにしました。ここで出てくるTableViewerとはEclipseの「問題ビュー」のようにヘッダとデータ部分を持った表形式のビューワーです。
 public void createPartControl(Composite parent) {
     // ビューワを追加。
     Composite container = new Composite(parent, SWT.NONE);
     container.setLayout(new FillLayout());
 
     final TableViewer viewer = new TableViewer(container,
         SWT.FULL_SELECTION | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
 
     viewer = new TableViewer(container, SWT.FULL_SELECTION | SWT.BORDER);
     viewer.setContentProvider(new ArrayContentProvider());
     viewer.setInput(getViewSite());
     initTable(viewer);
 
     createActions();
     initializeToolBar();
     initializeMenu();
   }
 
   private void initTable(final TableViewer viewer) {
 }

TableViewerにカラムを追加する処理は initTable(viewer); で行っています。具体的なソースは以下の通りです。

 private void initTable(final TableViewer viewer) {
     table = viewer.getTable();
 
     TableLayout layout = new TableLayout();
     table.setLayout(layout);
     table.setHeaderVisible(true);
     table.setLinesVisible(true);
     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());
       // tc.addSelectionListener(headerListener);
         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() {
     if (memento == null) {
       return DEFAULT_COLUMN_LAYOUTS;
 }

ColumnLayoutDataはフィールドの幅などレイアウト情報を管理するクラスです((Eclipseでテーブルビューワを使うときはカラムの制御は[[ColumnLayoutData:http://www.eclipse.org/documentation/html/plugins/org.eclipse.platform.doc.isv/doc/reference/api/org/eclipse/jface/viewers/ColumnLayoutData.html]]でやるのがよいみたいです((とりあえずEclipseのMarkerViewなどの実装はそうなってた))。[[ColumnLayoutData:http://www.eclipse.org/documentation/html/plugins/org.eclipse.platform.doc.isv/doc/reference/api/org/eclipse/jface/viewers/ColumnLayoutData.html]]はフィールドの幅などの情報を表現するクラスです。))。

このColumnLayoutDataクラスは
 table = viewer.getTable();
 TableLayout layout = new TableLayout();
 table.setLayout(layout);
というようにTableViewerとTableLayoutが関連づけられており、TableLayoutに
 layout.addColumnData(columnLayouts[i]);
とすることでTableViewerに関連づいています。


さて
 ColumnLayoutData[] columnLayouts = getColumnLayouts();
で、各カラムのレイアウト情報を配列で取得していますが、現段階ではgetColumnLayouts();は
 private ColumnLayoutData[] getColumnLayouts() {
   return DEFAULT_COLUMN_LAYOUTS;
 }
とフィールドに定義されている配列をそのまま返すようになっています。ここはあとで「すでに前回の情報が保存されていたらその情報を復元する」という処理に変更します。たとえば前回使用していたときにフィールドの幅などを変更していたとして、その幅を次回に復元する、といったことをできるようにするわけですね。


**実際にカラムを追加する [#j593a6c7]
実際にカラムを追加する処理は
 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());
 }
の箇所です。先のレイアウト情報をセットするのと、ヘッダの文言やイメージデータをカラムごとにセットしています。ここで出てくる VISIBLE_FIELDS[] のインスタンスはあらかじめ
 private final IField[] VISIBLE_FIELDS = 
    { new TitleField(),new SummaryField(), new URLField() };
とフィールドに定義されているインスタンスです。


***IFieldの役割 [#k634961f]
IFieldとは
 public interface IField {
     String getColumnHeaderText();
     Image getColumnHeaderImage();
     String getValue(Object obj);
     Image getImage(Object obj);
     int compare(Object obj1, Object obj2);
 }
というように、ヘッダ名やヘッダに表示するアイコンの情報、またビューワの各カラムにオブジェクトが渡されたときにオブジェクトを文字列に変換する変換処理、などを備えたインタフェースです。実装しているクラスTitleField, SummaryField, URLField はそれぞれ

 public class TitleField implements IField {
   public String getColumnHeaderText() {return "Title";}
   -- 以下省略 --
 }

 public class SummaryField implements IField {
   public String getColumnHeaderText() {return "Summary";}
   -- 以下省略 --
 }

 public class URLField implements IField {
   public String getColumnHeaderText() {return "URL";}
   -- 以下省略 --
 }

というようにヘッダ部分の文言が定義されています。


さて上記のIFieldとその実装クラスですが、先のソースで
 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());
 }
とTableColumnのsetTextを呼び出して、各カラムにヘッダ名をセットしているわけです。IFieldはこのようにヘッダ名をセットするだけではなくTableViewerへのデータ表示時にも使用されるのですが、それについては後述します。

**検索メソッドを定義する [#qdf36c45]
続いてこのメソッドに検索メソッドを定義します。メソッド名は
 public void search(String text);
というメソッドにしました。ただ、中身については後に実装しますので、とりあえず以下のようなテスト実装としておきました。
 public void search(String text) {
     logger.debug("search(String) - start");
     try {
         String key = GooglePlugin.getDefault().getPreferenceStore()
                 .getString(PreferenceConstants.GOOGLE_KEY);
         GoogleSearchResult result = GoogleCorePlugin.getDefault().search(key, text);
         ResultElement[] resultElements = result.getResultElements();
         logger.debug("search(String)の検索結果件数は "
                       + resultElements.length + " 件でした。");
         viewer.setInput(resultElements);
     } catch (CoreException e) {
         logger.error("search(String)", e);
     }
     // ↓実際はmementoからColumnLayoutData[]を構築して返す
     return DEFAULT_COLUMN_LAYOUTS;
   }
 
   private void createActions() {
 
     openBrowserAction = new Action("サイトにアクセスする") {
       public void run() {
       }
     };
 
     doubleClickAction = new Action("") {
       public void run() {
       }
     };
   }
 
   private void initializeToolBar() {
     IToolBarManager tbm = getViewSite().getActionBars().getToolBarManager();
   }
 
   private void initializeMenu() {
     IMenuManager manager = getViewSite().getActionBars().getMenuManager();
   }
 
   public void setFocus() {
   }
 
     logger.debug("search(String) - end");
 }

***スクリーンショット [#ec539a86]
ここまででビューにはTableViewerが配置されていて、そのTableViewerにはTitle,Summary,URLという3つのカラムがあり、検索メソッドがテスト実装されている、ということになりました。


**スクリーンショット [#e6fb2426]
実際にビューを表示してみると、こんな感じです。
#ref(view.png)

次は [[検索ロジックをビューに実装>Eclipse/プラグイン開発のTIPS集/GooglePlugin/実際の検索ロジックをビューに実装]] します。
#ref(result.gif)

まだTitleのカラムにオブジェクトのハッシュコードが表示されるのみですが、これは後に修正していきます。



さて実際にビューを生成して検索メソッドを呼び出す方法ですが、例によってJUnitを用いることができます。


**JUnitを使ってビューを生成する [#p7b01ebc]
テスト用のプロジェクトは前回の「nu.mine.kino.plugin.google.ui.test」を用います。今回も外部のテストプラグインからクラスを呼び出すので、「nu.mine.kino.plugin.google.ui」プラグインの外部に公開するパッケージに
- nu.mine.kino.plugin.google.ui.views

を追加しました。

#ref(export.gif)

前回と同様、nu.mine.kino.plugin.google.ui.views.GoogleResultView クラスを選択してJUnitのテストクラスを「nu.mine.kino.plugin.google.ui.test」プロジェクトに生成します。JUnitのテストクラスはnu.mine.kino.plugin.google.ui.views.GoogleResultViewTest です。テストクラスには以下のメソッドを実装しました。

 public void testGoogleResultView() {
   try {
     GooglePlugin.getDefault().getPreferenceStore().setValue(
         PreferenceConstants.GOOGLE_KEY, "xxxxxxxxxx");
     IWorkbenchPage page = PlatformUI.getWorkbench()
         .getActiveWorkbenchWindow().getActivePage();
     GoogleResultView view = (GoogleResultView) page
         .showView(GoogleResultView.ID);
     view.search("eclipse");
   } catch (PartInitException e) {
     e.printStackTrace();
   }
 }

パッケージ・エクスプローラで[GoogleResultViewTest]を選択し、Eclipseのツールバーから[実行]−[JUnitプラグイン・テスト]を選択します。Eclipseが起動して上のメソッドが実行され、一瞬で消えてしまうのですがビューが表示され、検索結果が一覧されます。





さて引き続き、[[前回の状態を復元する処理>Eclipse/プラグイン開発のTIPS集/GooglePlugin/前回の状態を復元する処理]]を追加したいと思います。

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

#comment
#topicpath


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


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