멋 진 AndroidStudio 플러그 인 만 들 기
14763 단어 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
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Android Studio에서 Material Icon을 빨리 캡처Android Studio 3.4 기능에 Resource Manager가 추가되었습니다. 이 Resource Manager를 사용하면 Material Icon을 앱에 쉽게 가져올 수 있습니다. Resource Man...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.