转载请注明出处:【huachao1001的专栏:http://blog.csdn.net/huachao1001/article/details/53885981】

我们开发AndroidStudio插件,绝大多数插件功能是用在编辑文本上面,让用户开发更便捷。这篇文章主要是介绍Editor部分,看完之后可以开发简单实用的插件啦!在看本文之前,请先确定已经看完《AndroidStudio插件开发(Hello World篇)》和《 AndroidStudio插件开发(进阶篇之Action机制)》。因为这两篇是基础,没有这些基础就无法继续往下读。

在本文的最后使用简单的代码实现简单的插件:自动生成Getter和Setter函数的插件。如下图所示,下图中,分别演示了通过点击和使用快捷键的方式触发Action。

1. 文本编辑

1.1 CaretModel和SelectionModel

为了能够更灵活地控制Editor,IDEA插件开发中将Editor细分为多个模型。在本文中只简单介绍CaretModel和SelectionModel,除了CaretModel和SelectionModel以外,还有如下几种模型:

  • FoldingModel
  • IndentsModel
  • ScrollingModel
  • ScrollingModel
  • SoftWrapModel

获取Editor的CaretModel和SelectionModel对象方法如下:

@Override
public void actionPerformed(AnActionEvent e) {Editor editor = e.getData(PlatformDataKeys.EDITOR); if (editor == null)return;SelectionModel selectionModel = editor.getSelectionModel();CaretModel caretModel=editor.getCaretModel();
}

其他模型对象获取方式类似,通过Editor对象的相应函数即可得到。

1.1.1 CaretModel对象

CaretModel对象用于描述插入光标,通过CaretModel对象,可以实现如下功能:

  1. moveToOffset(int offset):将光标移动到指定位置(offset)
  2. getOffset():获取当前光标位置偏移量
  3. getCaretCount:获取光标数量(可能有多个位置有光标)
  4. void addCaretListener(CaretListener listener) ,void removeCaretListener(CaretListener listener):添加或移除光标监听器(CareListener)
  5. Caret addCaret(VisualPosition visualPosition):加入新的光标
  6. ……

1.1.2 SelectionModel对象

SelectionModel对象用于描述光标选中的文本段,通过SelectionModel对象可以实现如下功能:

  1. String getSelectedText() :获取选中部分字符串。
  2. int getSelectionEnd():获取选中文本段末尾偏移量
  3. int getSelectionStart():获取选中文本段起始位置偏移量
  4. void setSelection(int start, int end):设置选中,将staert到end部分设置为选中
  5. void removeSelection():将选中文本段删除
  6. void addSelectionListener(SelectionListener listener):添加监听器,用于监听光标选中变化。
  7. void selectLineAtCaret():将光标所在的行设置为选中。
  8. void selectWordAtCaret(boolean honorCamelWordsSettings):将光标所在的单词设置为选中。honorCamelWordsSettings表示是否驼峰命名分隔,如果为true,则大写字母为单词的边界
  9. ……

1.2 Document对象

与Editor中的其他对象一样,通过Editor对象的一个getter函数即可得到Document对象:

Document document = editor.getDocument();

Document对象用于描述文档文件,通过Document对象可以很方便的对Editor中的文件进行操作。可以做如下这些事情:

  1. String getText()String getText( TextRange range):获取Document对象对应的文件字符串。
  2. int getTextLength():获取文件长度。
  3. int getLineCount():获取文件的行数
  4. int getLineNumber(int offset):获取指定偏移量位置对应的行号offset取值为[0,getTextLength()-1]
  5. int getLineStartOffset(int line):获取指定行的第一个字符在全文中的偏移量,行号的取值范围为:[0,getLineCount()-1]
  6. int getLineEndOffset(int line):获取指定行的最后一个字符在全文中的偏移量,行号的取值范围为:[0,getLineCount()-1]
  7. void insertString(int offset, CharSequence s):在指定偏移位置插入字符串
  8. void deleteString(int startOffset, int endOffset):删除[startOffset,endOffset]位置的字符串,如果文件为只读,则会抛异常。
  9. void replaceString(int startOffset, int endOffset, CharSequence s):替换[startOffset,endOffset]位置的字符串为s
  10. void addDocumentListener( DocumentListener listener):添加Document监听器,在Document内容发生变化之前和变化之后都会回调相应函数。
  11. ……

1.3 实现自动生成Getter和Setter函数的插件

有了上面的认识后,我们可以开始写个简单的Getter和Setter函数插件了。首先创建一个Action,名为GetterAndSetter,并在plugin.xml中注册。plugin.xml的<acitons>标签部分如下:

<actions> <action id="StudyEditor.GetterAndSetter" class="com.huachao.plugin.GetterAndSetter" text="Getter And Setter"description="生成Getter和Setter方法"><add-to-group group-id="EditorPopupMenu" anchor="first"/><keyboard-shortcut keymap="$default" first-keystroke="ctrl alt G"/></action>
</actions>

通过前面两篇文章的学习,我们知道,定义Action时需要重写actionPerformed和update函数。

@Override
public void actionPerformed(AnActionEvent e) {//获取Editor和Project对象Editor editor = e.getData(PlatformDataKeys.EDITOR);Project project = e.getData(PlatformDataKeys.PROJECT);if (editor == null||project==null)return;//获取SelectionModel和Document对象SelectionModel selectionModel = editor.getSelectionModel();Document document = editor.getDocument();//拿到选中部分字符串String selectedText = selectionModel.getSelectedText();//得到选中字符串的起始和结束位置int startOffset = selectionModel.getSelectionStart();int endOffset = selectionModel.getSelectionEnd(); //得到最大插入字符串(即生成的Getter和Setter函数字符串)位置int maxOffset = document.getTextLength() - 1;//计算选中字符串所在的行号,并通过行号得到下一行的第一个字符的起始偏移量int curLineNumber = document.getLineNumber(endOffset);int nextLineStartOffset = document.getLineStartOffset(curLineNumber + 1);//计算字符串的插入位置int insertOffset = maxOffset > nextLineStartOffset ? nextLineStartOffset : maxOffset;//得到选中字符串在Java类中对应的字段的类型String type = getSelectedType(document, startOffset);//对文档进行操作部分代码,需要放入Runnable接口中实现,由IDEA在内部将其通过一个新线程执行Runnable runnable = new Runnable() {@Overridepublic void run() {//genGetterAndSetter为生成getter和setter函数部分document.insertString(insertOffset, genGetterAndSetter(selectedText, type));}};//加入任务,由IDEA调度执行这个任务WriteCommandAction.runWriteCommandAction(project, runnable);}@Override
public void update(AnActionEvent e) {Editor editor = e.getData(PlatformDataKeys.EDITOR);SelectionModel selectionModel = editor.getSelectionModel();//如果没有字符串被选中,那么无需显示该Actione.getPresentation().setVisible(editor != null && selectionModel.hasSelection());
}

剩下的还有获取选中字段的类型和生成Getter、Setter函数两个部分,两个函数如下:

private String getSelectedType(Document document, int startOffset) {String text = document.getText().substring(0, startOffset).trim();int startIndex = text.lastIndexOf(' ');return text.substring(startIndex + 1);
}private String genGetterAndSetter(String field, String type) {if (field == null || (field = field.trim()).equals(""))return "";String upperField = field;char first = field.charAt(0);if (first <= 'z' && first >= 'a') {upperField = String.valueOf(first).toUpperCase() + field.substring(1);}String getter = "\tpublic TYPE getUpperField(){ \n\t\treturn this.FIELD;\n\t}";String setter = "\tpublic void setUpperField(TYPE FIELD){\n\t\tthis.FIELD=FIELD;\n\t}";String myGetter = getter.replaceAll("TYPE", type).replaceAll("UpperField", upperField).replaceAll("FIELD", field);String mySetter = setter.replaceAll("TYPE", type).replaceAll("UpperField", upperField).replaceAll("FIELD", field);return "\n"+myGetter + "\n" + mySetter + "\n";
}

运行后如下:

注意:在对Document进行修改时,需要实现Runnable接口并将修改部分代码写入run函数中,最后通过 WriteCommandAction的runWriteCommandAction函数执行。

2. Editor的坐标系统:位置和偏移量

前面小节我们知道,通过CaretModel对象我们可以获取当前光标位置。但在Editor中位置分为两种,一种是逻辑位置,对应LogicalPosition类;另一种是视觉位置,对应VisualPosition类。

LogicalPosition与VisualPosition的区别通过如下图很显然能区分开来。

上如中,光标的坐标为:

LogicalPosition:(13,6)
VisualPosition:(9,6)

注意,行号和列号都是从0开始。

另外,获取LogicalPosition和VisualPosition方法如下:

@Override
public void actionPerformed(AnActionEvent e) {//获取Editor和Project对象Editor editor = e.getData(PlatformDataKeys.EDITOR);Project project = e.getData(PlatformDataKeys.PROJECT);if (editor == null || project == null)return;CaretModel caretModel = editor.getCaretModel();LogicalPosition logicalPosition = caretModel.getLogicalPosition();VisualPosition visualPosition = caretModel.getVisualPosition();System.out.println(logicalPosition + "," + visualPosition);
}

3. Editor中的按键事件

为了监听按键时间,专门提供了TypedActionHandler类,我们只需继承TypedActionHandler,并重写execute函数即可。注意,只能监听可打印字符对应的按键。

package com.huachao.plugin;import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.actionSystem.TypedActionHandler;
import org.jetbrains.annotations.NotNull;/*** Created by huachao on 2016/12/26.*/
public class MyTypedActionHandler implements TypedActionHandler {@Overridepublic void execute(@NotNull Editor editor, char c, @NotNull DataContext dataContext) {System.out.println(c);}
}

TypedAction专门处理按键相关操作,定义了TypedActionHandler后,接下来就是将自定义的TypedActionHandler加入到TypedAction中。如何获取TypedAction对象呢?具体如下:

final EditorActionManager actionManager = EditorActionManager.getInstance();
final TypedAction typedAction = actionManager.getTypedAction();
typedAction.setupHandler(new MyTypedActionHandler());

上述代码即可将自定义的按键处理器成功加入,现在有个问题是,上面这段代码应该放入到哪里呢?之前我们都是重写AnAction的actionPerformed和update函数就行,能不能将上面这段代码放入到actionPerformed中呢?显然这是可以的,但是这样的话就得先点击当前Action后才能使MyTypedActionHandler被加入,并且每点击一次,就会创建新的MyTypedActionHandler并将原先的替换。我们可以把上面这段代码加入到Action的构造函数中,或者是在Action中创建static块。

注意:只能设置一个监听,如果自定义了按键监听,而不做其他处理的话,会使得原先IDEA中的按键监听无法处理,导致无法正常在输入框中输入。

为了能更充分理解TypedActionHandler,我们实现一个简单功能的插件:在输入字符的同时,在文档的开头也插入同样的字符。

首先定义TypedActionHandler:

package com.huachao.plugin;import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.CaretModel;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.editor.actionSystem.TypedActionHandler;
import org.jetbrains.annotations.NotNull;/*** Created by huachao on 2016/12/26.*/
public class MyTypedActionHandler implements TypedActionHandler {private TypedActionHandler oldHandler;private boolean isBegin = true;private int caretLine = 0;@Overridepublic void execute(@NotNull Editor editor, char c, @NotNull DataContext dataContext) {if (oldHandler != null)oldHandler.execute(editor, c, dataContext);Document document = editor.getDocument();CaretModel caretModel = editor.getCaretModel();int caretOffset = caretModel.getOffset();int line = document.getLineNumber(caretOffset);if (isBegin) {document.insertString(document.getLineStartOffset(line), String.valueOf(c) + "\n");caretLine = line + 1;isBegin = false;} else {if (line != caretLine) {isBegin = true;execute(editor, c, dataContext);} else {document.insertString(document.getLineEndOffset(line - 1), String.valueOf(c));}}System.out.println(caretLine + "," + line);}public void setOldHandler(TypedActionHandler oldHandler) {this.oldHandler = oldHandler;}
}将我们定义的TypedActionHandler设置进去,只需实现一个简单Action。
```java
package com.huachao.plugin;import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.editor.actionSystem.EditorActionManager;
import com.intellij.openapi.editor.actionSystem.TypedAction;
import com.intellij.openapi.editor.actionSystem.TypedActionHandler;/*** Created by huachao on 2016/12/26.*/
public class InsertCharAction extends AnAction {public InsertCharAction() {final EditorActionManager actionManager = EditorActionManager.getInstance();final TypedAction typedAction = actionManager.getTypedAction();MyTypedActionHandler handler = new MyTypedActionHandler();//将自定义的TypedActionHandler设置进去后,//返回旧的TypedActionHandler,即IDEA自身的TypedActionHandlerTypedActionHandler oldHandler = typedAction.setupHandler(handler);handler.setOldHandler(oldHandler);}@Overridepublic void actionPerformed(AnActionEvent e) {}
}

运行结果如下:

参考资料

Document类源码:点击这里
官方文档:http://www.jetbrains.org/intellij/sdk/docs/tutorials/editor_basics.html

AndroidStudio插件开发(进阶篇之Editor)相关推荐

  1. python xposed_Xposed插件开发进阶篇

    Dalvik 孵化器 Zygote (Android系统中,所有的应用程序进程以及系统服务进程SystemServer都是由Zygote进程孕育/fork出来的)进程对应的程序是/system/bin ...

  2. [Android Pro] AndroidStudio IDE界面插件开发(进阶篇之Action机制)

    转载请注明出处:[huachao1001的专栏:http://blog.csdn.net/huachao1001/article/details/53883500] 从上一篇<AndroidSt ...

  3. C#使用Xamarin开发可移植移动应用进阶篇(10.综合演练,来一份增删改查CRUD)

    说点什么.. 呃 也有半个月没更新了. 本来这篇的Demo早就写完了,文章也构思好了.迟迟没发布..是因为实在太忙.. 项目要上线..各种  你们懂的.. 正赶上自己十一人生大事..结婚..所以..忙 ...

  4. cocos2dx视频教程进阶篇--第1天--吃西瓜游戏。

    感谢大家的鼓励和支持,给了我很多动力,今天进阶篇终于和大家见面了. 2014-03-16更新:出品了box2d编辑工具PhysicsEditor的视频(一起做一个实战级别的游戏,需要MAC操作系统)h ...

  5. [安全攻防进阶篇] 二.如何学好逆向分析、逆向路线推荐及吕布传游戏逆向案例

    从2019年7月开始,我来到了一个陌生的专业--网络空间安全.初入安全领域,是非常痛苦和难受的,要学的东西太多.涉及面太广,但好在自己通过分享100篇"网络安全自学"系列文章,艰难 ...

  6. AnyChart 开发 进阶篇

    AnyChart 开发 进阶篇 AnyChart 开发 进阶篇 AnyChart 开发 进阶篇 摘要 Plotly 简介 主题定制 获得Anychart 需要的JSON数据 福利---黑色风格的Any ...

  7. AnyChart 开发进阶篇 - 如何将静态图发挥到极致

    AnyChart 开发 进阶篇 AnyChart 开发 进阶篇 AnyChart 开发 进阶篇 Plotly 主题定制 AnyChart 基础Tools 配置 Mapbox 地图在Anychart中使 ...

  8. Enterprise Library Step By Step系列(十二):异常处理应用程序块——进阶篇

    一.把异常信息Logging到数据库 在日志和监测应用程序块中,有朋友提意见说希望能够把异常信息Logging到数据库中,在这里介绍一下具体的实现方法. 1.创建相关的数据库环境: 我们可以用日志和监 ...

  9. Docker 数据卷之进阶篇

    Docker 数据卷之进阶篇 原文:Docker 数据卷之进阶篇 笔者在<Docker 基础 : 数据管理>一文中介绍了 docker 数据卷(volume) 的基本用法.随着使用的深入, ...

最新文章

  1. HDOJ--4821--String【弦hash】
  2. 【Kotlin】Kotlin Sealed 密封类 ( 密封类声明 | 密封类子类定义 | 密封类特点 | 代码示例 )
  3. 菜鸟学SSH(八)——Hibernate对象的三种状态
  4. 第三次学JAVA再学不好就吃翔(part100)--文件名称过滤器
  5. Collatz函数的C++递归实现
  6. 加解密技术(Cryptography)基本概念
  7. mysql show slave_MySQL show slave status 参考
  8. Java/JDK下载安装与环境配置(Windows 10 超详细的图文版教程 )
  9. 2019年2月10日训练日记
  10. vue项目打包部署到服务器子目录二级目录。
  11. 游戏开发工程师岗位要求
  12. Shell运行原理及Linux权限
  13. 操作系统——进程调度
  14. 计算机网络设计前三层实验,基于Packet Tracer的计算机网络实验设计
  15. 小哈智能机器人的功能_小哈智能教育机器人H2产品外观参数说明
  16. Lua 实现Get Set属性
  17. 除了SCI-HUB,还有那么多免费外文文献网站,资源速取。
  18. 【NVMe-MI 1.2a - 1】NVM Express Management Interface介绍
  19. TMO (time-triggered message-triggered object)
  20. jrtplib收发实例

热门文章

  1. Java中StringBuffer的相关运用与实践
  2. Android apm监控框架,移动性能监控 SDK 详细集成文档
  3. 明远智睿5G工业网关 5G物联网人工智能
  4. 看了这几个原创AI公众号,再也不担心错过前沿技术干货啦!
  5. python中同级目录下不同py文件之间的调用失败问题
  6. vv_jeffy之spring4.x集成myibatis3.x
  7. C1-IT基础-计算机网络
  8. 日志易之AIX、HP-UNIX小机探针Agent安装步骤
  9. 【毕业设计】协同过滤商品推荐系统 - python 深度学习
  10. Python项目外星人入侵(一):实现宇宙飞船