操作(Actions)

走着路睡觉大约 13 分钟

操作(Actions)

IntelliJ Platform允许用户定制菜单和工具栏。例如 File | Open File..菜单 和 Open... 工具按钮

在IntelliJ Platform注册了实现的Action后,用户执行相关操作时,将会调用Action的实现类

新建Actions 教程是如何新建一个Action

分组Actions 教程主要是介绍Action分组的3种类型

实现Action

新建Action需要继承抽象类AnActionopen in new window

注意

AnAction的子类不能有任何类型的成员变量,这是因为 AnAction 子类的实例存在于应用程序的整个生命周期中。如果AnAction 子类使用成员变量存储了某些数据,但是没有及时清除,将会发生数据泄漏。

需要重写的方法

必须重写 AnAction.actionPerformed()方法,如果需要也可以重写 AnAction.update()方法。

  • AnAction.update(): IntelliJ Platform更新Action状态(state)的时候会调用AnAction.update() 方法,状态(state)有2个值enabled, visible,可以控制是否显示该Action。调用该方法的时候会传入AnActionEventopen in new window 并包含当前Action的上下文信息。通过改变事件上下文中Presentationopen in new window 的状态来启用或禁用该Action,该方法需要很快的执行速度

  • AnAction.actionPerformed() :当Action处于可用状态,并被用户使用的时候,会调用该Action的 AnAction.actionPerformed() 方法。调用该方法的时候会传入AnActionEventopen in new window ,可以获取到上下文信息,例如 projects, files, selection等。

重写AnAction.update()方法

IntelliJ Platform会定期调用 AnAction.update() 方法。这个方法会评估Action的状态来确定禁用/启用该Action。该方法必须保证能执行完成(不会异常),否则,该Action将会 "stuck"(卡住)

注意

AnAction.update() 方法将在 UI 线程上频繁调用。该方法必须很快的执行,不要执行耗时长的业务操作

提示

如果不能很快的确定并修改action的状态,那么应该在该Action的 AnAction.actionPerformed()方法中进行判断,并通知用户如果不满足某些条件,该Action将不会执行

确定Action上下文

调用AnAction.update() 方法的时候,会传入AnActionEvent 参数,通过AnActionEvent 参数可以获取到Action的上下文信息,例如 Presentation,以及该Action是否是从哪里触发的。也可以通过 AnActionEvent.getData() 方法获取其它信息。AnActionEvent.getData() 方法传入不同的key可以获取到不同的信息,可以在CommonDataKeysopen in new window 中查看所有的key,包含 Project, Editor, PsiFile等。获取此类信息执行速度很快,可以在AnAction.update() 方法中调用 (AnAction.update() 方法要尽可能的快,不要执行耗时长的操作)

激活Action

根据Action的上下文信息,AnAction.update() 可以激活,禁用,或隐藏该Action。Action的状态通过 AnActionEvent.getPresentation() 方法获取到Presentation对象来进行控制,

默认的Presentation 对象是一组有关菜单或工具栏操作的描述性信息。所有的Action对象(菜单,工具栏,导航搜索等)都拥有一个唯一的PresentationPresentation对象里包含Action的文本,描述,图标,是否可见和状态(enable/disable)等。Presentation 的属性从注册Action的信息中初始化的。

Presentation.setEnabled() 可以修改Action的enabled/disabled状态

Presentation.setVisible() 可以修改Action是否可见

如果Action的状态是enabled,用户触发该Action 的时候将会调用AnAction.actionPerformed() 方法。菜单Action的显示位置根据Action的注册信息来确定。工具栏Action会显示它的图标

如果Action的状态是disabledAnAction.actionPerformed() 将不会被执行,菜单Action会显示他的disabled状态的图标。

如果Action 配置了compact 属性(compact=true),那么,该Action被禁用的时候,将不会显示在工具栏中(compact属性相关查看分组Actions

提示

有任何用户活动或焦点转移时都会调用工具栏中Action的 update()方法。如果没有任何用户活动或焦点转移,Action可用性发生变化,可以调用 ActivityTracker.getInstance().inc() 以通知操作子系统更新所有工具栏操作。

示例 PopupDialogAction.update()open in new window 演示了根据项目是否打开来启用菜单操作的示例。

重写AnAction.actionPerformed方法

用户选择某个菜单或工具栏的时候,该菜单或工具栏绑定的 Action 中的 AnAction.actionPerformed 方法将会被执行,这个方法执行真正的业务逻辑。

使用 AnActionEvent.getData(CommonDataKeys) 可以获取到 Project, Editor, PsiFile等信息。例如:可以在 AnAction.actionPerformed 方法中修改,删除或在打开的文档中增加 PSI(Program Structure Interface)元素。

AnAction.update() 相比,AnAction.actionPerformed方法对执行速度要求并没那么严格,但依然需要尽量提升执行速度。

action_basics PopupDialogAction.actionPerformed()open in new window 示例中演示了检查 PSI 元素的功能

Action IDs

每一个 action和action group都有唯一的id. IntelliJ Platform中已定义的 Action ID可以在 IdeActionsopen in new window 中找到

分组Actions

Actions可以进行分组,多个Action组合成一个工具栏或一个菜单。

Action可以同时被加入多个分组,从而出现在不同的地方。同一个Action可以注册在不同的地方,但是需要配置不同的id。

Presentation

配置在不同位置的同一个Action具有不同的 Presentationopen in new window 对象。所以同一个Action出现在不同的地方的时候,有不同的文本和图标。通过 AnAction.getTemplatePresentation() 方法可以为Action复制不同的 Presentation

Compact属性

Group Action的 compact 属性可以控制Actiondisabled 状态时是否显示在工具栏中。在 注册Action 可以查看在注册Action Group时如何设置compact 属性。 compact 设置为true时,Action 只有在enabledvisible 状态时才显示。

分组Actions 教程中提供了示例

注册Action

有2种方式可以注册Action

  • plugin.xml中注册
  • 通过代码注册

在plugin.xml中注册

下面的示例介绍了如何在 plugin.xml中注册Action

设置显示文本

从2020.1版本开始,可以根据Action出现的位置配置不同的文本。根据Action的位置(如菜单,工具栏等),可以使用 <override-text> 为Action定义不同的文本。 在2020.3及之后的版本中 Group Action也支持 <override-text> 元素

详情查看下面示例

设置Action别名

2020.3版本以后,用户可以通过 Help -> Find Action 来定位Action. 如果需要通过别名搜索到该Action,可以在 <action><reference> 元素中配置一个或多个 <synonym> 元素

<action id="MyAction" text="My Action Name" >
<!--    如果text要国际化,需要把 text属性替换成 key属性, 属性值配置为国际化文本的key (国际化文本的key) ,如何定义key,见下文-->
<!--    <synonym key="MyAction.synonym.key"/>-->
  <synonym text="Another Search Term"/> 
</action>

如果文本需要国际化,在你的国际化语言包 定义key

禁用组搜索

2020.3以后,设置 searchable="false",用户通过 Help -> Find Action results 搜索Action时,将搜索不到当前 Action

Actions和Groups的国际化

Action 和 group的国际化,需要在国际化配置文件 $NAME$Bundle.properties 里定义对应的key。可以参考示例插件action_basicsopen in new window

国际化的文本需要配置在资源包里

国际化的文本资源包需要在 plugin.xml 中声明,示例插件action_basicsopen in new window 中,只提供了一个国际化文本资源包,如下所示:

<resource-bundle>messages.BasicActionsBundle</resource-bundle>

如果需要在 2020.1版本中定义国际化文本资源包,需要在Action中进行配置,如下所示:

<actions resource-bundle="messages.MyActionsBundle">
    <!-- action/group defined here will use keys from MyActionsBundle.properties -->
</actions>

Action注册参考示例

ActionPlacesopen in new window 类中的常量定义了Action 可以配置的的显示位置

IntelliJ Platform中的Action都在 PlatformActions.xmlopen in new window 中进行了注册

通过 Code Completionopen in new window , Quick Definitionopen in new windowQuick Documentationopen in new window 功能可以查看更多相关信息

提示

通过 UI Inspector 可以查找平台的Action Id

plugin.xmlActionGroup 的配置如下:

<actions>

  <!--
  注册Action,配置描述如下
   - "id" (必填) - action id,需要保持IDE中的唯一性
   - "class" (必填) - action实现类的全限定命名
   - "text" (必填) - 菜单中的action显示名称,工具栏action的文本提示 
   - "use-shortcut-of" (可选) - 快捷键
   - "description" (可选) - action获得焦点时,显示的描述文本
   - "icon" (可选) - 图标
  -->
  <action
      id="VssIntegration.GarbageCollection"
      class="com.example.impl.CollectGarbage"
      text="Garbage Collector: Collect _Garbage"
      description="Run garbage collector"
      icon="icons/garbage.png">
    <!--
    <override-text>为菜单定义了别名:
     - "text" (必填) - action的显示文本
     - "place" (必填) - action的显示位置,下面的示例中,当Action显示在主菜单时,会显示为 “Collect _Garbage"
    第二个 <override-text> 的配置表示,当Action显示在EditorPopup时,和在主菜单中显示的文本保持一致
    -->
    <override-text place="MainMenu" text="Collect _Garbage"/>
    <override-text place="EditorPopup" use-text-of-place="MainMenu"/>

    <!-- 搜索action时,可以用 GC进行搜索 -->
    <synonym text="GC"/>

    <!--
    配置<add-to-group> 属性,可以把Action加入某个group里,一个action可以加入多个group
     - "group-id" (必填) - 要加入的group的id,该group必须实现 DefaultActionGroup 类
     - "anchor" (必填) - action在group中的位置,可以配置为: "first", "last","before", and "after".
     - "relative-to-action" 如果anchor配置为"before" 或 "after"时,必须配置该属性,表示位于该属性的before或after
    -->
    <add-to-group
        group-id="ToolsMenu"
        relative-to-action="GenerateJavadoc"
        anchor="after"/>

    <!--
    <keyboard-shortcut> 该action的快捷键,一个Action可以配置多个快捷键
     - "first-keystroke" (必填) - 快捷链
     - "second-keystroke" (可选) - 第2种快捷键
     - "keymap" (可选) - 键盘映射(keymap), com.intellij.openapi.keymap.KeymapManager定义了keymap的列表
    -->

    <!-- 给所有的keymap增加第一,第二快捷键 -->
    <keyboard-shortcut
        keymap="$default"
        first-keystroke="control alt G"
        second-keystroke="C"/>

    <!-- keymap为 Mac OS X时生效
     - "remove" (可选) - 移除配置的快捷锓 
    -->
    <keyboard-shortcut
        keymap="Mac OS X"
        first-keystroke="control alt G"
        second-keystroke="C"
        remove="true"/>

    <!-- 
    在"Mac OS X 10.5+"键盘中。删除其它配置的快捷键,只留这一种快捷键
     - "replace-all" (可选) - 在添加指定快捷键之前从action中删除所有键盘和鼠标快捷方式
     -->
    <keyboard-shortcut
        keymap="Mac OS X 10.5+"
        first-keystroke="control alt G"
        second-keystroke="C"
        replace-all="true"/>

    <!--
    <mouse-shortcut> 鼠标快捷键,可以有多个
    - "keystroke" (必选) - 指定鼠标按键和修饰符
       * 鼠标按钮: "button1", "button2", "button3"
       * 修饰符: "shift", "control", "meta", "alt", "altGraph"
       * 双击: "doubleClick"
    - "keymap" (必选) - 键盘映射(keymap), 哪种键盘时激活该快捷键,com.intellij.openapi.keymap.KeymapManager定义了keymap的列表
        也可以配置 "remove" and "replace-all" 属性,详见看上面的配置介绍
    -->
    <mouse-shortcut
        keymap="$default"
        keystroke="control button3 doubleClick"/>
  </action>

  <!--
  该action没有配置文本和描述属性. 如果它有资源包(resource bundle),将根据id自动匹配资源包中配置的文本和描述
  -->
  <action
      id="sdk.action.PopupDialogAction"
      class="sdk.action.PopupDialogAction"
      icon="SdkIcons.Sdk_default_icon"/>

  <!--
  <group> 元素定义了action group。<action>, <group>和 <separator>元素将被自动包含进该group
   - "id" (必选) - group的id,唯一
   - "class" (可选) - 指定group实现类的全限定命名(FQN),如果不配置,将会使用 com.intellij.openapi.actionSystem.DefaultActionGroup
   - "text" (可选) - group的文本 (显示子菜单中菜单项的文本).
   - "description" (可选) 当该group获取焦点时,显示的文本
   - "icon" (可选) - 图标
   - "popup" (可选) - 配置group在菜单中的显示方式
      * "true" - group actions放在子菜单中
      * "false" - 放在当前菜单中,由分隔线分隔.
   - "compact" (optional) -  compact="true" 时,如果action不可用,则不显示该action
  -->
  <group
      class="com.example.impl.MyActionGroup"
      id="TestActionGroup"
      text="Test Group"
      description="Group with test actions"
      icon="icons/testgroup.png"
      popup="true"
      compact="true">

    <action
        id="VssIntegration.TestAction"
        class="com.example.impl.TestAction"
        text="My Test Action"
        description="My test action"/>

    <!-- action之间的分隔线 -->
    <separator/>

    <group id="TestActionSubGroup"/>

    <!-- 把另一个action加入该group, ref配置为另一个action的id -->
    <reference ref="EditorCopy"/>

    <add-to-group
        group-id="MainMenu"
        relative-to-action="HelpMenu"
        anchor="before"/>
  </group>
</actions>

用代码注册Action

用代码注册Action,需要2步:

  1. 通过 ActionManager.registerAction() 注册action ,参数包含**action idAnAction 的实现类

  2. 需要把action加了一个或多个group中。通过 ActionManager.getAction(group id) 可以获取到 group (ActionManager.getAction(group id)返回值可以强转为 DefaultActionGroup)

构建ActionUI

如果插件需要包含一个工具栏或一个弹出菜单,可以使用 ActionPopupMenuopen in new windowActionToolbaropen in new window 。通过 ActionManager.createActionPopupMenu()createActionToolbar() 方法可以创建弹出菜单对象和工具栏对象。可以使用 getComponent() 方法从弹出菜单对象和工具栏对象中获取Swing 组件。

如果一个action 需要加入某个组件(例如工具窗口中的面板),使用 ActionToolbar.setTargetComponent(目标组件) 方法。设置目标组件保证了该action工具栏的状态和目标组件保持一致.

查看 Toolbaropen in new window 文档,了解更多。

常用action相关类

Toggle/Selection

带有"selected"/"pressed"的action 可以使用ToggleActionopen in new window (例如: 带有复选框的菜单项, 工具栏操作按钮)。 也可以了解 ToggleOptionActionopen in new window

Back/Forward 导航

BackActionopen in new window 提供了回到上一步编辑位置的功能,ForwardActionopen in new window 提供了回到后一步编辑位置的功能。

    History history = anActionEvent.getData(History.KEY);
    history.back();
    history.forward();

Action运行时占位符

如果Action需要在运行时注册,可以使用注册一个EmptyActionopen in new window 方便在 Settings/Preferences -> Keymap 中对该Action进行设置

上次编辑于:
贡献者: zhaojingbo
Loading...