效果
主要类(继承JTextArea监听输入,使用JComboBox显示下拉)
可以手动添加候选信息或,使用配置文件,读取配置文件
public class AutoCompleteTextArea extends JTextArea implements AutoCompleteListener {
private Map<String,TipInfo> map;
private JComboBox<String> dropDownBox;
private DefaultComboBoxModel<String> model;
private int infoWidth;
private FontMetrics metrics;
private int fontHeight;
private int matchLen;
// 设置提示框宽度
public void setInfoWidth(int infoWidth) {
this.infoWidth = infoWidth;
}
// 设置字体大小
@Override
public void setFont(Font f) {
super.setFont(f);
updateFontSize();
}
public AutoCompleteTextArea() {
initData();
setComponent();
}
private void setComponent() {
add(dropDownBox);
getDocument().addDocumentListener(this);
addKeyListener(this);
}
private void initData() {
infoWidth=100;
map=new HashMap<>();
model=new DefaultComboBoxModel<>();
dropDownBox=new JComboBox<>(model);
updateFontSize();
}
private void updateFontSize() {
metrics = getFontMetrics(getFont());
fontHeight=metrics.getHeight();
}
public void addEntry(String key,TipInfo value){
map.put(key,value);
}
// 直接添加提示信息
public void addMap(Map<String,TipInfo> map){
this.map.putAll(map);
}
// 加载外部属性文件
public void loadProperties(Properties properties){
Set<String> names = properties.stringPropertyNames();
for (String name:names){
String property = properties.getProperty(name);
addEntry(name,getTipInfoByString(property));
}
}
// 添加提示信息
private TipInfo getTipInfoByString(String property) {
String[] split = property.split("&");
if(split.length==1){
return new TipInfo(split[0]);
}else {
return new TipInfo(split[0],Integer.parseInt(split[1]));
}
}
@Override
public void insertUpdate(DocumentEvent e) {
if(e.getLength()!=1)return;
fireTextChange(e.getOffset()+1);
}
private void fireTextChange(int pos) {
String text = getText();
int start=pos-1;
while (start>=0&&isChar(text.charAt(start))){
start--;
}
if(start==pos-1)return;//没有进展 不要提示了!!!
String hotWord = text.substring(start+1,pos);
model.removeAllElements();
dropDownBox.setPopupVisible(false);
//设置符合候选词
map.keySet().stream().filter(s->s.startsWith(hotWord)).forEach(s->model.addElement(s));
if(model.getSize()==0)return;
matchLen=hotWord.length();
try {
//显示候选框
showDropDownBox(text,hotWord.length());
} catch (BadLocationException e) {
e.printStackTrace();
}
}
// 判断是否为合法字符 减少处理量
private boolean isChar(char c) {
return (c>96&&c<123)||(c>64&&c<91);
}
@Override
public void removeUpdate(DocumentEvent e) {
if(e.getLength()!=1||e.getOffset()==0)return;
fireTextChange(e.getOffset());
}
// 显示下拉框
private void showDropDownBox(String text,int len) throws BadLocationException {
int position = getCaretPosition();
int lineCount = getLineOfOffset(position)+1;//根据当前位置 获取当前所在行
int offset = getLineStartOffset(lineCount - 1);
int stringWidth = metrics.stringWidth(text.substring(offset, position - len + 1));
dropDownBox.setBounds(stringWidth,fontHeight*lineCount,infoWidth,0);
dropDownBox.setPopupVisible(true);
}
// 监听鼠标按键
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if(keyCode==KeyEvent.VK_SPACE){
Object selectedItem = dropDownBox.getSelectedItem();
if(selectedItem!=null){
int position = getCaretPosition();
TipInfo tipInfo = map.get(selectedItem.toString());
//替换范围
replaceRange(tipInfo.getContent(),position-matchLen,position);
setCaretPosition(position-matchLen+tipInfo.getPointPos());
dropDownBox.setSelectedItem(null);
dropDownBox.setPopupVisible(false);
}
}else if(keyCode==KeyEvent.VK_ESCAPE){
dropDownBox.setSelectedItem(null);
dropDownBox.setPopupVisible(false);
}else if(keyCode==KeyEvent.VK_UP||keyCode==KeyEvent.VK_DOWN){
dropDownBox.dispatchEvent(e);
}
}
}
接口监听输入事件
public interface AutoCompleteListener extends DocumentListener, KeyListener {
@Override
default void keyTyped(KeyEvent e) {
}
@Override
default void keyReleased(KeyEvent e) {
}
@Override
default void changedUpdate(DocumentEvent e) {
}
}
配置信息类
public class TipInfo {
private String content;
private int pointPos;
public TipInfo(String content, int pointPos) {
this.content = content;
this.pointPos = pointPos;
}
public TipInfo(String content) {
this.content = content;
pointPos =content.length();
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public int getPointPos() {
return pointPos;
}
public void setPointPos(int pointPos) {
this.pointPos = pointPos;
}
}
使用
public class MyIDE extends JFrame implements ActionListener {
public static void main(String[] args) {
SwingUtilities.invokeLater(MyIDE::new);
}
private JButton create;
private JTextField name;
private JTextField args;
private JButton run;
private AutoCompleteTextArea textArea;
private String className;
private JavaCompiler compiler;
private static final String BASE_CLASS="public class %s {\n\n\n\n}";
public MyIDE() throws HeadlessException {
initData();
setFrame();
}
private void setFrame() {
setSize(600,400);
setDefaultCloseOperation(EXIT_ON_CLOSE);
add(textArea);
JPanel bottom = new JPanel(new FlowLayout());
bottom.add(new JLabel("类名:"));
bottom.add(name);
bottom.add(create);
bottom.add(new JLabel("参数:"));
bottom.add(args);
bottom.add(run);
add(bottom,BorderLayout.SOUTH);
setVisible(true);
}
private void initData() {
textArea = new AutoCompleteTextArea();
setData(textArea);
create=new JButton("创建");
create.addActionListener(this);
name=new JTextField(15);
args=new JTextField(15);
run=new JButton("运行");
run.addActionListener(this);
compiler = ToolProvider.getSystemJavaCompiler();
}
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource()==create){
className=name.getText();
textArea.setText(String.format(BASE_CLASS, className));
}else if(e.getSource()==run){
if(className==null)return;
createFile();
compiler();
}
}
private void compiler() {
int res = compiler.run(System.in, System.out, System.err, "src/files/" + className + ".java");
if(res==0)invoke();
}
private void invoke() {
try {
Class clazz = new MyClassLoad().findClass(className);
Method main = clazz.getMethod("main", String[].class);
String[] array = args.getText().split(",");
main.invoke(null,new Object[]{array});
} catch (Exception e) {
e.printStackTrace();
}
}
private void createFile() {
try {
Files.write(Paths.get("src/files",className+".java"),textArea.getText().getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
private void setData(AutoCompleteTextArea textArea) {
Properties properties = getProperties();
textArea.loadProperties(properties);
}
private Properties getProperties() {
Properties properties = new Properties();
try {
FileInputStream in = new FileInputStream("src/files/map.properties");
properties.load(in);
in.close();
} catch (IOException e) {
e.printStackTrace();
}
return properties;
}
static class MyClassLoad extends ClassLoader{
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] bytes = Files.readAllBytes(Paths.get("src/files", name + ".class"));
return defineClass(name,bytes,0,bytes.length);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
}
加载的配置文件(使用&分割,后面的数字代表插入内容后光标相对开始的偏移,也可以不指定,光标会处于插入文本的后面)
sout=System.out.println();&19
system=System
string=String
psvm=public static void main(String[] args) {\n\n}&41
最终效果
还有很多地方需要完善,不过最近比较忙,可能暂时不会再改进