Image對象是一個包裝了本地資源的Java結構,不能被Java的垃圾回收器管理。加載圖像而不卸載,會導致內存泄漏。
Eclipse使用ImageDescriptor來描述Image,ImageDescriptor可以通過getImageDescriptor()方法來獲得。
ImageCache類為一個專門管理圖像的類。創建編輯器的上下文操作,targetID="#TextEditorContext",為eclipse默認文件編輯器的上下文菜單標識符。實現類必須實現IEditorActionDelegate接口。
Java編輯器對應的ID為:#TextEditorContext和 #CompilationUnitEditorContext,定義好targetID,就把我們的插件id指向視圖菜單的id了。這樣Java編輯器就會添加我們的菜單項了。
targetID用來與特定的編輯器,查看器,菜單綁定。
命令與鍵綁定,和操作相關聯。命令只是操作的聲明,操作的具體實現細節由操作本身來完成。將命令從操作實現中分離出來。
Org.eclipse.ui.commands擴展點 定義命令
Org.eclipse.ui.bindings擴展點 與命令和鍵綁定
視圖的實現類都必須實現org.eclipse.ui.IViewPart接口,一般都會集成org.eclipse.ui.ViewPart類。
視圖的行為由一個實現了org.eclipse.ui.IViewPart接口的類定義
IStructuredContentProvider結構化內容提供者負責從輸入對象中提取對象,把它傳遞給表格查看器進行顯示。AddressViewContentProvider實現IStructuredContentProvider(內容與查看器的綁定)和 AddressManagerListener接口(監聽模型變化)。
AddressViewContentProvider通過AddressManager獲取模型來提供顯示
AddressViewContentProvider實現AddressManagerListener的addressesChanged方法。
LableProvider標簽提供者將獲取內容提供者返回的一個表格行對象,并提取要顯示到的列上的值。
ITableLabelProvider接口要求實現getColumnText和getColumnImage兩個主要的方法,getColumnText返回指定單元格的顯示文本,getColumnImage返回指定單元格的顯示圖標。
根據對表格列的判斷來顯示相應區域的文本和圖像。
public String getColumnText(Object element, int columnIndex),其中element表示單元格所在行的對象,columnIndex表示單元格所在的列,返回顯示的文本。
public Image getColumnImage(Object element, int columnIndex),其中element表示單元格所在行的對象,columnIndex表示單元格所在的列,返回此單元格的顯示圖標。
ViewerSorter查看器排序器用于對內容提供者提供的元素進行排序,繼承ViewerSorter類。
一個視圖僅可以有一個內容提供者,一個標簽提供者和一個排序器,但過濾器可以有很多個。
ViewerFilter類用于確定顯示內容提供者返回的哪些行對象,不顯示哪些行對象。
獲取當前選中項,先查找視圖,找到該視圖后,調用getSelection()得到當前選擇項,然后馬上轉換為IStructuredSelection,以后的操作就是統一的了,因為有IStructuredSelection接口。如果是在action中遇到這種需求,就將event調用getSelection(),然后轉換為IStructuredSelection,后續操作相同。
Eclipse的每個視圖(View)都有自己的菜單和工具條,View通過與自己相關的IViewSite對象與這些東西打交道,確切的說,是通過這個IViewSite對象的IActionBars對象來管理,ActionBars對象負責菜單、工具條和狀態欄。
makeActions是創建操作,必要的IAction對象,這些對象可用在菜單、工具條里。
hookContextMenu是把剛剛創建的IAction對象放進與View相關的MenuManager里。
當觸發了menu事件時,重新填充(fillContextMenu)
setRemoveAllWhenShown(true)的作用是清空以前顯示的菜單項。不把removeAllWhenShow置為true的話,每點一下右鍵你就會看到菜單項多出一倍來。
用MenuManager可以創建出一個Menu對象,然后我們用表格的setMenu方法將表格控件與Menu控件聯系在一起就好了。
getSite().registerContextMenu(menuMgr, viewer)這是context menu能不能顯示的關鍵。一個視圖中可以有多個context menu,而每一個context menu都必須注冊給workbench。這需要通過調用org.eclipse.ui.IWorkbenchPartSite.registerContextMenu(MenuManager menuManager, ISelectionProvider selectionProvider)或者(當有多個注冊的context menu時)org.eclipse.ui.IWorkbenchPartSite.registerContextMenu(String menuId, MenuManager menuManager, ISelectionProvider selectionProvider) 增加的參數menuId用于區分不同的context menu。
上下文菜單比工具欄多一步創建上下文菜單。
屬性視圖支持所有實現IResource接口的資源。
一個視圖(View)希望得到另外一個視圖顯示的內容,或者選擇的內容。在Eclipse中,比較標準的做法是通過ISelectionProvider和ISelectionListener來完成的。
Eclipse為了解決這個問題,提供了所謂的Site,以及ISelectionService機制,來處理視圖之間的簡單的交互。簡單的說,ViewSite提供了一個交互的中心點,其它View向ViewSite提供選擇事件,或者向其注冊監聽器,而事件的觸發與轉發則由ViewSite()來完成。只需在View中實現ISelectionListener接口,就可以監聽其他Workbench部分發生的選擇事件。
向Workbench注冊監聽器
getSite().getPage().addSelectionListener(this);
向特定WorkbenchID注冊監聽器
getSite().getPage().addSelectionListener(String WorkbenchID, ISelectionListener);
并實現selectionChanged方法來處理監聽器捕獲到的事件。
一個ISelectionProvider如果希望被別的View進行監聽的話,則應該向其Site()進行注冊。
getViewSite().setSelectionProvider(viewer);
IPropertySource的實現類必須為每個顯示在屬性視圖中的項創建一個屬性描述符。
ComboBoxPropertyDescriptor可以在屬性視圖的單元格中顯示復選框,但必須為其定義標簽提供者和一組String數組為顯示內容。getText的參數是getPropertyValue的返回值,這個返回值必須是整數。在ComboBoxPropertyDescriptor中,每個項是按照它們的索引號來區分的。在initProperties中,將類別名用整數表示。
Integer item = (Integer)element;
表格查看器添加狀態欄支持,則表格查看器添加監聽器ISelectionChangedListener。
“插件是為系統提供功能的代碼和/或數據的結構化包。可以以代碼庫(帶有公共 [應用程序接口] API 的 Java 類)、平臺擴展甚至文檔的形式來提供功能。插件可以定義擴展點、定義良好的位置,其他插件可以在這些位置添加功能。”
Eclipse使用OSGi作為插件系統的基礎。動態添加新插件和停止現有插件的能力。以動態方式管理組件生命周期的一個健壯的系統。
OSGi是基于Java的框架,旨在用于需要長運行時間、動態更新和對運行環境破壞最小的系統。
OSGi規范定義了綁定包生命周期的基礎架構和綁定包的交互方式。這些規則通過使用特殊 Java類加載器來強制執行。
在一般Java應用程序中,CLASSPATH中的所有類都對所有其他類可見。相反,OSGi類加載器基于OSGi規范和每個綁定包的manifest.mf文件中指定的選項來限制類交互。
在3.1之前版本的Eclipse中,在每個插件的plugin.xml文件中定義插件依賴關系以及擴展和擴展點。在使用OSGi的新版本Eclipse中,依賴關系信息被分解到manifest.mf文件中,而 plugin.xml文件只包含擴展和擴展點的XML定義。
插件級的依賴關系改為需要顯式導出和導入包的依賴關系。插件開發人員必須進行專門選擇來使插件中的功能可供外部使用。該限制允許內部包保留在內部,避免插件暴露不必要的類。
Eclipse的擴展和擴展點 可插拔
每一個希望被別的模塊擴展的模塊,都必須聲明一系列的擴展點,即插座;希望在這個模塊上擴展功能的模塊,需要按照擴展點的什么來編寫擴展,即插頭。擴展點提供服務,擴展是要服務。
延遲裝載,只有在一個插件被其他模塊調用的時候,才裝載到內存中。
通過將擴展的聲明和實現分離,eclipse實現類延遲裝載。
擴展點和擴展的聲明都是通過XML文件完成的,即清單文件MANIFEST.MF,描述了一個插件能夠做什么,而JAVA代碼則具體完成這些功能。系統啟動時,只需搜索清單文件,建立一張索引表,知道有哪些插件以及能夠提供什么服務。當eclipse第一次啟動時,eclipse運行時會遍歷plugins文件夾中的目錄,掃描每個插件的清單文件信息,并建立一個內部模型來記錄它所找到的每個插件的信息。
RCP包括基于OSGi的運行時框架Equniox,基于SWT/JFace的圖形模塊,eclipse平臺的UI和Runtime模塊。基于Eclipse的應用程序所需的最小插件集稱為Eclipse Rich Client Platform(RCP)
Platform Runtime 平臺運行庫是內核,它在啟動時檢查已安裝了哪些插件,并創建關于它們的注冊表信息。即在eclipse運行時發現和管理插件。為降低啟動時間和資源使用,它在實際需要任何插件時才加載該插件。除了內核外,其他每樣東西都是作為插件來實現的。
Workspace 工作區是負責管理用戶資源的插件。這包括用戶創建的項目、那些項目中的文件,以及文件變更和其他資源。工作區還負責通知其他插件關于資源變更的信息,比如文件創建、刪除或更改。
Workbench 工作臺為Eclipse提供用戶界面。它是使用標準窗口工具包(SWT)和一個更高級的 API(JFace)來構建的;SWT 是 Java 的 Swing/AWT GUI API 的非標準替代者,JFace 則建立在 SWT 基礎上,提供用戶界面組件。
插件可以扮演雙重角色,其他插件服務的使用者和其他插件服務的提供者。
manifest.mf
Bundle-Activator
該類用于啟動和停止綁定包。該類擴展 org.eclipse.core.runtime.Plugin,實現了 BundleActivator 接口。
Bundle-Version
該屬性指定綁定包的版本號。包導入和必需的綁定包規范可以包括綁定包版本號。
Export-Package
該屬性指定要公共暴露給其他插件的所有包。
Import-Package
該屬性指定要從必需插件中顯式導入的所有包。默認情況下,必須為要啟動的綁定包解析所有包。還可以將包導入指定為可選項,以支持包不存在的情況。顯式導入的類在 Require-Bundle 插件中的包之前解析。
Require-Bundle
該屬性指定要在給定綁定包中導入使用的綁定包及其已導出的包。指定的綁定包在顯式包導入之后解析。
---------------------- --------------------
|plugin A | |plugin B |
| --------------- | contibute | -------------- |
| | ext point p | <--------------- | extension | |
| --------------- |_ | -------------- |
| || | | | || |
| --------------- | |implement | -------------- |
| | interface I | <--|------------ | class C | |
| --------------- | | | -------------- |
---------------------- | ----------/ -------
| create, call |
----------------------|
你可以把extension point想為接口, 而我們擴展這些extension,其實就是實現了這個接口
假設plugin A定義了一個extension point, plugin B定義了一個extension,是基于plugin A的這個extension point的,它的實現類是class C,那eclipse啟動后, 會讀取每個plugin的配置plugin.xml,然后發現plugin B有一個基于ext point p的擴展,那它就會用interface I作為對象, 然后實例化一個class C, 就等于實現了這個extension
eclipse的內部實現
IPluginRegistry registry = Platform.getPluginRegistry();
IExtensionPoint extensionPoint = registry.getExtensionPoint(xpid);
//通過擴展點ID獲得擴展點
IExtension[] extensions = extensionPoint.getExtensions(); //獲得該擴展點的所有擴展
// For each extension ...
for (int i = 0; i < extensions.length; i++) {
IExtension extension = extensions[i];
IConfigurationElement[] elements = extension.getConfigurationElements();
//獲得擴展點配置元素
configurationElement[j].createExecutableExtension(“Class”);
//為每個回調對象創建實例
……
}
eclipse就是注冊每一個extension和extension point,然后用extension point來實例化它對應的那個extension。
發生事件并向其它對象請求處理的對象被稱為“事件對象”,而處理事件的對象被稱為“回調對象”。回調對象由擴展者插件定義,由宿主插件創建實例。
Eclipse各bundle使用各自的class loader,若需要引用其他bundle類來動態創建實例,則需要使用類所在的bundle的class loader。對org.eclipse.ui.popupMenus擴展點
1、 viewerContribution,也就是通過視圖的id。這樣只有視圖id和給定的id一致的時候,它的彈出菜單才會增加我們定義的菜單項。實現類必須實現IObjectActionDelegate接口
2、 objectContribution,通過對象類型。當在視圖里選定的對象類型和給定的一致時,在視圖的彈出菜單增加我們定義的菜單項。實現類必須實現IViewerActionDelegate接口
創建編輯器的上下文操作,targetID="#TextEditorContext",為eclipse默認文件編輯器的上下文菜單標識符。實現類必須實現IEditorActionDelegate接口
所有視圖必須實現org.eclipse.ui.IViewPart接口
IViewPart和IEditorPart都繼承自IWorkbenchPart
ResourceNavigator即可被實例化,又可以被繼承
ContentOutline和PropertySheet視圖不能被再次實例化也無法被繼承,它們僅有的實例是由工作臺創建并管理的,可以被引用。可以通過IWorkbenchPage的showView()方法顯示ContentOutline和PropertySheet。BookmarkNavigator和TaskList
視圖模式
VIEW_ACTIVATE
Show view mode that
indicates the view should be made visible and activated.
激活定義視圖的插件并在當前頁面中顯示視圖
VIEW_CREATE
Show view mode that
indicates the view should be made created but not necessarily be made visible.
VIEW_VISIBLE
Show view mode that
indicates the view should be made visible.
該視圖可見,但不會立即顯示該視圖
createPartControl(Composite)定義視圖的用戶界面。
TableViewer的責任是用特定的對象填充到Table窗體組件中。是我們最終在窗體要看到的對象。
IStructuredContentProvider和ITableLabelProvider的實現類是用來幫助TableViewer來完成這個任務的。
結構化內容提供者IStructuredContentProvider負責從輸入對象中提取對象,并把它們傳遞給表格查看器進行顯示。
標簽提供者LabelProvider將獲得內容提供者返回的一個表格行對象,并提取要顯示到列上的值。
所以,要實現一個在TableView中顯示特定對象只需要實現IStructuredContentProvider實現類的getElements方法。和ITableLabelProvider的getColumnText和getColumnImage方法。
查看器排序器ViewerSorter用于對內容提供者提供的元素進行排序。
視圖過濾器
獲取到的有關“當前選擇項”內容,先查找視圖,找到該視圖后,調用getSelection()得到當前選擇項,然后馬上轉換為IStructuredSelection,以后的操作就是統一的了,因為有IStructuredSelection接口。如果是在action中遇到這種需求,就將event調用getSelection(),然后轉換為IStructuredSelection,后續操作相同。
KeyEvent
Character, 就是按鍵對應字符
stateMask則是用來檢測Alt,Shift,Ctrl這些鍵有沒有同時被按下。stateMask與這些鍵的鍵碼進行位與運算,如果得到的結果不是0就說明這些鍵被按下了,比如如果stateMake & SWT.ALT不為零,我們就可以認為Alt鍵被按下了。
屬性視圖支持所有實現IResource接口的資源。
Java是一種強類型語言,每個實例都必須有指定的類型。實際上,Java類型有兩種聲明類型和運行時類型(也可以相應的說是靜態類型 和動態類型)。
可以把IAdaptable想象為一個能夠動態進行類型轉換的途徑。其實這種機制可以讓我們將目標類轉換為它并沒有實現的接口。
大部分IAdaptable的實現是一些if語句的疊加,實現getAdapter()方法。getAdapter()方法的調用者需要傳入一個代表特定接口的Class對象,該方法則會返回一個具有該接口的對象。
在Eclipse中,比較標準的做法是通過ISelectionProvider和ISelectionListener來完成的。Eclipse為了解決這個問題,提供了所謂的Site,以及ISelectionService機制,來處理試圖之間的簡單的交互。簡單的說,ViewSite提供了一個交互的中心點,其它View向ViewSite提供選擇事件,或者向其注冊監聽器,而事件的觸發與轉發則由ViewSite()來完成。
注意SelectionProvider和SelectionListener并不直接對應。這個地方有一點容易混淆,就是Eclipse實際上提供有兩套與Selection相關的事件與接口:
ISelectionChangedListener <--> ISelectionProvider
這個是JFace中的選擇監聽機制,對試圖或者控件而言,它提供對原始的選擇事件的通知與響應。ISelectionProvider需要注冊在Site上,以供ISelectionSerivce使用,將選擇事件擴散到其他的ISelectionListener中。
ISelectionListener <--> ISelectionService
這個是在Site中使用的,ISelectionService不需要自己實現,已經實現好了,ISelectionListener則需要注冊到ISelectionService上,以對其它SelectionProvider的事件響應進行監聽。
在Eclipse本身的實現中,PropertySheet和Outline都使用了這種機制。不過需要注意的是,缺省的PropertySheet需要接受一個IStructuredSelection,而不僅僅是一個ISelection。因此,如果ISeletionProvider需要提供一個能夠讓PropertySheet進行顯示的對象的話,除了除了要實現ISelection接口外,還需要對其進行封裝成一個IStructuredSelection。這個比較簡單,直接調用StructuredSelection構造函數即可。
Protected類型不希望其他類使用該方法。
A site maintains the context for a part,
including the part, its pane, active contributions, selection provider, etc.
Together, these components make up the complete behavior for a
part as if it was implemented by one person.
The PartSite lifecycle is as follows ..
(1) a site is constructed
(2) a part is constructed and stored in the part
(3) the site calls part.init()
(4) a pane is constructed and stored in the site
(5) the action bars for a part are constructed and stored in the site
(6) the pane is added to a presentation
(7) the SWT widgets for the pane and part are created
(8) the site is activated, causing the actions to become visible
以前做插件用到的ActionSet都只是依葫蘆畫瓢,沒有真正理解它,現在開始好好學習學習,主要是看"Building
Commercial-Quality Plug-ins"寫的
Action的組成包括幾個部分,一個是在plugin.xml中的聲明,一個是在Eclipse UI中會用到的IAction實例化對象, 另一個是封裝在IActionDelegate中的action執行代碼. action的實例化是基于plugin.xml配置文件和IActionDelegate定義的,Eclipse可以先在界面上將action顯示出 來,直到用戶點擊了菜單或者工具條按鈕才會去真正的加載插件,這也就是所謂的Eclipse的延遲加載機制
IActionDelegate有幾個子類需要說明一下:
IActionDelegate2 如果實現IActionDelegate需要一些其他的信息,而這些信息在IActionDelegate銷毀時需要同時被銷毀時, 它可以提供相關是生命周期事件方法來處理
IEditorActionDelegate 是跟editor相關的
IObjectActionDelegate 是跟上下文菜單相關的
IViewActionDelegate 是跟view相關的
IWorkbenchWindowActionDelegate是跟menubar和toolbar相關的
Actions和Eclipse中的其他東東一樣,也是通過擴展點org.eclipse.ui.actionSets來添加到Eclipse IDE的各個地方,actions的外在表現形式主要有menubar, toolbar, context
menu這樣幾種
在actionSets的下級節點是actionSet, 它有幾個屬性,id,label都不用說了,還有一個visible屬性,表示在打開Eclipse時是否顯示該action,通常我們可以通過 Window > Customize Perspective...來設置action是否可見
在actionSet下可以添加menu節點, 它的id,label屬性不用說了,其中的path是用來指定菜單的顯示位置的,一般設置為additions
action并不是直接添加到menu上的, 而是跟menu內部的group關聯的,所以我們必須先在menu下創建group,即新建groupMarker,
其name必須保證在當前menu下唯一, menu還有一種叫separator的group, 在separator group處添加的menu item會在這個組中的第一個菜單項上面添加一條水平線.而groupMarker則不會有線, 當其他action要添加到該menu group下時,使用水平線進行分組就顯得非常必要
group只是用來標識menu的位置,即指這個位置可以添加菜單項, 而不是實際的menu item, 實際的menu item是action
因為menu會有層次關系,所以actionSet中專門用一個menu節點來表現這種層次關系, 而toolbar則沒有這種情況,所以這里沒有提供toolbar節點
在actionSet下添加action就是最終我們要toolbar上顯示的按鈕和menubar上的menu item, 這里有幾個屬性, id和label不用說了,menuBarPath是menu要顯示的位置, 比如"com.qualityeclipse.favorites.workbenchMenu/content",這個值就是actionSet+" /"+group name, toolbarPath 就是toolbar item的顯示位置, 比如"Normal/additions", 還有幾個屬性不常用到,但是有必要解釋一下
allowLabelUpdate這個要在retarget屬性為true的時候有效,還不是很清楚
class 是menu或button要執行的動作, 如果使用的是pulldown風格的action,還需要實現IWorkbenchWindowPulldownDelegate接口,它的構造函數是無
參數的,如果要傳遞參數,還需要是要實現IExecutableExtension 接口, 這個接口還不是很清楚
definitionId 這個是用來指定action對應的command唯一標識符的
enablesFor 是一個表達式,用來表示當前action是否可用
retarget 不是很理解
state主要是針對style屬性為radio和toggle的,表示當前action是否被選中
style:默認是push,還有radio,表示一組menu中只能有一個被選中, toggle當它是checkbox好了, pulldown說明是子菜單項或者是下拉toolbar menu
對于action所使用的圖標,我們可以從Eclipse下的plugins\org.eclipse.ui_3.1.2.jar 和 plugins\org.eclipse.jdt.ui_3.1.2.jar得到一些gif的圖片
Eclipse中actionSet以及下級元素都是有id的,而且他們可以通過一種path的方式來進行指定,比如 com.qualityeclipse.favorites.workbenchMenu/content,如果上一級節點是workbench menu bar或者是view content menu的話,那么它的上一級節點是不用指定的,只要指定group就好
為了方便其他的插件的action能添加到該插件的actionSet下, 在為插件定義group的時候, 會添加一個為additions的group, additions是eclipse使用的一個默認標識符,用來表示其他action或menu顯示的位置, 在class中它的對應常量是IWorkbenchActionConstants.MB_ADDITIONS,
比如我們定義了一個menu,指定其path為additions, 這表示我們的menu將添加到window的左邊,因為這個就是workbench
menubar的additons所在的位置, 如果指定的是window/additions, 那么將作為子菜單項添加到window下
當一個actionSet下定義的action跟另一個actionSet下定義的menu關聯的時候將發生Invalid Menu Extension (Path is invalid): some.action.id的異常, 為了避免這種問題,menu需要在兩個actionSet都定義
toolbarPath的指定也跟menuBarPath有同樣的情況, 比如定義為Normal/additions, 這里的Normal指的是workbench toolbar, additions就是action要顯示的group位置
action的class屬性是封裝了執行動作的類,它實現了IWorkbenchWindowActionDelegate, 在它里面有幾個方法需要介紹一下, 一個是selectionChanged, 用來動態修改action的state狀態值, 是否可用,以及action的text屬性等, 比如對于enableFor屬性來說,
它根據選中對象的數量來判斷action是否可用, 但是通過selectionChanged可以通過當前選定的對象的情況來調用setEnable()方法從而更精確的控制action是否可用, 有時候當插件還沒有加載,在調用action的run方法時會加載插件,這時可能在調用run()方法之前沒有調用selectedChange方法, 因此在run()方面里面需要寫一些防護性代碼,比如空值的判斷之類的, 另外run()是在主UI線程中執行的,因此,如果該執行耗時較長,需要將其放到后臺線程中執行.
插件運行之后在menubar和toolbar上看到定義的actionSet,可以試試下面的方法:
在Window > Customize Perspective... 下看看是不是actionSet沒有選中
使用Window > Reset Perspective刷新當前視圖
關閉重新打開當前視圖
如果還沒有出來,可以在在run或者debug的設置中將"Clear workspace data before
launching "選中, 然后再啟動運行
雖然通過擴展點可以非常方便的在Eclipse上添加,但是這個玩意兒多了也不是什么好事, 可能會降低用戶體驗,因此需要進行控制,
actionSet擴展點也有相關的設置來指定是否可見, 而且還可以調用IActionSetDescriptor.setInitiallyVisible() 來控制頂層菜單是否可見, 另一種解決辦法就是在首選項中提供一個checkbox讓用戶決定是否顯示頂層菜單,其編程實現就是在代碼中調用
IActionSetDescriptor.setInitiallyVisible()和
IWorkbenchPage.hideActionSet()來進行控制
來自:http://hi.baidu.com/xianle/blog