打造酷炫AndroidStudio插件剖析.docx
- 文档编号:6825218
- 上传时间:2023-01-10
- 格式:DOCX
- 页数:12
- 大小:122.74KB
打造酷炫AndroidStudio插件剖析.docx
《打造酷炫AndroidStudio插件剖析.docx》由会员分享,可在线阅读,更多相关《打造酷炫AndroidStudio插件剖析.docx(12页珍藏版)》请在冰豆网上搜索。
打造酷炫AndroidStudio插件剖析
打造酷炫AndroidStudio插件
这篇文章打算开发一个酷炫一点的插件。
因为会用到前面的基础,所以如果没有看前面系列文章的话,请先返回。
当然,如果有基础的可以忽略之。
先看看本文实现的最终效果如下(好吧,很多人说看的眼花):
虽然并没有什么实际用途,但是作为学习插件开发感觉挺有意思的。
1.基本思路
基本思路可以归结如下几步:
通过Editor对象可以拿到封装代码编辑框的JComponent对象,即调用如下函数:
JComponentcomponent=editor.getContentComponent();
获取输入或删除的字符(或字符串。
通过选中多个字符删除或粘贴则为字符串)。
可以通过添加DocumentListener,监听文本变化。
重写beforeDocumentChange函数,并通过DocumentEvent对象取得新的字符和旧的字符。
分别通过函数:
documentEvent.getNewFragment()、documentEvent.getOldFragment()。
它们代表着输入的字符串和删除的字符串。
将输入或删除的字符串在编辑框中显示出来。
只需将各个字符串分别封装到Jlabel中,并将JLabel加入到JComponent中即可显示出输入或删除的字符串(或字符)。
获取用于显示各个字符串的Jlabel对象在JComponent中的坐标位置。
添加CaretListener,监听光标的位置。
每次光标位置发生变化,就刷新到临时变量中。
当要添加一个JLabel时,获取当前的临时变量中保存的位置即为Jlabel应存放的位置。
动画效果。
开启一个线程,对于输入的字符串,只需不断修改字体大小。
对于删除的字符串,不断修改JLabel的位置和字体大小。
插件状态保存到本地。
用户点击开启或者关闭插件以及其他开关选项,需要保存起来,下一次开启AndroidStudio时可以恢复。
只需实现PersistentStateComponent接口即可。
用户未点击Action时,能自动注册DocumentListener。
这主要是考虑到,用户开启了插件,下一次打开AndroidStudio时无需点击Aciton,直接输入时就能自动注册监听Document变化。
由于注册DocumentListener需要Editor对象,而想要取得Editor对象只有两种方式:
通过AnActionEvent对象的getData函数;另一种是通过DataContext对象,使用
PlatformDataKeys.EDITOR.getData(dataContext)方法。
显然第一种方法只能在AnAction类的actionPerformed和update方法中才能取得。
因此只能考虑用第二种方法,而在前面文章中介绍过,监听键盘字符输入时,可以取得DataContext对象。
即重写TypedActionHandler接口的execute函数,execute参数中传递了DataContext对象。
可以看到,以上用到的知识都是前面3篇文章中介绍过的内容,并不复杂。
只有第6条没有介绍,本文中会学习本地持久化数据。
2.插件状态本地持久化
先看看如何实现本地持久化。
首先定义一个全局共享变量类GlobalVar,使之实现PersistentStateComponent接口。
先来个视觉上的认识,直接看代码。
/**
*配置文件
*Createdbyhuachaoon2016/12/27.
*/
@State(
name="amazing-mode",
storages={
@Storage(
id="amazing-mode",
file="$APP_CONFIG$/amazing-mode_setting.xml"
)
}
)
publicclassGlobalVarimplementsPersistentStateComponent
publicstaticfinalclassState{
publicbooleanIS_ENABLE;
publicbooleanIS_RANDOM;
}
@Nullable
@Override
publicStategetState(){
returnthis.state;
}
@Override
publicvoidloadState(Statestate){
this.state=state;
}
publicStatestate=newState();
publicGlobalVar(){
state.IS_ENABLE=false;
state.IS_RANDOM=false;
}
publicstaticGlobalVargetInstance(){
returnServiceManager.getService(GlobalVar.class);
}
}
使用@State注解指定本地存储位置、id等。
具体实现基本可以参照这个模板写,就是重写loadState()和getState()两个函数。
另外需要注意一下getInstance()函数的写法。
基本模板就这样,没有什么特别的地方,依葫芦画瓢就行。
还有一点特别重要,一定要记得在plugin.xml中注册这个持久化类。
找到
--Addyourextensionshere-->
serviceImplementation="com.huachao.plugin.util.GlobalVar" serviceInterface="com.huachao.plugin.util.GlobalVar" /> 这样写完以后,在获取数据的时候,直接如下: privateGlobalVar.Statestate=GlobalVar.getInstance().state; //state.IS_ENABLE //state.IS_RANDOM 3.编写Action 主要包含2个Action: EnableAction和RandomColorAction。 EnableAction用于设置插件的开启或关闭,RandomColorAction用于设置是否使用随机颜色。 由于二者功能类似,我们只看看EnableAction的实现: /** *Createdbyhuachaoon2016/12/27. */ publicclassEnableActionextendsAnAction{ privateGlobalVar.Statestate=GlobalVar.getInstance().state; @Override publicvoidupdate(AnActionEvente){ Projectproject=e.getData(PlatformDataKeys.PROJECT); Editoreditor=e.getData(PlatformDataKeys.EDITOR); if(editor==null||project==null){ e.getPresentation().setEnabled(false); }else{ JComponentcomponent=editor.getContentComponent(); if(component==null){ e.getPresentation().setEnabled(false); }else{ e.getPresentation().setEnabled(true); } } updateState(e.getPresentation()); } @Override publicvoidactionPerformed(AnActionEvente){ Projectproject=e.getData(PlatformDataKeys.PROJECT); Editoreditor=e.getData(PlatformDataKeys.EDITOR); if(editor==null||project==null){ return; } JComponentcomponent=editor.getContentComponent(); if(component==null) return; state.IS_ENABLE=! state.IS_ENABLE; updateState(e.getPresentation()); //只要点击Enable项,就把缓存中所有的文本清理 CharPanel.getInstance(component).clearAllStr(); GlobalVar.registerDocumentListener(project,editor,state.IS_ENABLE); } privatevoidupdateState(Presentationpresentation){ if(state.IS_ENABLE){ presentation.setText("Enable"); presentation.setIcon(AllIcons.General.InspectionsOK); }else{ presentation.setText("Disable"); presentation.setIcon(AllIcons.Actions.Cancel); } } } 代码比较简单,跟前面几篇文章中写的很相似。 只需注意一下actionPerformed函数中调用了两个函数: CharPanel.getInstance(component).clearAllStr(); GlobalVar.registerDocumentListener(project,editor,state.IS_ENABLE); CharPanel对象中的clearAllStr()函数后面介绍,只需知道它是将缓存中的所有动画对象清除。 GlobalVar对象中的registerDocumentListener()函数是添加DocumentListener监听器。 实现本文效果的中枢是DocumentListener监听器,是通过监听文本内容发生变化来获取实现字符动画效果的数据。 因此应应可能早地将DocumentListener监听器加入,而DocumentListener监听器加入的时刻包括: 用户点击Action、用户敲入字符。 也就是说,多个地方都存在添加DocumentListener监听器的可能。 因此把这个函数抽出来,加入到GlobalVar中,具体实现如下: privatestaticAmazingDocumentListeneramazingDocumentListener=null; publicstaticvoidregisterDocumentListener(Projectproject,Editoreditor,booleanisFromEnableAction){ if(! hasAddListener||isFromEnableAction){ hasAddListener=rue; JComponentcomponent=editor.getContentComponent(); if(component==null) return; if(amazingDocumentListener==null){ amazingDocumentListener=newAmazingDocumentListener(project); Documentdocument=editor.getDocument(); document.addDocumentListener(amazingDocumentListener); } Threadthread=newThread(CharPanel.getInstance(component)); thread.start(); } } 可以看到,一旦DocumentListener监听器被加入,就会开启一个线程,这个线程是一直执行,实现动画效果。 DocumentListener监听器只需加入一次即可。 4.实现动画 前面多次使用到了CharPanel对象,CharPanel对象就是用于实现动画效果。 先源码: packagecom.huachao.plugin.util; importcom.huachao.plugin.Entity.CharObj; importjavax.swing.*; importjava.awt.*; importjava.util.*; importjava.util.List; /** *Createdbyhuachaoon2016/12/27. */ publicclassCharPanelimplementsRunnable{ privateJComponentmComponent; privatePointmCurPosition; privateSet privateList privateGlobalVar.Statestate=GlobalVar.getInstance().state; publicvoidsetComponent(JComponentcomponent){ mComponent=component; } publicvoidrun(){ while(state.IS_ENABLE){ if(GlobalVar.font! =null){ synchronized(bufferList){ charSet.addAll(bufferList); bufferList.clear(); } draw(); intminFontSize=GlobalVar.font.getSize(); //修改各个Label的属性,使之能以动画形式出现和消失 Iterator while(it.hasNext()){ CharObjobj=it.next(); if(obj.isAdd()){//如果是添加到文本框 if(obj.getSize()<=minFontSize){//当字体大小到达最小后,使之消失 mComponent.remove(obj.getLabel()); it.remove(); }else{//否则,继续减小 intsize=obj.getSize()-6 minFontSize: (obj.getSize()-6); obj.setSize(size); } }else{//如果是从文本框中删除 Pointp=obj.getPosition(); if(p.y<=0||obj.getSize()<=0){//如果到达最底下,则清理 mComponent.remove(obj.getLabel()); it.remove(); }else{ p.y=p.y-10; intsize=obj.getSize()-1<0? 0: (obj.getSize()-1); obj.setSize(size); } } } } try{ if(charSet.isEmpty()){ synchronized(charSet){ charSet.wait(); } } Thread.currentThread().sleep(50); }catch(InterruptedExceptione){ e.printStackTrace(); } } } //绘制文本,本质上只是修改各个文本的位置和字体大小 privatevoiddraw(){ if(mComponent==null) return; for(CharObjobj: charSet){ JLabellabel=obj.getLabel(); Fontfont=newFont(GlobalVar.font.getName(),GlobalVar.font.getStyle(),obj.getSize()); label.setFont(font); FontMetricsmetrics=label.getFontMetrics(label.getFont()); inttextH=metrics.getHeight();//字符串的高,只和字体有关 inttextW=metrics.stringWidth(label.getText());//字符串的宽 label.setBounds(obj.getPosition().x,obj.getPosition().y-(textH-GlobalVar.minTextHeight),textW,textH); } mComponent.invalidate(); } publicvoidclearAllStr(){ synchronized(bufferList){ bufferList.clear(); charSet.clear(); Iterator while(setIt.hasNext()){ CharObjobj=setIt.next(); mComponent.remove(obj.getLabel()); } Iterator while(bufferIt.hasNext()){ CharObjobj=bufferIt.next(); mComponent.remove(obj.getLabel()); } } } //单例模式,静态内部类 privatestaticclassSingletonHolder{ //静态初始化器,由JVM来保证线程安全 privatestaticCharPanelinstance=newCharPanel(); } //返回单例对象 publicstaticCharPanelgetInstance(JComponentcomponent){ if(nent! =null){ SingletonHolder.instance.mComponent=component; } returnSingletonHolder.instance; } //由光标监听器回调,由此可动态获取当前光标位置 publicvoidsetPosition(Pointposition){ this.mCurPosition=position; } /** *将字符串添加到列表中。 * *@isAdd如果为true表示十新增字符串,否则为被删除字符串 *@str字符串 */ publicvoidaddStrToList(Stringstr,booleanisAdd){ if(mComponent! =null&&mCurPosition! =null){ CharObjcharObj=newCharObj(mCurPosition.y); JLabellabel=newJLabel(str); charObj.setStr(str); charObj.setAdd(isAdd); charObj.setLabel(label); if(isAdd) charObj.setSize(60); else charObj.setSize(GlobalVar.font.getSize()); charObj.setPosition(mCurPosition); if(state.IS_RANDOM){ label.setForeground(randomColor()); }else{ label.setForeground(GlobalVar.defaultForgroundColor); } synchronized(bufferList){ bufferList.add(charObj); } if(charSet.isEmpty()){ synchronized(charSet){ charSet.notify(); } } mComponent.add(label); } } //以下用于产生随机颜色 privatestaticfinalColor[]COLORS={Color.GREEN,Color.BLACK,Color.BLUE,Color.ORANGE,Color.YELLOW,Color.RED,Color.CYAN,Color.MAGENTA}; privateColorrandomColor(){ intmax=COLORS.length; intindex=newRandom().nextInt(max); returnCOLORS[index]; } } 解释一下两个关键函数run()和draw()。 run()函数是开启新线程开始执行的函数,它的实现是一个循环,当插件开启时会一直循环运行。 CharPanel使用了2个集合来保持用户删除或者添加的字符串
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 打造 AndroidStudio 插件 剖析