打造酷炫AndroidStudio插件剖析.docx

上传人:b****5 文档编号:6825218 上传时间:2023-01-10 格式:DOCX 页数:12 大小:122.74KB
下载 相关 举报
打造酷炫AndroidStudio插件剖析.docx_第1页
第1页 / 共12页
打造酷炫AndroidStudio插件剖析.docx_第2页
第2页 / 共12页
打造酷炫AndroidStudio插件剖析.docx_第3页
第3页 / 共12页
打造酷炫AndroidStudio插件剖析.docx_第4页
第4页 / 共12页
打造酷炫AndroidStudio插件剖析.docx_第5页
第5页 / 共12页
点击查看更多>>
下载资源
资源描述

打造酷炫AndroidStudio插件剖析.docx

《打造酷炫AndroidStudio插件剖析.docx》由会员分享,可在线阅读,更多相关《打造酷炫AndroidStudio插件剖析.docx(12页珍藏版)》请在冰豆网上搜索。

打造酷炫AndroidStudio插件剖析.docx

打造酷炫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;

privateSetcharSet=newHashSet();

privateListbufferList=newArrayList();

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的属性,使之能以动画形式出现和消失

Iteratorit=charSet.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();

IteratorsetIt=charSet.iterator();

while(setIt.hasNext()){

CharObjobj=setIt.next();

mComponent.remove(obj.getLabel());

}

IteratorbufferIt=bufferList.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个集合来保持用户删除或者添加的字符串

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 法律文书 > 调解书

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1