Preferences扩展点及其使用心得
本文学习参照文章“Preferences in the Eclipse Workbench UI”,除了实现流程外,更重要的是对例子中代码的学习。
在Eclipse中,选择window->preferences后打开preferences窗口,这个窗口是Eclipse所有设置项的集中地,同时也是我们自定义的插件进行设置的窗口。
第一章:Preference概述
Preference是一种数据,它存在于工作空间中,存储的是Eclipse工作的状态,使之保持一致性。Eclipse2.0提供了两种preference:UI preferences (the same as in 1.0) and Core Preferences。Preference并不涉及到当前工作空间中的任何资源,它应该被编辑器,视图或者其它的操作资源的对象所使用。一种资源实例对应的数据最好存储成一个属性。Preference对任何插件来说都是可用的,当然,先决条件是存在插件。通常的方法是你的插件上为你觉得可能被其他插件用到的Preference提供API, Preference的值存储在工作空间的.metadata/.plugins目录下面。
每个插件都有自己的preference store(存储区),这是工作空间提供的。在下面的例子中(这个例子是在文件中查找错误的单词),我们将定义一个插件,使用它的preference store存储preference。在UI中当要使用插件的时候我们定义AbustractUIPlugin的子类。构造函数将在工作台中创建一个独立的允许容易访问的插件实例。我们实现方法initializeDefaultPreferences为preference设置默认值。首先为错误的单词定义了一个preference,另外一个是定义高量显示的颜色。每个preference的值与给定的key(键值对的存在形式)相对应。
所有的preference应该都有默认值来保证在任何时候都有值。默认值也保证了UI能够提供一种方式重置preference的值回到初始设定,这是通过“Restore”button实现的。Preference的默认值在插件中也应该进行初始化,以便它在任何UI创建之前设置。
第二章:示例实现
一个插件在工作区都会有它的参数存储区 ,在本例中,我们定义一个插件用它的参数存储区来作为我们的参数。因为要用到UI界面,我们把它实现为AbstractUIPlugin的子类---IabstractWorkbenchPlugin类, IabstractWorkbenchPlugin定义了一个方法,当preference第一次创建的时候,它会被调用。这个方法就是initializeDefaultPreferences。在这个方法中你应该把所有的值都设为默认值,才能够使用preference store。我们在PreferenceConverter中用helper方法设定默认的color,PreferenceConverter允许插件开发者设置和得到一些常见的存储类型的preference的值,例如FontData,Point等等。这个API提供是因为preference无论是存储还是读取都是String—以一种我们人可以理解的格式。这样来平衡的属性机制。我们那个稍微复杂的错误单词的preference在初始化的时候使用了预先设定的值因为在PreferenceConvertor中并没有提供API来存储和读取String数组。
public class BadWordCheckerPlugin extends AbstractUIPlugin { //The shared instance. private static BadWordCheckerPlugin plugin; //The identifiers for the preferences public static final String BAD_WORDS_PREFERENCE = "badwords"; public static final String HIGHLIGHT_PREFERENCE = "highlight"; //The default values for the preferences public static final String DEFAULT_BAD_WORDS = "bug;bogus;hack;"; public static final int DEFAULT_HIGHLIGHT = SWT.COLOR_BLUE; public BadWordCheckerPlugin(IPluginDescriptor descriptor) { super(descriptor); plugin = this; } public static BadWordCheckerPlugin getDefault() { return plugin; } /** * Initializes a preference store with default preference values * for this plug-in. */ protected void initializeDefaultPreferences(IPreferenceStore store) { store.setDefault(BAD_WORDS_PREFERENCE, DEFAULT_BAD_WORDS); Color color= Display.getDefault().getSystemColor(DEFAULT_HIGHLIGHT); PreferenceConverter.setDefault(store, HIGHLIGHT_PREFERENCE, color.getRGB()); } } |
在xml中配置
<extension point="org.eclipse.ui.preferencePages"> <page id="BadWordsPreferencePage" name="Bad Words" class="org.eclipse.ui.articles.badwordchecker.BadWordsPreferencePage"> </page>
<page id="BadWordsColorPreferencePage" name="Colors" class="org.eclipse.ui.articles.badwordchecker.BadWordsColorPreferencePage" category="BadWordsPreferencePage"> </page> </extension> |
是在页面中显示的名字, 是参数配置页面类,这个类必须实现IWorkbenchPreferencePage接口。 是页面中显示的包含关系(不用多解释)。
第二个类:reference Page
这个页面只是一个用JFace实现的简单页面,Eclipse中有个PreferencePage类—它实现了Preference 页面需要的大多数API,当点下确定键后,参数对话框的信息就会被保存。代码如下:
package org.eclipse.ui.articles.badwordchecker; import org.eclipse.jface.preference.ColorFieldEditor; import org.eclipse.jface.preference.PreferencePage; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferencePage; /** * The BadWordsColorPreferencePage is a preference page that * handles setting the colors used by the editors. */ public class BadWordsColorPreferencePage extends PreferencePage implements IWorkbenchPreferencePage { private ColorFieldEditor colorEditor; /* * @see PreferencePage#createContents(Composite) */ @SuppressWarnings("deprecation") protected Control createContents(Composite parent) { Composite entryTable = new Composite(parent, SWT.NULL); //Create a data that takes up the extra space in the dialog . GridData data = new GridData(GridData.FILL_HORIZONTAL); data.grabExcessHorizontalSpace = true; entryTable.setLayoutData(data); GridLayout layout = new GridLayout(); entryTable.setLayout(layout);
Composite colorComposite = new Composite(entryTable,SWT.NONE); colorComposite.setLayout(new GridLayout());
//Create a data that takes up the extra space in the dialog. colorComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); colorEditor = new ColorFieldEditor( BadWordCheckerPlugin.HIGHLIGHT_PREFERENCE, "Highlight Color", colorComposite);
//Set the editor up to use this page colorEditor.setPreferencePage(this); colorEditor.setPreferenceStore(getPreferenceStore()); colorEditor.load(); return entryTable; } /* * @see IWorkbenchPreferencePage#init(IWorkbench) */ public void init(IWorkbench workbench) { //Initialize the preference store we wish to use setPreferenceStore(BadWordCheckerPlugin.getDefault().getPreferenceStore()); } /** * Performs special processing when this page's Restore Defaults button has * been pressed. * Sets the contents of the color field to the default value in the preference * store. */ protected void performDefaults() { colorEditor.loadDefault(); } /** * Method declared on IPreferencePage. Save the * color preference to the preference store. */ public boolean performOk() { colorEditor.store(); return super.performOk(); } } |
运行效果如下图:
第三个类:ad Words Preference Page
下面将要用一个更复杂些的组件作为preference,并使它存储在preference store且在preference页面中可编辑。例子是:添加String数组的bad words preference。
由于PreferenceConverter没有String数组的API,我们在BadWordCheckerPlugin中来实现。添加如下代码:
/** * Return the bad words preference default. */ public String[] getDefaultBadWordsPreference(){ return convert(getPreferenceStore().getDefaultString(BAD_WORDS_PREFERENCE)); } /** * Returns the bad words preference. */ public String[] getBadWordsPreference() { return convert(getPreferenceStore().getString(BAD_WORDS_PREFERENCE)); } /** * Converts PREFERENCE_DELIMITER delimited String to a String array. */ private String[] convert(String preferenceValue) { StringTokenizer tokenizer = new StringTokenizer(preferenceValue, PREFERENCE_DELIMITER); int tokenCount = tokenizer.countTokens(); String[] elements = new String[tokenCount]; for (int i = 0; i < tokenCount; i++) { elements[i] = tokenizer.nextToken(); } return elements; } /** * Sets the bad words preference. */ public void setBadWordsPreference(String[] elements) { StringBuffer buffer = new StringBuffer(); for (int i = 0; i < elements.length; i++) { buffer.append(elements[i]); buffer.append(PREFERENCE_DELIMITER); } getPreferenceStore().setValue(BAD_WORDS_PREFERENCE, buffer.toString()); } |
BadWordsPreferencePage主要是实现界面和两个按键的响应。代码如下(可以仔细研究):
package org.eclipse.ui.articles.badwordchecker; import org.eclipse.jface.preference.PreferencePage; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.*; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferencePage; public class BadWordsPreferencePage extends PreferencePage implements IWorkbenchPreferencePage { //The list that displays the current bad words private List badWordList; //The newEntryText is the text where new bad words are specified private Text newEntryText; /* * @see PreferencePage#createContents(Composite) */ protected Control createContents(Composite parent) { Composite entryTable = new Composite(parent, SWT.NULL); //Create a data that takes up the extra space in the dialog . GridData data = new GridData(GridData.FILL_HORIZONTAL); data.grabExcessHorizontalSpace = true; entryTable.setLayoutData(data); GridLayout layout = new GridLayout(); entryTable.setLayout(layout);
//Add in a dummy label for spacing new Label(entryTable,SWT.NONE); badWordList = new List(entryTable, SWT.BORDER); badWordList.setItems(BadWordCheckerPlugin.getDefault().getBadWordsPreference()); //Create a data that takes up the extra space in the dialog and spans both columns. data = new GridData(GridData.FILL_BOTH); badWordList.setLayoutData(data);
Composite buttonComposite = new Composite(entryTable,SWT.NULL);
GridLayout buttonLayout = new GridLayout(); buttonLayout.numColumns = 2; buttonComposite.setLayout(buttonLayout); //Create a data that takes up the extra space in the dialog and spans both columns. data = new GridData(GridData.FILL_BOTH | GridData.VERTICAL_ALIGN_BEGINNING); buttonComposite.setLayoutData(data);
Button addButton = new Button(buttonComposite, SWT.PUSH | SWT.CENTER); addButton.setText("Add to List"); //$NON-NLS-1$ addButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { badWordList.add(newEntryText.getText(), badWordList.getItemCount()); } });
newEntryText = new Text(buttonComposite, SWT.BORDER); //Create a data that takes up the extra space in the dialog . data = new GridData(GridData.FILL_HORIZONTAL); data.grabExcessHorizontalSpace = true; newEntryText.setLayoutData(data);
Button removeButton = new Button(buttonComposite, SWT.PUSH | SWT.CENTER); removeButton.setText("Remove Selection"); //$NON-NLS-1$ removeButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { badWordList.remove(badWordList.getSelectionIndex()); } });
data = new GridData(); data.horizontalSpan = 2; removeButton.setLayoutData(data);
return entryTable; } /* * @see IWorkbenchPreferencePage#init(IWorkbench) */ public void init(IWorkbench workbench) { //Initialize the preference store we wish to use setPreferenceStore(BadWordCheckerPlugin.getDefault().getPreferenceStore()); } /** * Performs special processing when this page's Restore Defaults button has been pressed. * Sets the contents of the nameEntry field to * be the default */ protected void performDefaults() { badWordList.setItems(BadWordCheckerPlugin.getDefault().getDefaultBadWordsPreference()); } /** * Method declared on IPreferencePage. Save the * author name to the preference store. */ public boolean performOk() { BadWordCheckerPlugin.getDefault().setBadWordsPreference (badWordList.getItems()); return super.performOk(); } } |
运行结果如下:
第四:对参数值的应用
通常,一个preference是向一个打开的editor或者view进行传值。这时,就需要通过IPropertyChangeListener对变化添加监听。IPropertyChangeListener类是对IProperyStore添加监听,当用户点击OK或者Apply时,监听会被触发。
在这个例子中,我们实现了一个视图,用选定的颜色显示存在的bad words,这个视图通过IPropertyChangeListener更新显示的颜色。
new IPropertyChangeListener() { public void propertyChange(PropertyChangeEvent event) { if (event.getProperty().equals(BadWordCheckerPlugin.HIGHLIGHT_PREFERENCE)) { //Update the colors by clearing the current color, //updating the view and then disposing the old color. Color oldForeground = foreground; foreground = null; setBadWordHighlights(text.getText()); oldForeground.dispose(); } if (event.getProperty().equals(BadWordCheckerPlugin.BAD_WORDS_PREFERENCE)) //Only update the text if only the words have changed setBadWordHighlights(text.getText()); } }; public void init(IViewSite site) throws PartInitException { super.init(site); site.getPage().addSelectionListener(...); BadWordCheckerPlugin .getDefault() .getPreferenceStore() .addPropertyChangeListener(preferenceListener);} public void dispose() { getSite().getPage().removeSelectionListener(...); BadWordCheckerPlugin .getDefault() .getPreferenceStore() .removePropertyChangeListener(preferenceListener); if (foreground != null) foreground.dispose(); super.dispose(); } |
实现效果如下:
具体类实现代码如下(好多代码还不懂,有待于学习):
package org.eclipse.ui.articles.badwordchecker; import java.io.IOException; import java.io.InputStream; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.jface.preference.PreferenceConverter; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.ISelectionListener; import org.eclipse.ui.IViewSite; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.PartInitException; import org.eclipse.ui.part.ViewPart; /** * The BadWordsView is a simple view that creates a StyledText' * that highlights the bad words. */ public class BadWordsView extends ViewPart { private Color foreground; private StyledText text; ISelectionListener selectionListener = new ISelectionListener() { /** * @see org.eclipse.ui.ISelectionListener#selectionChanged(IWorkbenchPart, ISelection) */ public void selectionChanged( IWorkbenchPart part, ISelection selection) { setText(selection); } }; IPropertyChangeListener preferenceListener = new IPropertyChangeListener() { /* * @see IPropertyChangeListener.propertyChange() */ public void propertyChange(PropertyChangeEvent event) { if (event .getProperty() .equals(BadWordCheckerPlugin.HIGHLIGHT_PREFERENCE)) { //Update the colors by clearing the current color, //updating the view and then disposing the old color. Color oldForeground = foreground; foreground = null; setBadWordHighlights(text.getText()); oldForeground.dispose(); } if (event .getProperty() .equals(BadWordCheckerPlugin.BAD_WORDS_PREFERENCE)) //Only update the text if only the words have changed setBadWordHighlights(text.getText()); } }; /** * @see org.eclipse.ui.IWorkbenchPart#createPartControl(Composite) */ public void createPartControl(Composite parent) { text = new StyledText(parent, SWT.NULL); GridData data = new GridData(GridData.FILL_BOTH); text.setLayoutData(data); setText( getSite() .getWorkbenchWindow() .getSelectionService() .getSelection()); } /** * Set the text value in the widget passed on the supplied * selection */ private void setText(ISelection selection) { if (selection instanceof IStructuredSelection) { Object firstObject = ((IStructuredSelection) selection).getFirstElement(); if (firstObject != null && firstObject instanceof IAdaptable) { Object resource = ((IAdaptable) firstObject).getAdapter(IResource.class); if (resource != null && resource instanceof IFile) { try { InputStream contents = ((IFile) resource).getContents(); byte[] data = new byte[contents.available()]; contents.read(data); setBadWordHighlights(new String(data)); } catch (CoreException exception) { //Do nothing } catch (IOException exception) { //Do nothing } } } } } /** * Set the highlights for the bad words using the passed in * text */ private void setBadWordHighlights(String fileContents) { Color foregroundColor = getForegroundColor(); text.setText(fileContents); String[] badStrings = BadWordCheckerPlugin.getDefault().getBadWordsPreference(); Color background = text.getBackground(); for (int i = 0; i < badStrings.length; i++) { String badWord = badStrings[i]; int length = badWord.length(); int nextIndex = 0; while (nextIndex > -1) { nextIndex = fileContents.indexOf(badWord, nextIndex); if (nextIndex > -1) { StyleRange range = new StyleRange( nextIndex, length, foregroundColor, background); text.setStyleRange(range); nextIndex += length; } } } } /** * Get the current value of the foreground color. * Create it if it does not exist yet. */ private Color getForegroundColor() { if (foreground == null) foreground = new Color( text.getDisplay(), PreferenceConverter.getColor( BadWordCheckerPlugin.getDefault().getPreferenceStore(), BadWordCheckerPlugin.HIGHLIGHT_PREFERENCE)); return foreground; } /** * @see org.eclipse.ui.IWorkbenchPart#setFocus() */ public void setFocus() { } /* (non-Javadoc) * Method declared on IViewPart. */ public void init(IViewSite site) throws PartInitException { super.init(site); site.getPage().addSelectionListener(selectionListener); BadWordCheckerPlugin .getDefault() .getPreferenceStore() .addPropertyChangeListener(preferenceListener); } /* (non-Javadoc) * Method declared on IWorkbenchPart. */ public void dispose() { // remove ourselves as a selection listener getSite().getPage().removeSelectionListener(selectionListener); //and no longer listen to the preference store BadWordCheckerPlugin .getDefault() .getPreferenceStore() .removePropertyChangeListener(preferenceListener); if (foreground != null) foreground.dispose(); // run super. super.dispose(); } } |