Eclipse

뷰 연결로 Eclipse 애플리케이션을 더욱 풍부하게!

_침묵_ 2006. 9. 7. 02:01

출처 :http://www-128.ibm.com/developerworks/kr/library/os-ecllink/index.html

 

뷰 연결로 Eclipse 애플리케이션을 더욱 풍부하게!

더욱 쉬워진 UI 뷰를 연결

사용자 삽입 이미지
사용자 삽입 이미지

 

난이도 : 중급

Chinmay Pandit, 소프트웨어 엔지니어, IBM India Software Labs

2005 년 11 월 15 일

풍부한 GUI에서 뷰는 다양한 방식으로 정보를 디스플레이 함으로서 사용자 경험을 향상시킨다. 따라서 UI 뷰는 다른 뷰들에 의존하고 인터랙팅 해야 한다. Eclipse는 UI 뷰들을 연결하고 비-UI 시나리오에 뷰 링크를 적용하는 방식을 제공하고 있다.

머리말

 

Eclipse 플랫폼에서는 플러거블 컴포넌트—플러그인(plug-ins)—로 풍부한 그래픽 사용자 인터페이스(GUI) 애플리케이션을 만들 수 있다. 플러그인들이 GUI에 뷰를 제공한다. 하지만 실제 애플리케이션에서 UI 뷰는 결코 섬이 되어서는 안된다. 다른 뷰들과 함께 인터랙팅 하고 업데이트 되어야 한다.

 

전 세계 가장 대표적인 여행지를 설명하는 GUI 애플리케이션이 오늘 설명할 예제이다. 이 GUI에서는 Select City 뷰를 통해 흥미로운 장소들과 대중 교통 정보를 디스플레이 한다.

 


그림 1. 뷰 연결 예제

사용자 삽입 이미지

이 글에서는 Eclipse의 뷰들이 협업하여 다른 뷰들의 상태에 부응하는 방법을 설명한다. 또한, 뷰 연결 방식 중 어떤 것이 더 나은지도 검토할 것이다.

 

Eclipse 개발자들은 뷰를 연결하기 위해 다음과 같은 방법을 사용한다.

  1. 셀렉션 공급자-셀렉션 리스너 패턴: 뷰 들은 다른 뷰들의 셀렉션에 부응한다.
  2. 몇몇 이벤트들과 결합하여 사용되는IAdaptable인터페이스
  3. 속성 변경 리스너(property change listener): 뷰는 속성 변경 이벤트를 등록된 리스너에 푸시(push)할 수 있다.


사용자 삽입 이미지


셀렉션 공급자-셀렉션 리스너 패러다임

 

셀렉션 공급자-셀렉션 리스너 패턴은 다른 뷰에서 변경사항이 생기면 이에 부응하는 뷰를 만들 수 있는 편리한 방법이다. 예를 들어, 사용자가 도시명을 나타내는 UI 아이템을 클릭하면 몇몇 다른 뷰는 그 장소의 상세를 디스플레이 한다. 그와 같은 뷰는 UI 셀렉션 객체 안에 포함된 정보를 소비할 수 있고(도시명을 나타내는 스트링 객체), 이것을 사용하여 이 모델에서 추가 정보를 검색 및 디스플레이 할 수 있다.

 

뷰는 UI 셀렉션 이벤트를 확인 및 소비할 수 있어야 한다. org.eclipse.ui.IselectionListener는 UI 셀렉션 이벤트를 받는 리스너 인터페이스이다. 이 셀렉션 리스너는 워크벤치 페이지와 함께 등록되어야 한다. 워크벤치 페이지는 org.eclipse.ui.IselectionService 인터페이스에 의해 정의된 서비스를 구현하여 UI 셀렉션 이벤트를 이 리스너들과 조합한다. 셀렉션 리스너는 하나의 셀렉션 서비스와 함께 등록되어야 한다.

 

선택 가능성이 있는 UI 아이템들을 디스플레이 하는 뷰들은 UI 셀렉션들을 퍼플리시 할 수 있어야 한다. "셀렉션 공급자"를 각각의 워크벤치 사이트로 등록함으로서 이러한 뷰들이 만들어 질 수 있다. Eclipse의 각 UI 파트는 org.eclipse.ui.IworkbenchPartSite 레퍼런스를 통해 워크벤치 사이트와 연락한다. 셀렉션 공급자들은 워크벤치 사이트와 함께 등록된다.

 

셀렉션 공급자-셀렉션 리스너 패턴을 사용하여 뷰들이 연결될 때 뷰는 스스로 리스너로서 워크벤치 페이지에 추가되고 셀렉션을 퍼블리시 하고자 하는 다른 뷰들은 셀렉션 공급자를 각각의 워크벤치 사이트에 추가해야 한다. 아래는org.eclipse.ui.ISelectionListener인터페이스이다.

public void selectionChanged(IWorkbenchPart part, ISelection selection);

뷰가 셀렉션 변경에 대해 알 수 있도록(listening) 하려면 뷰는 IselectionListener 인터페이스를 구현하고 이 워크벤치 페이지와 함께 등록해야 한다.

 


Listing 1. 셀렉션 리스너를 워크벤치 페이지에 추가하기

public class MyView extends ViewPart implements  ISelectionListener{ public void createPartControl(Composite parent) {    // add this view as a selection listener to the workbench page  getSite().getPage().addSelectionListener((ISelectionListener) this); } // Implement the method defined in ISelectionListener, to consume UI selections public void selectionChanged(IWorkbenchPart part, ISelection selection) {  //Examine selection and act on it! }}

UI 셀렉션을 소비하는 더 현명한 방법은 소비자 뷰를 특정 뷰 부분들에 대한 리스너로서 등록하는 것이다. 아래 예제에서 보듯, view ID는 셀렉션 리스너를 등록하는 동안 매개변수로서 언급된다.

getSite().getPage().addSelectionListener("SampleViewId",(ISelectionListener)this);

이러한 접근 방식은 소비자 뷰로의 과잉 콜백을 줄여준다. 이 뷰가 비특정 리스너로서 등록되었다면 소비자 뷰가 다르게 발생한다.

 

Listing 2의 코드는 JFace TableViewer를 만들고 이것을 셀렉션 공급자로서 워크벤치 사이트에 추가하는 뷰의createPartControl()메소드를 보여준다. 이 코드는 TableViewer에 어떤 UI 셀렉션이 변경되었더라도 페이지와 관련 소비자 뷰로 전파된다.

 


Listing 2. 셀렉션 공급자 설정

public void createPartControl(Composite parent) { // Set up a JFace Viewer viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); viewer.setContentProvider(new ViewContentProvider()); viewer.setLabelProvider(new ViewLabelProvider()); viewer.setSorter(new NameSorter()); viewer.setInput(getViewSite());  // ADD the JFace Viewer as a Selection Provider to the View site. getSite().setSelectionProvider(viewer);}

이 뷰는 다음 두 가지 이유로 셀렉션 공급자로서 JFace TableViewer를 등록한다.

  1. 뷰는 TableViewer를 사용하여 정보를 디스플레이 하고, 사용자는 TableViewer와 인터랙팅한다.
  2. TableViewer는 셀렉션 공급자 인터페이스를 구현하고 셀렉션 이벤트를 워크벤치 파트 사이트로 정보를 제공 할 수 있다.

JFace 뷰어는 셀렉션 공급자이기 때문에 대부분의 경우 셀렉션 공급자를 만들 필요가 없다. 뷰는 많은 JFace 뷰어들 중 하나를 사용하여 데이터를 디스플레이 하고 JFace 뷰어를 셀렉션 공급자로서 등록해야 한다.

 

다른 연결 방법

 

다음은 다른 뷰 연결 방식을 취해야 할 경우이다.

  1. 메모리 사용이 증가하면서, 정보량이 UI 셀렉션 객체에는 너무 커서 효율적으로 보유할 수 없다.
  2. 뷰는 시각적 셀렉션이 아닌 정보를 퍼블리시 해야 한다. 퍼블리시 되어야 하는 정보는 그 셀렉션에 근거한 포스트 프로세싱(post-processing)의 결과가 된다.
  3. 어떤 뷰는 뷰를(저장된 JFace 뷰어와 함께) 기여하지 않았던 또 다른 플러그인에서 정보를 소비해야 한다. 이 경우 UI 셀렉션 기반 연결을 활용할 수 없다.

첫 번째 문제는 org.eclipse.core.runtime.IAdaptable인터페이스로 어느 정도 해결될 수 있다. 셀렉션 객체는 온 디맨드 방식으로 더 많은 정보를 가져올 수 있다. 2번과 3번 문제는 직접적인 방식이 필요하다.


사용자 삽입 이미지


IAdaptable 인터페이스 사용하기

 

IAdaptable을 구현하는 클래스는 앞으로의 정보를 검색하는데 사용될 수 있는 특정 유형의 어댑터를 동적으로 리턴하는 기능이 있다. 뷰어의 셀렉션 객체들이IAdaptable인터페이스를 구현하면 이들은 어댑터의 유형에 근거하여 추가 정보나 관련 정보들을 검색하는데 효율적으로 사용된다. 아래는org.eclipse.core.runtime.IAdaptable인터페이스 이다.

public void object getAdapter(Class adapter);

분명히, 콜러는 셀렉션이 리턴할 어댑터 인터페이스의 유형을 이해해야 한다. 싱글 레벨 트리에 도시들을 렌더링 하도록 설정된 JFace TreeViewer를 생각해 보자. 도시들을 나타내는 이 객체들은CityClass유형이다.CityClass객체는 도시에 대한 기본적인 정보를 보유하고 있고 필요할 때에만 상세한 정보를 리턴한다. Listing 3에서, 콜러가 더 많은 정보를 온 디맨드 방식으로 검색할 수 있도록CityClass가 지원하는 어댑터 유형을 주목하라.

 


Listing 3. JFace TreeViewer의 CityClass

class CityClass implements IAdaptable {       private String cityName;      public CityClass(String name) {             this.name = name;      }      public String getName() {     return name;      }      public CityClass getParent() {            return parent;      }      public String toString() {            return getName();      }       public Object getAdapter(Class key) {       if (key.getName().equals("ITransportationInfo"))        return CityPlugin.getInstance().getTransportAdapter();       else (key.getName().equals("IPlacesInfo"))        return CityPlugin.getInstance().getPlacesAdapter();      return null;      }}

Eclipse IDE 워크벤치에 익숙한 개발자들은 Outline 뷰를 알고 있다. 이것은 에디터에서 열린 파일의 구조적 뷰를 제공한다. 이 Outline 뷰 예제는IAdaptable인터페이스가 특정 이벤트 유형과 결합하여 다른 뷰의 내용에 기반하여 뷰를 어떻게 효과적으로 초기화 하는지를 보여준다. 에디터는 사용자가 편집을 이 파일에 대한 Content Outline 페이지를 만들어야 한다.

 

Content Outline 페이지는 IContentOutlinePage 라고 하는 특정 인터페이스에 순응한다. 이 에디터는IAdaptable인터페이스를 구현하여 에디터가 IContentOutlinePage 유형의 어댑터용으로 검색될 수 있도록 해야 한다. 이 어댑터는 파일에 대한 아웃라인 정보를 검색 및 디스플레이 하는데 사용된다.IAdaptable인터페이스의 또 다른 예제는 Properties 뷰이다. 이 Properties 뷰는 활성 부분에 대한 셀렉션을 트래킹하고 현재 셀렉션 객체에 대해getAdapter메소드를 호출한다. 조회 된 어댑터 유형은IPropertySource이다. Properties 뷰는IPropertySource어댑터를 사용하여 디스플레이 정보를 검색한다.

 

뷰 연결 예제에서, 애플리케이션은 Selection Changed 또는 Part Activation 공지를 받을 때IAdaptable을 통해 정보를 초기화한다. 따라서 셀렉션이IAdaptable이면 사용자는 셀렉션 객체에서 사용할 수 있는 것 보다 어댑터를 통해서 훨씬 더 많은 정보를 검색할 수 있다.


사용자 삽입 이미지


속성으로 리스너 패러다임 바꾸기

 

인터랙션의 속성 변경 리스너 유형을 사용하여 앞서 언급된 다른 두 가지 문제들을 해결 할 수 있다. 뷰 기여가 없는 플러그인에서 뷰가 어떻게 정보를 소비하는가? 그리고 시각적 셀렉션에 대해 몇몇 프로세싱이 발생한 후에 만들어진 정보를 뷰가 어떻게 퍼블리시 하는가?

 

플러그인을 구현하여 속성 변경 리스너의 등록을 수락하고 등록된 리스너를 필요에 따라 공지할 수 있다. 이 애플리케이션은 공유된 정보를 포함하고 있는 커스터마이징 된 이벤트에 대한 리스너를 공지할 수 있다.

 

셀렉션 공급자의 경우와는 달리 속성 변경 공급자가 구현할 특정 인터페이스는 없다. 만들어진 공급자와 함께 리스너를 등록하는 문법을 결정해야 한다. Listing 4의 코드는 메소드가 속성 변경 리스너를 속성 공급자 뷰 또는 플러그인 클래스에서 추가 또는 제거하는 방법을 보여주고 있다.

 


Listing 4. 속성 변경 리스너의 추가와 제거

//To add a listener for property changes to this notifier:  public void addPropertyChangeListener(IPropertyChangeListener listener);//To remove the given content change listener from this notifier:  public void removePropertyChangeListener(IPropertyChangeListener listener);

속성 공급자는org.eclipse.jface.util.PropertyChangeEvent를 사용하여 효과적으로 파퓰레이트 및 전개될 수 있는 이벤트를 만든다. 또한, 리스너 리스트를 관리하고 이들을 콜백해야 하는 책임은 속성 공급자에게 넘어간다.

 

주요 도시를 위해 매 시간 World Weather 웹 서비스를 호출하고 다른 플러그인과 뷰에서 사용할 수 있는 정보를 만드는 플러그인을 생각해 보자. CityWeatherPlugin은 CitiesWeatherXML이라는 속성을 노출하고 소비자는PropertyChange리스너로서 CityWeatherPlugin와 함께 등록할 수 있다. 리스너는 CityWeatherPlugin의 메소드를 인식하여 날씨 데이터 이벤트에 리스너로 등록한다. CityWeatherPlugin은 리스너를 계속 트래킹 하고 공지해야 한다.PropertyChangeEvent를 사용하여 리스너에게 데이터를 공급한다.

 


Listing 5. 속성 공급자 구현하기

class CityPopulationPlugin { ArrayList myListeners; // A public method that allows listener registration public void addPropertyChangeListener(IPropertyChangeListener listener) {  if(!myListeners.contains(listener))   myListeners.add(listener); } // A public method that allows listener registration public void removePropertyChangeListener(IPropertyChangeListener listener) {  myListeners.remove(listener); } public CityPopulationPlugin (){ // method to start the thread that invokes the population \ web service once every hour // and then notifies the listeners via the propertyChange() callback method.    initWebServiceInvokerThread( myListeners );   } void initWebServiceInvokerThread(ArrayList listeners) {  // Code to Invoke Web Service Periodically, and retrieve information     // Post Invocation, inform listeners  for (Iterator iter = listeners.iterator(); iter.hasNext();) {   IPropertyChangeListener element = (IProperty\   ChangeListener) iter.next();   element.propertyChange(new PropertyChangeEvent(this, \   "CitiesWeatherXML" , null , CityWeatherXMLObj));     } }}

속성 변경 리스너는org.eclipse.jface.util.IPropertyChangeListener인터페이스를 구현하여 속성 변경 공급자로 부터 콜백을 실행시켜야 한다. 이 인터페이스는public void propertyChange(PropertyChangeEvent event)라는 한 개의 메소드를 갖고 있다.

 


Listing 6. IPropertyChangeListener 구현하기

class MyView implements IPropertyChangeListener { public void createPartControl() {  //register with a Known Plugin that sources Population Data   CityPopulationPlugin.getInstance().addPropertyChangeListener(this); } public void propertyChange(PropertyChangeEvent event) {  //This view is interested in the Population Counts of the Cities.  //The population data is being sourced by another   plugin in the background.  if( event.getProperty().equals("CitiesWeatherXML")) {   Object val = event.getNewValue();   // do something with val  } }}

이 방식은 애플리케이션이 리스너를 공지하고 필요한 만큼 다양한 상황에서 정보를 전달한다는 점에서 보다 유연하다고 할 수 있다. 전달된 정보는 UI 셀렉션과 직접적으로 관련 될 필요가 없다. 포스트 프로세싱의 결과가 될 수도 있다. 그리고 다른 배경 작업의 상태 또는 모델에서 검색된 최신 정보의 주기적인 푸시와 관련이 될 수도 있다. City Selector View는 선택된 도시 정보를 전달할 뿐만 아니라 현재 선택된 도시의 날씨 정보를PropertyChange패러다임을 사용하는 다른 소비자에게 비동기식으로 전달한다.


사용자 삽입 이미지


요약

 

지금까지 뷰들이 협업하고 서로 응답하는 다양한 방법을 설명했다. UI 셀렉션으로 충분하지 않다면IAdaptable인터페이스를 사용할 수 있다. 이 속성 변경 리스너는 비 UI 시나리오에도 적용할 수 있다.