======================================== Use IAdapter
By using the IAdaptable interface in your applications, you can provide different views of your objects without cluttering them with non-domain/UI specific code. In this example, I will provide a list of Contact objects to a ListViewer and that when selected, the ListViewer will broadcast the selection. The gold is the adapter which will convert the selected Contact to an IPropertySource which will be displayed in a PropertySheetViewer.
To get started, create a new plug-in that will contribute to the UI. You don’t need anything more than that. The Contact class will consist of two attributes which are tied to the domain and a map for non-domain attributes. An example of a non-domain attribute would be the creation date or the last time the object was synchronised to a device.
首先,这是一个提供数据的Bean。
public class Contact {
private String address;
private String name;
private Map properties;
public Contact(String name, String address) { this.name = name; this.address = address; }
public String getAddress() { return this.address; }
public String getName() { return this.name; }
public synchronized Map getProperties() { if (this.properties == null) { return Collections.emptyMap(); }
return new HashMap(this.properties); }
public synchronized void setProperty(String key, Serializable value) { if (this.properties == null) { this.properties = new HashMap(); }
this.properties.put(key, value); } }
I also created a simple manager which returns a list of populated Contact objects and some sample property attributes.
这个就是做了一些数据。
public class Manager {
List contacts;
public Manager() { this.contacts = new ArrayList();
Contact contact = new Contact("Seamus Venasse", "141 Pueblo Crescent"); contact.setProperty("Created", "2008/04/05 11:44"); contact.setProperty("Submitted", "2008/04/05 11:53"); this.contacts.add(contact);
contact = new Contact("Derrick Law", "11 Boswell Crescent"); contact.setProperty("Created", "2008/04/05 15:21"); contact.setProperty("Submitted", "2008/04/07 09:48"); this.contacts.add(contact);
contact = new Contact("Noah Gehmair", "Boonies"); contact.setProperty("Created", "2008/04/06 10:34"); this.contacts.add(contact); }
public Contact[] contacts() { return this.contacts.toArray(new Contact[0]); }
}
For the view, a ListViewer is used to display the contacts from the Manager and to also broadcast the selected Contact to the rest of the application.
这个是程序主体,用ListViewer来显示数据。 重要的还是这句话:getViewSite().setSelectionProvider(contactsList); 必须有。
public class ContactView extends ViewPart {
public static final String ID = "us.pompo.adaptable.Contacts"; //$NON-NLS-1$
@Override public void createPartControl(Composite parent) { Composite container = new Composite(parent, SWT.NONE); container.setLayout(new FillLayout());
final ListViewer contactsList = new ListViewer(container, SWT.BORDER); contactsList.setLabelProvider(new ListLabelProvider()); contactsList.setContentProvider(new ContentProvider()); contactsList.setInput(Activator.getDefault().getContactManager()); getViewSite().setSelectionProvider(contactsList); }
@Override public void setFocus() { }
class ContentProvider implements IStructuredContentProvider {
public void dispose() { }
public Object[] getElements(Object inputElement) { Manager contactManager = (Manager) inputElement; return contactManager.contacts(); }
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } }
class ListLabelProvider extends LabelProvider {
@Override public Image getImage(Object element) { return null; }
@Override public String getText(Object element) { Contact contact = (Contact) element; return contact.getName(); } }
}
The most important piece in the above code is setting the selection provider in the createPartControl method. Without this statement, the knowledge of the selection remains with the ListViewer and any listeners you’ve added.
There are also two inner classes to provide the data to the ListViewer and how the element object should be displayed. Although not needed, I have created a perspective to ensure that both the ContactView and PropertySheetViewer are displayed.
下面,是做了一个布局,把Property视图给显示出来了。 它这里是用了内置的那个窗口,没有new一个。
public class ContactPerspective implements IPerspectiveFactory {
public void createInitialLayout(IPageLayout layout) { String editorArea = layout.getEditorArea(); layout.addView(ContactView.ID, IPageLayout.LEFT, 0.25f, editorArea); layout.addView(IPageLayout.ID_PROP_SHEET, IPageLayout.BOTTOM, 0.6f, editorArea); }
}
The only thing to note is that I am using the built-in Eclipse view for property sheets, IPageLayout.ID_PROP_SHEET, and not creating another view.
Now that the view is configured to broadcast the selected Contact object, any active class that implements ISelectionListener will receive the object in their selectionChanged method. The PropertySheetViewer is one such class that implements the ISelectionListener interface.
视图会广播被选中的对象,所有实现了ISelectionListener接口的对象的selectionChanged方法会被调用。
When a Contact is selected, the PropertySheetViewer.selectionChanged method will ask the platform if there are any known adapters that will convert a Contact object to an IPropertySource object. Since we haven’t created our adapter the PropertySheetViewer will not display anything. PropertySheetViewer的selectionChanged方法会向平台询问,谁能转换选择的对象为IPropertySource对象。
So we need a IPropertySource which will display the properties from the Map in Contact. The ContactPropertySource will iterate through the keys of the map and return an array of IPropertyDescriptor objects. We only need a read-only view of the properties so we’ll use the PropertyDescriptor object.
这个就是原始数据类型,对应的支持Property视图的数据类型,实现IPropertySource接口就行。
public class ContactPropertySource implements IPropertySource {
private Contact contact;
public ContactPropertySource(Contact contact) { this.contact = contact; }
@Override public Object getEditableValue() { return this; }
@Override public IPropertyDescriptor[] getPropertyDescriptors() { List descriptors = new ArrayList();
Map properties = this.contact.getProperties(); for (String key : properties.keySet()) { PropertyDescriptor descriptor = new PropertyDescriptor(key, key); descriptor.setAlwaysIncompatible(true); descriptors.add(descriptor); }
return descriptors.toArray(new IPropertyDescriptor[0]); }
@Override public Object getPropertyValue(Object id) { Map properties = this.contact.getProperties(); return properties.get(id); }
@Override public boolean isPropertySet(Object id) { return false; }
@Override public void resetPropertyValue(Object id) { }
@Override public void setPropertyValue(Object id, Object value) { } }
Like a Map, the identifier for a PropertyDescriptor must be unique as it will be passed to the getPropertyValue method to retrieve the value.
So how does PropertySheetViewer know about the ContactPropertySource? From registering an IAdapterFactory which will answer PropertySheetViewer’s earlier question of asking the platform if there are any known adapters that will convert a Contact object to an IPropertySource object.
再写一个适配器工厂,根据被选中的数据类型,返回对应的实现了IPropertySource接口的数据类型。
public class ContactAdapterFactory implements IAdapterFactory {
private static final Class[] TYPES = { Contact.class };
@Override public Object getAdapter(Object adaptableObject, Class adapterType) { if ((adaptableObject instanceof Contact) && (adapterType == IPropertySource.class)) { return new ContactPropertySource((Contact) adaptableObject); } return null; }
@Override public Class[] getAdapterList() { return TYPES; }
}
This is a very simple object to understand. The two method from IAdapterFactory offer the ability to dynamically convert the Contact selection to an IPropertySource by returning a ContactPropertySource object.
First off the list is the getAdapertList method. This method tells the platform the types of objects it can adapt, otherwise this object would be asked for every type of selection.
The getAdapter method receives the object to adapt and the class type that is requested. In this example, when a Contact and IPropertySource are being passed, a new ContactPropertySource is returned. If the object cannot handle the request, a null is returned. The PropertySheetViewer ensures that the result is not null before using the adapted object.
还有一步,就是把工厂注册到平台上,否则平台哪里知道这有个工厂呢。 可以在Activator里注册,也可以在plugins.xml中写。 这个文章没写! 看这里吧:http://www.mytju.com/classcode/news_readNews.asp?newsID=314 The ContactAdapterFactory must now be registered to the platform either programmatically from the Activator or through the plugins.xml. I prefer to put as much configuration in the plugin.xml as possible.
Even though Contact does not implement the IAdaptable interface, it can still be represented in the adapter factory. Now when a contact is selected, the PropertySheetViewer displays the entries from the properties map. The domain object does not know anything about IPropertySheet or IAdaptable
|