轉載請註明出處:【huachao1001的專欄:http://blog.csdn.net/huachao1001/article/details/53883500】html
從上一篇《AndroidStudio插件開發(Hello World篇)》中咱們已經大體瞭解了Action,這篇文章繼續深刻探究IntelliJ IDEA插件開發中的Action機制。一個Action本質上來講就是一個Java類,而且這個類須要繼承AnAction。而一個Action對應於一個菜單項,每一次點擊這個菜單項就回調這個Action的actionPerformed(AnActionEvent event)
函數,所以咱們定義的Action在繼承AnAction時,須要重寫actionPerformed函數。定義好Action類後,咱們須要註冊Action,即在plugin.xml文件中添加Action對應的標籤,在這個標籤中定義了Action應放置在界面的的哪一個位置,做爲哪一個菜單項的子項等。接下來咱們對Action機制進行深刻。java
1. 定義Action(繼承AnAction)
定義Action只需簡單地定義一個繼承AnAction的子類便可,子類中,最重要的就是actionPerformed函數和update函數。git
1.1 重寫actionPerformed函數
咱們知道,每次在菜單項中點擊咱們自定義的Action時,對應會執行AnAction的actionPerformed函數。對應actionPerformed函數的理解,只需記住,當回調actionPerformed函數函數時,就意味着當前Action被點擊了一次。重寫actionPerformed函數很是簡單,這裏簡單彈出一個Hello World。github
package com.huachao.plugin; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; /** * Created by huachao on 2016/12/26. */ public class MyAction extends AnAction { @Override public void actionPerformed(AnActionEvent event) { Project project = event.getData(PlatformDataKeys.PROJECT); Messages.showMessageDialog(project, "Hello World!", "Information", Messages.getInformationIcon()); } }
1.2 重寫update函數
咱們知道,爲了響應用戶的點擊操做,咱們重寫了actionPerformed函數。在actionPerformed函數中執行一些邏輯,好比彈出對話框,在打開文件中自動生成代碼等等操做。這些邏輯在actionPerformed函數中完成就好,可是,有時候咱們定義的插件只在某些場景中使用。好比說,當咱們編寫自動生成代碼的插件時,只有當有文件打開時才能夠正常執行。所以,當咱們不但願用戶點擊咱們定義的插件時,咱們能夠將插件隱藏,讓用戶沒法看到插件,只有當符合插件執行的環境時,才讓插件在菜單中顯示。api
爲了能在用戶點擊自定義插件對應的菜單項以前動態判斷是否將插件項顯示,只需重寫update函數。dom
update函數在Action狀態發生更新時被回調,當Action狀態刷新時,update函數被IDEA回調,而且傳遞AnActionEvent對象,AnAction對象中封裝了當前Action對應的環境。ide
如何理解上面這段話呢?咱們知道,咱們定義的每一個Action都在菜單中對應一個子選項(爲了方便描述,本文稱之爲Action菜單項),當Action菜單項被點擊或者是Action的父菜單(包含Action菜單項的菜單)被點擊使得Action菜單項被顯示出來時,就會回調update函數。在update被回調時,傳入AnActionEvent對象,經過AnActionEvent對象咱們能夠判斷當前編輯框是否已經打開等實時IDEA環境情況。函數
注意:先執行update函數,再執行actionPerformed函數。換言之,update發生在actionPerformed以前。ui
好比,咱們想要實現:當編輯框被打開時顯示自定義的Action菜單項,不然,將Action菜單項設置爲灰色。url
@Override public void update(AnActionEvent e) { Editor editor = e.getData(PlatformDataKeys.EDITOR); if (editor != null) e.getPresentation().setEnabled(true); else e.getPresentation().setEnabled(false); }
代碼中,若是editor!=null
即編輯框已打開,將Action菜單項設置爲可用狀態(即正常顏色,黑色),不然設置爲不可用狀態(即灰色)。固然了,你也能夠經過e.getPresentation().setVisible(false);
將Action菜單項設置爲不可見,這樣Action菜單項就不會出如今菜單中。
另外,不要忘記在plugin.xml中將MyAction註冊,具體註冊方法能夠參考上一篇文章《AndroidStudio插件開發(Hello World篇)》或者後一節的詳細介紹。
當編輯框被打開時(即有文件打開時),能夠看到咱們自定義的插件是正常。
當編輯框被關閉時(即沒有文件被打開時),能夠看到咱們自定義的插件是灰色。
注意:Action菜單項爲灰色並不意味着被點擊時actionPerformed不會被調用,相反,只要Action菜單項被點擊,actionPerformed函數就會被調用。所以若是但願點擊Action菜單項時不作響應,須要在actionPerformed函數裏面再次作具體判斷。
1.3 關於AnActionEvent
前面咱們屢次用到了AnActionEvent 對象,AnActionEvent 函數和update函數的形參都包含AnActionEvent對象。AnActionEvent對象是咱們與IntelliJ IDEA交互的橋樑,咱們能夠經過AnActionEvent對象獲取當前IntelliJ IDEA的各個模塊對象,如編輯框窗口對象、項目窗口對象等,獲取到這些對象咱們就能夠作一些定製的效果。
1.3.1 getData函數
經過AnActionEvent對象的getData函數能夠獲得IDEA界面各個窗口對象以及各個窗口爲實現某些特定功能的對象。getData函數須要傳入DataKey<T>
對象,用於指明想要獲取的IDEA中的哪一個對象。在CommonDataKeys
已經定義好各個IDEA對象對應的DataKey<T>
對象。
CommonDataKeys.java
定義的DataKey<T>
對象以下:
public static final DataKey<Project> PROJECT = DataKey.create("project"); public static final DataKey<Editor> EDITOR = DataKey.create("editor"); public static final DataKey<Editor> HOST_EDITOR = DataKey.create("host.editor"); public static final DataKey<Caret> CARET = DataKey.create("caret"); public static final DataKey<Editor> EDITOR_EVEN_IF_INACTIVE = DataKey.create("editor.even.if.inactive"); public static final DataKey<Navigatable> NAVIGATABLE = DataKey.create("Navigatable"); public static final DataKey<Navigatable[]> NAVIGATABLE_ARRAY = DataKey.create("NavigatableArray"); public static final DataKey<VirtualFile> VIRTUAL_FILE = DataKey.create("virtualFile"); public static final DataKey<VirtualFile[]> VIRTUAL_FILE_ARRAY = DataKey.create("virtualFileArray"); public static final DataKey<PsiElement> PSI_ELEMENT = DataKey.create("psi.Element"); public static final DataKey<PsiFile> PSI_FILE = DataKey.create("psi.File"); public static final DataKey<Boolean> EDITOR_VIRTUAL_SPACE = DataKey.create("editor.virtual.space");
不只僅CommonDataKeys
中定義了DataKey<T>
對象,爲了添加更多的DataKey<T>
對象而且兼容等,又提供了PlatformDataKeys
類,PlatformDataKeys
類是CommonDataKeys子類,也就是說,只要是CommonDataKeys
有的,PlatformDataKeys
類都有。
PlatformDataKeys.java
定義的DataKey<T>
對象以下:
public static final DataKey<FileEditor> FILE_EDITOR = DataKey.create("fileEditor"); public static final DataKey<String> FILE_TEXT = DataKey.create("fileText"); public static final DataKey<Boolean> IS_MODAL_CONTEXT = DataKey.create("isModalContext"); public static final DataKey<DiffViewer> DIFF_VIEWER = DataKey.create("diffViewer"); public static final DataKey<DiffViewer> COMPOSITE_DIFF_VIEWER = DataKey.create("compositeDiffViewer"); public static final DataKey<String> HELP_ID = DataKey.create("helpId"); public static final DataKey<Project> PROJECT_CONTEXT = DataKey.create("context.Project"); public static final DataKey<Component> CONTEXT_COMPONENT = DataKey.create("contextComponent"); public static final DataKey<CopyProvider> COPY_PROVIDER = DataKey.create("copyProvider"); public static final DataKey<CutProvider> CUT_PROVIDER = DataKey.create("cutProvider"); public static final DataKey<PasteProvider> PASTE_PROVIDER = DataKey.create("pasteProvider"); public static final DataKey<DeleteProvider> DELETE_ELEMENT_PROVIDER = DataKey.create("deleteElementProvider"); public static final DataKey<Object> SELECTED_ITEM = DataKey.create("selectedItem"); public static final DataKey<Object[]> SELECTED_ITEMS = DataKey.create("selectedItems"); public static final DataKey<Rectangle> DOMINANT_HINT_AREA_RECTANGLE = DataKey.create("dominant.hint.rectangle"); public static final DataKey<ContentManager> CONTENT_MANAGER = DataKey.create("contentManager"); public static final DataKey<ToolWindow> TOOL_WINDOW = DataKey.create("TOOL_WINDOW"); public static final DataKey<TreeExpander> TREE_EXPANDER = DataKey.create("treeExpander"); public static final DataKey<ExporterToTextFile> EXPORTER_TO_TEXT_FILE = DataKey.create("exporterToTextFile"); public static final DataKey<VirtualFile> PROJECT_FILE_DIRECTORY = DataKey.create("context.ProjectFileDirectory"); public static final DataKey<Disposable> UI_DISPOSABLE = DataKey.create("ui.disposable"); public static final DataKey<ContentManager> NONEMPTY_CONTENT_MANAGER = DataKey.create("nonemptyContentManager"); public static final DataKey<ModalityState> MODALITY_STATE = DataKey.create("ModalityState"); public static final DataKey<Boolean> SOURCE_NAVIGATION_LOCKED = DataKey.create("sourceNavigationLocked"); public static final DataKey<String> PREDEFINED_TEXT = DataKey.create("predefined.text.value"); public static final DataKey<String> SEARCH_INPUT_TEXT = DataKey.create("search.input.text.value"); public static final DataKey<Object> SPEED_SEARCH_COMPONENT = DataKey.create("speed.search.component.value"); public static final DataKey<Point> CONTEXT_MENU_POINT = DataKey.create("contextMenuPoint");
1.3.2 Presentation對象
一個Presentation對象表示一個Action在菜單中的外觀,經過Presentation能夠獲取Action菜單項的各類屬性,如顯示的文本、描述、圖標(Icon)等。而且能夠設置當前Action菜單項的狀態、是否可見、顯示的文本等等。經過AnActionEvent對象的getPresentation()函數能夠取得Presentation對象。
2. 註冊Action(修改plugin.xml)
註冊Action,咱們能夠手動直接修改plugin.xml文件,也可由IDEA直接自動幫咱們生成,甚至是經過代碼動態註冊。其中,我的認爲必須把手動註冊過程掌握透徹,這樣就能理解自動註冊與代碼註冊的原理。
2.1 手動註冊Action
2.1.1 單個Action
手動註冊即咱們直接修改plugin.xml
文件,在plugin.xml
文件(resoutces/META-INF/plugin.xml
)中找到<actions>
標籤,並在<actions>
標籤中添加<action>
標籤。<action>
標籤的屬性在上一篇文章中解釋過,這裏再解釋一遍:
id:做爲
<action>
標籤的惟一標識。通常以<項目名>.<類名>
方式。
class:即咱們自定義的AnAction類
text:顯示的文字,如咱們自定義的插件放在菜單列表中,這個文字就是對應的菜單項
description:對這個AnAction的描述
<add-to-group>
標籤用於描述當前Action放入到那個菜單組中,<add-to-group>
標籤主要關注anchor屬性和relative-to-action屬性。anchor屬性用於描述位置,主要有四個選項:first、last、before、after。他們的含義以下:
first:放在全部子菜單的最前面
last:放在全部子菜單的最後
before:放在relative-to-action屬性指定的ID的子菜單的前面
after:放在relative-to-action屬性指定的ID的子菜單的後面
<keyboard-shortcut>
標籤用於描述快捷鍵,主要關注2個屬性:keymap和first-keystroke。keymap使用默認值($default)就好,first-keystroke用於指定快捷鍵。
將Action菜單項放入到Help菜單的最前面,示例以下:
<actions> <!-- Add your actions here --> <action class="com.huachao.plugin.MyAction" id="StudyAction.MyAction" text="Hello Action"> <add-to-group group-id="HelpMenu" anchor="first"/> <keyboard-shortcut keymap="$default" first-keystroke="ctrl alt Q"/> </action> </actions>
2.1.2 Action組(Action Group)
前面咱們都是將一個Action放入到已有的菜單中做爲子選項。如今咱們定義一個跟Help同級的菜單,或者是定義包含多個子選項的菜單,這就是Action Group。使用Action Group很是簡單,就是在<actions>
標籤中添加<group>
子標籤,<group>
標籤主要關注3個屬性:id、text、popup。id和text跟<action>
標籤意義同樣,再也不解釋,但須要注意,text中若是須要首字母加下劃線,則開頭下「_」便可。popup屬性用於描述是否有子菜單彈出,若是取值爲true
,則<group>
標籤的內全部的<action>
子標籤做爲<group>
菜單的子選項,不然,<group>
標籤的內全部的<action>
子標籤將替換<group>
菜單項所在的位置,即沒有<group>
這一層菜單。下面經過一個例子進行對比。
<actions> <group id="StudyAction.MyGroup" text="_MyGroup" popup="true"> <add-to-group group-id="HelpMenu" anchor="first"/> <action class="com.huachao.plugin.MyAction" id="StudyAction.MyAction" text="Hello Action"> <keyboard-shortcut keymap="$default" first-keystroke="ctrl alt Q"/> </action> <action id="StudyAction.SecondAction" class="com.huachao.plugin.SecondAction" text="SecondAction"/> </group> </actions>
運行結果以下:
將popup屬性改成false
運行結果以下:
注意到,咱們將<group>
的<add-to-group>
子標籤的group-id屬性依然指定爲Help菜單,如今咱們換成與Help同級。將group-id屬性指定爲MainMenu
,運行以下:
能夠看到,IDEA的全部的導航菜單都放在MainMenu中,咱們指定了anchor="first"
,所以被加入第一個位置。接下來咱們再看看將group加入到編輯框窗口右鍵菜單,只需將group-id屬性指定爲EditorPopupMenu
,運行以下:
修改成項目窗口右鍵菜單,修改group-id爲:ProjectViewPopupMenu。運行以下:
2.2 IDEA自動註冊Action
在咱們熟悉了手動修改plugin.xml後,使用IDEA的方式就更簡單了。直接點擊在包目錄上右擊>New>Action。彈出框對應填寫屬性便可,這樣在自動建立Action的同時,完成了Action的註冊。
2.3 代碼動態註冊Action
代碼動態註冊Action主要是以Action Group動態添加和移除Action。前面咱們在使用<group>
標籤時,沒有使用到class
屬性,即咱們沒有定義本身的Action Group,而是使用默認的Action Group(DefaultActionGroup)。爲了定製本身的Action Group,咱們定義MyGroup類,使之繼承ActionGroup類,並在<group>
標籤的class
屬性中指定com.huachao.plugin.MyGroup
。
package com.huachao.plugin; import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** * Created by huachao on 2016/12/26. */ public class MyGroup extends ActionGroup { @NotNull @Override public AnAction[] getChildren(@Nullable AnActionEvent anActionEvent) { return new AnAction[]{new CustomAction("first"),new CustomAction("second")}; } class CustomAction extends AnAction { public CustomAction(String text) { super(text); } @Override public void actionPerformed(@NotNull AnActionEvent anActionEvent) { } } }
plugin.xml文件中對應的<actions>
標籤以下:
<actions> <group id="StudyAction.MyGroup" class="com.huachao.plugin.MyGroup" text="_MyGroup" popup="true"> <add-to-group group-id="MainMenu" anchor="last"/> </group> </actions>
運行結果以下:
若是咱們想在plugin.xml中註冊Action,而且想修改Group的菜單屬性。咱們只需重寫DefaultActionGroup的update函數,DefaultActionGroup的update函數與AnAction的update函數意義差很少,前面解釋過AnAction的update函數,這裏就再也不解釋。例如,咱們將Group菜單添加一個圖標,代碼以下:
package com.huachao.plugin; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.editor.Editor; /** * Created by huachao on 2016/12/26. */ public class MyGroup extends DefaultActionGroup { @Override public void update(AnActionEvent e) { Editor editor = e.getData(CommonDataKeys.EDITOR); e.getPresentation().setVisible(true); e.getPresentation().setEnabled(editor != null); e.getPresentation().setIcon(AllIcons.General.Error); } }
運行結果以下:
3. 總結
定義一個AndroidStudio插件只需簡單的2步:
- 定義Action
- actionPerformed()
- update()
- AnActionEvent對象
- 註冊Action
- 手動修改plugin.xml
- Action Group
- IDEA自動生成(New>Action>…)
- 代碼註冊(經過Acton Group動態添加)
相比上一篇文章,在本文中,咱們知其然更知其因此然。爲後面定製AndroidStudio打下基礎。
參考資料
Action System相關類源碼(Github):《intellij-community》
官網資料:http://www.jetbrains.org/intellij/sdk/docs/tutorials/action_system.html