멋 진 AndroidStudio 플러그 인 만 들 기

앞의 몇 편의 글 에서 AndroidStudio 플러그 인의 기 초 를 배 운 후에 이 글 은 멋 진 플러그 인 을 개발 할 계획 이다.앞의 기 초 를 사용 하기 때문에 앞의 시 리 즈 를 보지 않 았 다 면 먼저 돌아 가세 요.물론 기초 가 있다 면 무시 할 수 있다.먼저 본 고가 실현 한 최종 효 과 를 보면 다음 과 같다.

실제 용 도 는 없 지만 학습 플러그 인 개발 로 재 미 있 습 니 다.
1.기본 적 인 사고방식
기본 적 인 사 고 는 다음 과 같은 몇 단계 로 요약 할 수 있다.
1)Editor 대상 을 통 해 패 키 징 코드 편집 상자 의 JComponent 대상 을 얻 을 수 있 습 니 다.즉,다음 함수 로 호출 합 니 다.JComponent component=editor.getContentComponent();
2)입력 하거나 삭제 한 문자(또는 문자열 가 져 오기.여러 문 자 를 선택 하여 삭제 하거나 붙 여 넣 으 면 문자열 입 니 다.DocumentListener 를 추가 하여 텍스트 변 화 를 감청 할 수 있 습 니 다.beforeDocumentChange 함 수 를 다시 쓰 고 DocumentEvent 대상 을 통 해 새로운 문자 와 오래된 문 자 를 가 져 옵 니 다.각각 함수:documentEvent.getNewFragment(),documentEvent.getOldFragment()를 통 해.입력 한 문자열 과 삭 제 된 문자열 을 대표 합 니 다.
3)입력 하거나 삭제 한 문자열 을 편집 상자 에 표시 합 니 다.각 문자열 을 각각 Jlabel 에 봉 하고 JLabel 을 JComponent 에 추가 하면 입력 하거나 삭제 한 문자열(또는 문자)을 표시 할 수 있 습 니 다.
4),각 문자열 을 표시 할 Jlabel 대상 이 JComponent 에 있 는 좌표 위 치 를 가 져 옵 니 다.CaretListener,감청 커서 의 위 치 를 추가 합 니 다.커서 위치 가 바 뀔 때마다 임시 변수 로 갱 신 됩 니 다.JLabel 을 추가 하려 면 현재 임시 변수 에 저 장 된 위 치 를 Jlabel 에 저장 할 위 치 를 가 져 옵 니 다.
5)애니메이션 효과.입력 한 문자열 에 대해 서 는 글꼴 크기 만 계속 수정 하면 됩 니 다.삭 제 된 문자열 에 대해 서 는 JLabel 의 위치 와 글꼴 크기 를 계속 수정 합 니 다.
6)플러그 인 상 태 를 로 컬 에 저장 합 니 다.사용자 가 플러그 인 을 켜 거나 끄 거나 다른 스위치 옵션 을 누 르 면 저장 해 야 합 니 다.다음 AndroidStudio 를 열 때 복원 할 수 있 습 니 다.PersistentState Component 인터페이스 만 실현 하면 됩 니 다.
7)、사용자 가 Action 을 클릭 하지 않 으 면 DocumentListener 를 자동 으로 등록 할 수 있 습 니 다.사용자 가 플러그 인 을 켰 고,다음 안 드 로 이 드 스튜디오 를 열 때 Aciton 을 클릭 하지 않 고 바로 입력 하면 자동 으로 감청 문서 변 화 를 등록 할 수 있다 는 점 을 고려 한 것 이다.DocumentListener 를 등록 하려 면 Editor 대상 이 필요 하기 때문에 Editor 대상 을 얻 으 려 면 두 가지 방법 만 있 습 니 다.AnActionEvent 대상 의 getData 함 수 를 통 해;다른 하 나 는 DataContext 대상 을 통 해 사용 하 는 것 이다.
PlatformDataKeys.EDITOR.getData(dataContext)방법.첫 번 째 방법 은 AnAction 류 의 actionPerformed 와 update 방법 에서 만 얻 을 수 있 음 이 분명 하 다.따라서 두 번 째 방법 만 고려 할 수 있 고 앞의 글 에서 소개 한 바 와 같이 키보드 문자 입력 을 감청 할 때 DataContext 대상 을 얻 을 수 있다.즉,TypedAction Handler 인터페이스의 execute 함 수 를 다시 쓰 고 execute 매개 변수 에서 DataContext 대상 을 전달 합 니 다.
이 를 통 해 알 수 있 듯 이 위 에서 사용 한 지식 은 모두 앞의 세 편의 글 에서 소개 한 내용 으로 복잡 하지 않다.제6 조 만 소개 되 지 않 고 본 고 는 현지의 지속 적 인 데 이 터 를 배 울 것 이다.
2.플러그 인 상태 로 컬 영구 화
먼저 현지의 지속 화 를 어떻게 실현 하 는 지 보 자.먼저 전역 공유 변수 클래스 GlobalVar 를 정의 하여 PersistentState Component 인 터 페 이 스 를 실현 합 니 다.먼저 시각 적 인 인식 을 가지 고 코드 를 직접 보 세 요.

/**
 *     
 * 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 등 을 지정 합 니 다.구체 적 인 실현 은 기본적으로 이 템 플 릿 을 참조 하여 쓸 수 있 습 니 다.바로 loadState()와 getState()두 함 수 를 다시 쓰 는 것 입 니 다.또한 getInstance()함수 의 쓰기 에 주의해 야 합 니 다.기본 템 플 릿 은 이렇다.특별한 점 이 없 으 니 그대로 모방 하면 된다.
그리고 한 가지 중요 한 것 은 plugin.xml 에 이 지구 화 류 를 등록 하 는 것 을 기억 해 야 합 니 다.탭 을 찾 아하위 탭 을 추가 합 니 다.다음 과 같 습 니 다.

<extensions defaultExtensionNs="com.intellij">
 <!-- Add your extensions here -->
 <applicationService
   serviceImplementation="com.huachao.plugin.util.GlobalVar"
   serviceInterface="com.huachao.plugin.util.GlobalVar"
 />
</extensions>
이렇게 쓴 후에 데 이 터 를 가 져 올 때 바로 다음 과 같 습 니 다.

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

3.액 션 작성
주로 2 개의 Action:EnableAction 과 RandomColorAction 이 포함 되 어 있 습 니 다.EnableAction 은 플러그 인의 켜 거나 끄 기 를 설정 하 는 데 사 용 됩 니 다.RandomColorAction 은 무 작위 색상 을 사용 할 지 여 부 를 설정 하 는 데 사 용 됩 니 다.두 가지 기능 이 유사 하기 때문에 우 리 는 EnableAction 의 실현 만 봅 니 다.

/**
 * 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 함수 에서 두 함수 가 호출 되 었 음 을 주의 하 십시오:

CharPanel.getInstance(component).clearAllStr();
GlobalVar.registerDocumentListener(project, editor, state.IS_ENABLE);
CharPanel 대상 의 clearAllStr()함수 뒤에 소개 합 니 다.캐 시 에 있 는 모든 애니메이션 대상 을 지 우 는 것 임 을 알 아야 합 니 다.GlobalVar 대상 의 registerDocumentListener()함 수 는 DocumentListener 모니터 를 추가 합 니 다.본 논문 효 과 를 실현 하 는 중 추 는 DocumentListener 모니터 로 텍스트 내용 의 변 화 를 감청 하여 문자 애니메이션 효 과 를 실현 하 는 데 이 터 를 얻 는 것 이다.따라서 DocumentListener 모니터 를 일찍 추가 해 야 합 니 다.DocumentListener 모니터 가 가입 하 는 시간 은 사용자 가 Action 을 클릭 하고 사용자 가 문 자 를 입력 하 는 것 을 포함 합 니 다.DocumentListener 모니터 를 추가 할 가능성 이 여러 곳 에 존재 한 다 는 것 이다.따라서 이 함 수 를 추출 하여 GlobalVar 에 넣 고 구체 적 으로 다음 과 같이 실현 합 니 다.

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 모니터 가 가입 하면 스 레 드 가 열 립 니 다.이 스 레 드 는 계속 실행 되 고 애니메이션 효 과 를 실현 합 니 다.DocumentListener 모니터 는 한 번 만 가입 하면 됩 니 다.
4.애니메이션 구현
앞에서 CharPanel 대상 을 여러 번 사 용 했 는데 CharPanel 대상 은 애니메이션 효 과 를 실현 하 는 데 사 용 됩 니 다.원본 코드:

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   ,             
    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 {//  ,    
       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();
   }
  }
 }

 //    ,                   
 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(); //     ,       
   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());
   } 
  }
 }

 //    ,     
 private static class SingletonHolder {
  //      , JVM       
  private static CharPanel instance = new CharPanel();
 }

 //      
 public static CharPanel getInstance(JComponent component) {
  if (component != null) {
   SingletonHolder.instance.mComponent = component;
  }
  return SingletonHolder.instance;
 }

 //        ,             
 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);
  }
 }

 //          
 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];
 }
}

두 개의 핵심 함수 run()과 draw()를 설명 하 세 요.run()함 수 는 새 스 레 드 를 열 고 실행 을 시작 하 는 함수 입 니 다.플러그 인 이 열 릴 때 계속 순환 합 니 다.CharPanel 은 사용자 가 삭제 하거나 추가 한 문자열 을 유지 하기 위해 2 개의 집합 을 사 용 했 습 니 다.charSet 은 직접 표 시 됩 니 다.bufferList 는 DocumentListener 모니터 가 들 은 입력 이나 삭 제 된 문자열 을 저장 합 니 다.입력 하거나 삭제 한 문자열 은 CharObj 클래스 에 봉 인 됩 니 다.run 함수 에서 매번 순환 하기 전에 bufferList 의 데 이 터 를 모두 charset 로 옮 깁 니 다.왜 두 개의 집합 을 사용 합 니까?이 는 charset 를 순환 적 으로 옮 겨 다 닐 때 DocumentListener 가 들 은 변화 데 이 터 를 charset 에 직접 추가 하면 오류 가 발생 할 수 있 기 때문이다.자바 의 집합 이 모든 시간 에 걸 쳐 있 기 때문에 그 안에 있 는 요 소 를 추가 하거나 삭제 할 수 없습니다.
run 함 수 는 순환 할 때마다 draw()함 수 를 호출 합 니 다.draw()함 수 는 CharObj 가 봉 인 된 데이터 에 따라 JLabel 의 위치 속성 과 글꼴 속성 을 다시 설정 합 니 다.그러면 JLabel 은 애니메이션 효 과 를 가 집 니 다.run 함수 가 순환 할 때마다 글꼴 크기 와 위치 데 이 터 를 점차적으로 수정 하기 때 문 입 니 다.
5.소스 코드
다른 코드 는 비교적 간단 해서 코드 에 대한 설명 도 별로 재미 가 없다.직접 소스 코드 를 드 리 겠 습 니 다.궁금 한 점 이 있 으 면 메 시 지 를 남 겨 주세요.저 는 가능 한 한 시간 을 내 서 일일이 답 하 겠 습 니 다.
Github 주소:https://github.com/huachao1001/Amazing-Mode
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기