欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

打造酷炫的AndroidStudio插件

 更新時間:2016年12月30日 11:35:42   作者:huachao1001  
這篇文章主要為大家詳細介紹了如何打造酷炫的AndroidStudio插件,具有一定的參考價值,感興趣的小伙伴們可以參考一下

前面幾篇文章學習了AndroidStudio插件的基礎后,這篇文章打算開發(fā)一個酷炫一點的插件。因為會用到前面的基礎,所以如果沒有看前面系列文章的話,請先返回。當然,如果有基礎的可以忽略之。先看看本文實現(xiàn)的最終效果如下(好吧,很多人說看的眼花):

雖然并沒有什么實際用途,但是作為學習插件開發(fā)感覺挺有意思的。

1. 基本思路

基本思路可以歸結(jié)如下幾步:

1)、通過Editor對象可以拿到封裝代碼編輯框的JComponent對象,即調(diào)用如下函數(shù):JComponent component = editor.getContentComponent();

2)、獲取輸入或刪除的字符(或字符串。通過選中多個字符刪除或粘貼則為字符串)??梢酝ㄟ^添加DocumentListener,監(jiān)聽文本變化。重寫beforeDocumentChange函數(shù),并通過DocumentEvent對象取得新的字符和舊的字符。分別通過函數(shù):documentEvent.getNewFragment()、documentEvent.getOldFragment()。它們代表著輸入的字符串和刪除的字符串。

3)、將輸入或刪除的字符串在編輯框中顯示出來。只需將各個字符串分別封裝到Jlabel中,并將JLabel加入到JComponent中即可顯示出輸入或刪除的字符串(或字符)。

4)、獲取用于顯示各個字符串的Jlabel對象在JComponent中的坐標位置。添加CaretListener,監(jiān)聽光標的位置。每次光標位置發(fā)生變化,就刷新到臨時變量中。當要添加一個JLabel時,獲取當前的臨時變量中保存的位置即為Jlabel應存放的位置。

5)、動畫效果。開啟一個線程,對于輸入的字符串,只需不斷修改字體大小。對于刪除的字符串,不斷修改JLabel的位置和字體大小。

6)、插件狀態(tài)保存到本地。用戶點擊開啟或者關(guān)閉插件以及其他開關(guān)選項,需要保存起來,下一次開啟AndroidStudio時可以恢復。只需實現(xiàn)PersistentStateComponent接口即可。

7)、用戶未點擊Action時,能自動注冊DocumentListener。這主要是考慮到,用戶開啟了插件,下一次打開AndroidStudio時無需點擊Aciton,直接輸入時就能自動注冊監(jiān)聽Document變化。由于注冊DocumentListener需要Editor對象,而想要取得Editor對象只有兩種方式:通過AnActionEvent對象的getData函數(shù);另一種是通過DataContext對象,使用
PlatformDataKeys.EDITOR.getData(dataContext)方法。顯然第一種方法只能在AnAction類的actionPerformed和update方法中才能取得。因此只能考慮用第二種方法,而在前面文章中介紹過,監(jiān)聽鍵盤字符輸入時,可以取得DataContext對象。即重寫TypedActionHandler接口的execute函數(shù),execute參數(shù)中傳遞了DataContext對象。

可以看到,以上用到的知識都是前面3篇文章中介紹過的內(nèi)容,并不復雜。只有第6條沒有介紹,本文中會學習本地持久化數(shù)據(jù)。

2. 插件狀態(tài)本地持久化

先看看如何實現(xiàn)本地持久化。首先定義一個全局共享變量類GlobalVar,使之實現(xiàn)PersistentStateComponent接口。先來個視覺上的認識,直接看代碼。

/**
 * 配置文件
 * Created by huachao on 2016/12/27.
 */
@State(
  name = "amazing-mode",
  storages = {
    @Storage(
      id = "amazing-mode",
      file = "$APP_CONFIG$/amazing-mode_setting.xml"
    )
  }
)
public class GlobalVar implements PersistentStateComponent<GlobalVar.State> {

 public static final class State {
  public boolean IS_ENABLE;
  public boolean IS_RANDOM;
 }

 @Nullable
 @Override
 public State getState() {
  return this.state;
 }

 @Override
 public void loadState(State state) {
  this.state = state;
 }

 public State state = new State();

 public GlobalVar() {

  state.IS_ENABLE = false;
  state.IS_RANDOM = false;
 }

 public static GlobalVar getInstance() {
  return ServiceManager.getService(GlobalVar.class);
 }

}

使用@State注解指定本地存儲位置、id等。具體實現(xiàn)基本可以參照這個模板寫,就是重寫loadState()和getState()兩個函數(shù)。另外需要注意一下getInstance()函數(shù)的寫法?;灸0寰瓦@樣,沒有什么特別的地方,依葫蘆畫瓢就行。

還有一點特別重要,一定要記得在plugin.xml中注冊這個持久化類。找到<extensions>標簽,加入<applicationService>子標簽,如下:

<extensions defaultExtensionNs="com.intellij">
 <!-- Add your extensions here -->
 <applicationService
   serviceImplementation="com.huachao.plugin.util.GlobalVar"
   serviceInterface="com.huachao.plugin.util.GlobalVar"
 />
</extensions>

這樣寫完以后,在獲取數(shù)據(jù)的時候,直接如下:

private GlobalVar.State state = GlobalVar.getInstance().state;
//state.IS_ENABLE
//state.IS_RANDOM

3. 編寫Action

主要包含2個Action:EnableAction和RandomColorAction。EnableAction用于設置插件的開啟或關(guān)閉,RandomColorAction用于設置是否使用隨機顏色。由于二者功能類似,我們只看看EnableAction的實現(xiàn):

/**
 * Created by huachao on 2016/12/27.
 */
public class EnableAction extends AnAction {
 private GlobalVar.State state = GlobalVar.getInstance().state;


 @Override
 public void update(AnActionEvent e) {
  Project project = e.getData(PlatformDataKeys.PROJECT);
  Editor editor = e.getData(PlatformDataKeys.EDITOR);
  if (editor == null || project == null) {
   e.getPresentation().setEnabled(false);
  } else {
   JComponent component = editor.getContentComponent();
   if (component == null) {
    e.getPresentation().setEnabled(false);
   } else {
    e.getPresentation().setEnabled(true);
   }
  }
  updateState(e.getPresentation());
 }

 @Override
 public void actionPerformed(AnActionEvent e) {
  Project project = e.getData(PlatformDataKeys.PROJECT);
  Editor editor = e.getData(PlatformDataKeys.EDITOR);
  if (editor == null || project == null) {
   return;
  }
  JComponent component = 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);
 }


 private void updateState(Presentation presentation) {

  if (state.IS_ENABLE) {
   presentation.setText("Enable");
   presentation.setIcon(AllIcons.General.InspectionsOK);
  } else {
   presentation.setText("Disable");
   presentation.setIcon(AllIcons.Actions.Cancel);
  }
 }


}

代碼比較簡單,跟前面幾篇文章中寫的很相似。只需注意一下actionPerformed函數(shù)中調(diào)用了兩個函數(shù):

CharPanel.getInstance(component).clearAllStr();
GlobalVar.registerDocumentListener(project, editor, state.IS_ENABLE);

CharPanel對象中的clearAllStr()函數(shù)后面介紹,只需知道它是將緩存中的所有動畫對象清除。GlobalVar對象中的registerDocumentListener ()函數(shù)是添加DocumentListener監(jiān)聽器。實現(xiàn)本文效果的中樞是DocumentListener監(jiān)聽器,是通過監(jiān)聽文本內(nèi)容發(fā)生變化來獲取實現(xiàn)字符動畫效果的數(shù)據(jù)。因此應應可能早地將DocumentListener監(jiān)聽器加入,而DocumentListener監(jiān)聽器加入的時刻包括:用戶點擊Action、用戶敲入字符。也就是說,多個地方都存在添加DocumentListener監(jiān)聽器的可能。因此把這個函數(shù)抽出來,加入到GlobalVar中,具體實現(xiàn)如下:

private static AmazingDocumentListener amazingDocumentListener = null;

public static void registerDocumentListener(Project project, Editor editor, boolean isFromEnableAction) {
 if (!hasAddListener || isFromEnableAction) {
  hasAddListener = true;
  JComponent component = editor.getContentComponent();
  if (component == null)
   return;
  if (amazingDocumentListener == null) {

   amazingDocumentListener = new AmazingDocumentListener(project);
   Document document = editor.getDocument();
   document.addDocumentListener(amazingDocumentListener);
  }

  Thread thread = new Thread(CharPanel.getInstance(component));
  thread.start();
 }
}

可以看到,一旦DocumentListener監(jiān)聽器被加入,就會開啟一個線程,這個線程是一直執(zhí)行,實現(xiàn)動畫效果。DocumentListener監(jiān)聽器只需加入一次即可。

4. 實現(xiàn)動畫

前面多次使用到了CharPanel對象,CharPanel對象就是用于實現(xiàn)動畫效果。先源碼:

package com.huachao.plugin.util;

import com.huachao.plugin.Entity.CharObj;

import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.util.List;

/**
 * Created by huachao on 2016/12/27.
 */
public class CharPanel implements Runnable {
 private JComponent mComponent;
 private Point mCurPosition;
 private Set<CharObj> charSet = new HashSet<CharObj>();
 private List<CharObj> bufferList = new ArrayList<CharObj>();


 private GlobalVar.State state = GlobalVar.getInstance().state;


 public void setComponent(JComponent component) {
  mComponent = component;
 }


 public void run() {
  while (state.IS_ENABLE) {
   if (GlobalVar.font != null) {
    synchronized (bufferList) {
     charSet.addAll(bufferList);
     bufferList.clear();
    }
    draw();
    int minFontSize = GlobalVar.font.getSize();

    //修改各個Label的屬性,使之能以動畫形式出現(xiàn)和消失
    Iterator<CharObj> it = charSet.iterator();
    while (it.hasNext()) {
     CharObj obj = it.next();
     if (obj.isAdd()) {//如果是添加到文本框
      if (obj.getSize() <= minFontSize) {//當字體大小到達最小后,使之消失
       mComponent.remove(obj.getLabel());
       it.remove();
      } else {//否則,繼續(xù)減小
       int size = obj.getSize() - 6 < minFontSize ? minFontSize : (obj.getSize() - 6);
       obj.setSize(size);
      }
     } else {//如果是從文本框中刪除
      Point p = obj.getPosition();
      if (p.y <= 0 || obj.getSize() <= 0) {//如果到達最底下,則清理
       mComponent.remove(obj.getLabel());
       it.remove();
      } else {
       p.y = p.y - 10;
       int size = obj.getSize() - 1 < 0 ? 0 : (obj.getSize() - 1);
       obj.setSize(size);
      }
     }
    }

   }
   try {
    if (charSet.isEmpty()) {
     synchronized (charSet) {
      charSet.wait();
     }
    }
    Thread.currentThread().sleep(50);
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
  }
 }

 //繪制文本,本質(zhì)上只是修改各個文本的位置和字體大小
 private void draw() {
  if (mComponent == null)
   return;

  for (CharObj obj : charSet) {
   JLabel label = obj.getLabel();

   Font font = new Font(GlobalVar.font.getName(), GlobalVar.font.getStyle(), obj.getSize());

   label.setFont(font);
   FontMetrics metrics = label.getFontMetrics(label.getFont());
   int textH = metrics.getHeight(); //字符串的高, 只和字體有關(guān)
   int textW = metrics.stringWidth(label.getText()); //字符串的寬
   label.setBounds(obj.getPosition().x, obj.getPosition().y - (textH - GlobalVar.minTextHeight), textW, textH);
  }
  mComponent.invalidate();
 }

 public void clearAllStr() {
  synchronized (bufferList) {
   bufferList.clear();
   charSet.clear();

   Iterator<CharObj> setIt = charSet.iterator();
   while (setIt.hasNext()) {
    CharObj obj = setIt.next();
    mComponent.remove(obj.getLabel());
   }

   Iterator<CharObj> bufferIt = bufferList.iterator();
   while (bufferIt.hasNext()) {
    CharObj obj = bufferIt.next();
    mComponent.remove(obj.getLabel());
   } 
  }
 }

 //單例模式,靜態(tài)內(nèi)部類
 private static class SingletonHolder {
  //靜態(tài)初始化器,由JVM來保證線程安全
  private static CharPanel instance = new CharPanel();
 }

 //返回單例對象
 public static CharPanel getInstance(JComponent component) {
  if (component != null) {
   SingletonHolder.instance.mComponent = component;
  }
  return SingletonHolder.instance;
 }

 //由光標監(jiān)聽器回調(diào),由此可動態(tài)獲取當前光標位置
 public void setPosition(Point position) {
  this.mCurPosition = position;
 }

 /**
  * 將字符串添加到列表中。
  *
  * @isAdd 如果為true表示十新增字符串,否則為被刪除字符串
  * @str 字符串
  */
 public void addStrToList(String str, boolean isAdd) {
  if (mComponent != null && mCurPosition != null) {

   CharObj charObj = new CharObj(mCurPosition.y);
   JLabel label = new JLabel(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);
  }
 }

 //以下用于產(chǎn)生隨機顏色
 private static final Color[] COLORS = {Color.GREEN, Color.BLACK, Color.BLUE, Color.ORANGE, Color.YELLOW, Color.RED, Color.CYAN, Color.MAGENTA};

 private Color randomColor() {
  int max = COLORS.length;
  int index = new Random().nextInt(max);
  return COLORS[index];
 }
}

解釋一下兩個關(guān)鍵函數(shù)run()和draw()。run()函數(shù)是開啟新線程開始執(zhí)行的函數(shù),它的實現(xiàn)是一個循環(huán),當插件開啟時會一直循環(huán)運行。CharPanel使用了2個集合來保持用戶刪除或者添加的字符串, charSet是會直接被顯示出來的,bufferList保存的是DocumentListener監(jiān)聽器監(jiān)聽到的輸入或刪除的字符串。輸入或刪除的字符串都封裝到CharObj類中。run函數(shù)中每一次循環(huán)之前,先將bufferList中數(shù)據(jù)全部轉(zhuǎn)移到charSet中。為什么要使用2個集合呢?這主要是因為,當循環(huán)遍歷charSet時,如果DocumentListener監(jiān)聽到的變化數(shù)據(jù)直接加入到charSet中,會導致出錯。因為Java的集合在遍歷時,不允許添加或刪除里面的元素。

run函數(shù)每一次循環(huán)都會調(diào)用draw()函數(shù),draw()函數(shù)根據(jù)CharObj封裝的數(shù)據(jù),將JLabel的位置屬性和字體屬性重新設置一次,這樣就使得JLabel有動畫效果,因為run函數(shù)的每次循環(huán)的最后會逐步修改字體大小和位置數(shù)據(jù)。

5. 源碼

其他代碼比較簡單,對著代碼解釋也沒什么意思。直接獻上源碼,如有疑惑的地方請留言,我盡量找時間一一回復。

Github地址:https://github.com/huachao1001/Amazing-Mode

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評論