1. 程式人生 > >制作一個自己的 Java 編輯器

制作一個自己的 Java 編輯器

round .tar.gz nis panel proc 紅色 action javax 實驗樓

難度中等,適合 Java 基礎紮實,對 Java 核心 API 有所熟悉的同學學習

No1、制作GUI界面

一、實驗介紹

1.1 實驗內容

本節課程的主要內容是準備開發環境,建立項目並完成 GUI 界面的編程實現。

1.2 實驗知識點

Java Swing 編程

1.3 實驗環境

本實驗環境采用帶桌面的 Ubuntu Linux 環境,實驗中會用到的環境或軟件:

JDK1.7

Eclipse。

1.4 適合人群

本節課程難度較低,屬於初級課程,適合要想學習Java Swing 編程的同學學習。

1.5 代碼獲取

你可以在Xfce終端下通過下面命令將實驗的完整工程項目下載到實驗樓環境中,作為參照對比進行學習。

$ wget http://labfile.oss.aliyuncs.com/courses/287/MyEdit.tar.gz

二、項目文件結構

三、實驗步驟

這一節我們將開發GUI界面。

3.1 新建項目

首先請雙擊打開桌面上的 Eclipse ,等待它啟動完成後,在菜單 File 中點擊 New -> Java Project選項。 此處輸入圖片的描述

在彈出的窗口裏填寫項目的名稱 MyEdit,然後點擊 Finish 按鈕。

技術分享圖片

3.2 創建包和類

項目創建完成後,我們需要按照之前的項目結構來創建各個類。本項目一共有兩個類:

FileWindow:主要方法類,用作GUI界面以及邏輯功能的實現

Main:測試類

因此我們首先需要創建一個名為 com.hijackykun.myedit 的包。

請在創建好的項目目錄 src 文件夾上右鍵點擊,然後選擇 New -> Package。

在彈出的 New Java Package 對話框中填寫包名com.shiyanlou.myedit,並點擊 Finish 按鈕。

最後,在新建好的包下新建FileWindow和Main類。

3.3 GUI 界面的實現

GUI界面的效果圖如下:

技術分享圖片

界面的設計采用卡片布局(CardLayout),白色文本域為程序輸入區,粉紅色文本域為編譯結果顯示區,淺藍色文本域為程序運行結果區。點擊上方不同的功能按鈕顯示對應的文本域。

在FileWindow類中編寫實現 GUI 的代碼,相關的代碼如下,代碼的講解將會以註釋的形式進行,請在編寫代碼的同時留意相關的註釋。

註:以下代碼均未導入相關的包

public class FileWindow extends JFrame implements ActionListener, Runnable {

/*註意:因為實現了ActionListener 和Runnable接口,所以必須要實現這兩個接口的方法。這裏我們先把這兩個方法簡單實現以下。下節課將徹底完成這兩個方法。*/

Thread compiler = null;

Thread run_prom = null;

boolean bn = true;

CardLayout mycard; //聲明布局,以後會用到

File file_saved = null;

JButton button_input_txt, //按鈕的定義

button_compiler_text,

button_compiler,

button_run_prom,

button_see_doswin;

JPanel p = new JPanel();

JTextArea input_text = new JTextArea(); // 程序輸入區

JTextArea compiler_text = new JTextArea();// 編譯錯誤顯示區

JTextArea dos_out_text = new JTextArea();// 程序的輸出信息

JTextField input_file_name_text = new JTextField();

JTextField run_file_name_text = new JTextField();

public FileWindow() {

// TODO Auto-generated constructor stub

super("Java語言編譯器");

mycard = new CardLayout();

compiler=new Thread(this);

run_prom=new Thread(this);

button_input_txt=new JButton("程序輸入區(白色)");

button_compiler_text=new JButton("編譯結果區(粉紅色)");

button_see_doswin=new JButton("程序運行結果(淺藍色)");

button_compiler=new JButton("編譯程序");

button_run_prom=new JButton("運行程序");

p.setLayout(mycard);//設置卡片布局

p.add("input",input_text);//定義卡片名稱

p.add("compiler", compiler_text);

p.add("dos",dos_out_text);

add(p,"Center");

compiler_text.setBackground(Color.pink); //設置顏色

dos_out_text.setBackground(Color.cyan);

JPanel p1=new JPanel();

p1.setLayout(new GridLayout(3, 3)); //設置表格布局

//添加組件

p1.add(button_input_txt);

p1.add(button_compiler_text);

p1.add(button_see_doswin);

p1.add(new JLabel("輸入編譯文件名(.java):"));

p1.add(input_file_name_text);

p1.add(button_compiler);

p1.add(new JLabel("輸入應用程序主類名"));

p1.add(run_file_name_text);

p1.add(button_run_prom);

add(p1,"North");

//定義事件

button_input_txt.addActionListener(this);

button_compiler.addActionListener(this);

button_compiler_text.addActionListener(this);

button_run_prom.addActionListener(this);

button_see_doswin.addActionListener(this);

}

public void actionPerformed(ActionEvent e)

{

//實現方法

}

@Override

public void run() {

//實現方法

}

}

到此,我們的 GUI 界面就算做好了!

3.4 測試類的實現

下面,我們趕緊做個測試類,測試一下我們的界面。

Main.java:

import java.awt.event.WindowAdapter;

import java.awt.event.WindowEvent;

public class Main {

public static void main(String[] args) {

// TODO Auto-generated method stub

FileWindow win=new FileWindow();

win.pack();

win.addWindowListener(new WindowAdapter() {

public void windowClosing(WindowEvent e)

{

System.exit(0);

}

});

win.setBounds(200, 180,550,360);

win.setVisible(true);

}

}

界面和測試類就完成了。

四、實驗總結

在本節實驗中,我們完成了項目的創建以及 GUI 界面,下節實驗我們將完善本節的遺留問題,實現界面組件事件響應邏輯和Java文件的編輯、編譯及運行。主要包括兩個方法:

public void actionPerformed(ActionEvent e)

public void run()

No2、實現功能

一、實驗介紹

1.1 實驗內容

在上節實驗中我們完成了編輯器的界面,可是按鈕的響應功能並未完成,在本節實驗中我們將實現界面組件事件響應邏輯和Java文件的編輯、編譯及運行。主要包括兩個方法:

public void actionPerformed(ActionEvent e)

public void run()

1.2 實驗知識點

Java Swing 編程

IO 流操作

Runtime 類

Thread 的使用

1.3 實驗環境

本實驗環境采用帶桌面的 Ubuntu Linux 環境,實驗中會用到的環境或軟件:

JDK1.7

Eclipse。

1.4 適合人群

本節課程難度較難,屬於中級課程,適合對 Java 核心 API 有較深入理解的同學學習。

1.5 代碼獲取

你可以在Xfce終端下通過下面命令將實驗的完整工程項目下載到實驗樓環境中,作為參照對比進行學習。

$ wget http://labfile.oss.aliyuncs.com/courses/287/MyEdit.tar.gz

二、項目文件結構

三、實驗步驟

3.1 actionPerformed 方法的實現

首先咱們實現 public void actionPerformed(ActionEvent e) 這個方法。代碼中的註釋進行了詳細的講解。

public void actionPerformed(ActionEvent e)

{

if(e.getSource()==button_input_txt)

{ //顯示程序輸入區

mycard.show(p,"input");

}

else if(e.getSource()==button_compiler_text)

{ //顯示編譯結果顯示區

mycard.show(p,"compiler");

}

else if(e.getSource()==button_see_doswin)

{ //顯示程序運行結果區

mycard.show(p,"dos");

}

else if(e.getSource()==button_compiler)

{ //如果是編譯按鈕,執行編譯文件的方法

if(!(compiler.isAlive()))

{

compiler=new Thread(this);

}

try {

compiler.start();

} catch (Exception e2) {

// TODO: handle exception

e2.printStackTrace();

}

mycard.show(p,"compiler");

}

else if(e.getSource()==button_run_prom)

{ //如果是運行按鈕,執行運行文件的方法

if(!(run_prom.isAlive()))

{

run_prom=new Thread(this);

}

try {

run_prom.start();

} catch (Exception e2) {

// TODO: handle exception

e2.printStackTrace();

}

mycard.show(p,"dos");

}

}

以上的代碼就是通過比較來判斷需要處理哪些事件。

3.2 run 方法的實現

然後就剩一個 run() 方法,也是最重要的一個方法。在這個方法裏會判斷是編譯還是運行:

如果當前Thread是編譯,那麽會將程序輸入區中的代碼以.java文件的形式保存到項目的當前目錄下,並通過javac命令執行剛才保存的.java文件生成.class文件,編譯後的信息會輸出到編譯結果顯示區。

如果當前Thread是運行,那麽會通過java命令執行編譯生成的.class文件,並將程序結果顯示到程序運行結果區中。

public void run() {

//TODO Auto-generated method stub

if(Thread.currentThread()==compiler)

{

compiler_text.setText(null);

String temp=input_text.getText().trim();

byte [] buffer=temp.getBytes();

int b=buffer.length;

String file_name=null;

file_name=input_file_name_text.getText().trim();

try {

file_saved=new File(file_name);

FileOutputStream writefile=null;

writefile=new FileOutputStream(file_saved);

writefile.write(buffer, 0, b);

writefile.close();

} catch (Exception e) {

// TODO: handle exception

System.out.println("ERROR");

}

try {

//獲得該進程的錯誤流,才可以知道運行結果到底是失敗了還是成功。

Runtime rt=Runtime.getRuntime();

InputStream in=rt.exec("javac "+file_name).getErrorStream(); //通過Runtime調用javac命令。註意:“javac ”這個字符串是有一個空格的!!

BufferedInputStream bufIn=new BufferedInputStream(in);

byte[] shuzu=new byte[100];

int n=0;

boolean flag=true;

//輸入錯誤信息

while((n=bufIn.read(shuzu, 0,shuzu.length))!=-1)

{

String s=null;

s=new String(shuzu,0,n);

compiler_text.append(s);

if(s!=null)

{

flag=false;

}

}

//判斷是否編譯成功

if(flag)

{

compiler_text.append("Compile Succeed!");

}

} catch (Exception e) {

// TODO: handle exception

}

}

else if(Thread.currentThread()==run_prom)

{

//運行文件,並將結果輸出到dos_out_text

dos_out_text.setText(null);

try {

Runtime rt=Runtime.getRuntime();

String path=run_file_name_text.getText().trim();

Process stream=rt.exec("java "+path);//調用java命令

InputStream in=stream.getInputStream();

BufferedInputStream bisErr=new BufferedInputStream(stream.getErrorStream());

BufferedInputStream bisIn=new BufferedInputStream(in);

byte[] buf=new byte[150];

byte[] err_buf=new byte[150];

@SuppressWarnings("unused")

int m=0;

@SuppressWarnings("unused")

int i=0;

String s=null;

String err=null;

//打印編譯信息及錯誤信息

while((m=bisIn.read(buf, 0, 150))!=-1)

{

s=new String(buf,0,150);

dos_out_text.append(s);

}

while((i=bisErr.read(err_buf))!=-1)

{

err=new String(err_buf,0,150);

dos_out_text.append(err);

}

}

catch (Exception e) {

// TODO: handle exception

}

}

}

3.3 進行簡單測試

點擊編譯按鈕會出現錯誤信息,證明距離成功不遠了。

運行按鈕錯誤:

點擊按鈕在程序輸入區(白色),寫一個簡單的測試小程序吧!代碼如下:

class a

{

public static void main(String [] args)

{

System.out.println("Hello ShiYanLou");

}

}

接著在輸入編譯文件名(.java)後面的文本框裏填入與類名相同的.java文件,如a.java,點擊編譯程序。

如果程序沒有出錯,那麽編譯結果顯示如下:

在輸入應用程序主類名後面的文本框裏填入類名,如a,點擊運行程序。

程序的運行結果將會顯示在淺藍色的文本域中。

重新編輯的時候需要點擊按鈕在程序輸入區(白色),在白色文本域進行輸入。

三、實驗總結

至此,我們終於完成了整個程序,實現了編輯Java代碼、編譯和運行Java文件的功能。本次課程涉及的知識點比較復雜,特別是 Runtime 類和 Thread 的使用,希望同學們下來能夠對這些知識點進行鞏固。

五、課後習題

同學們下來考慮如何豐富其功能,例如 “代碼高亮”、“代碼自動補全” 等等。這些功能有的比較難,不一定要實現,但要勤於思考。

具體代碼:

FileWindow.java

技術分享圖片
package com.hijackykun.myedit;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;

@SuppressWarnings("serial")
//壓制信息,不會的同學可以不理會。
public class FileWindow extends JFrame implements ActionListener,Runnable{
     /*註意:因為實現了ActionListener 和Runnable接口,所以必須要實現這兩個接口的方法。
      * 這裏我們先把這兩個方法簡單實現以下。下節課將徹底完成這兩個方法。*/
    Thread compiler = null;
    Thread run_prom = null;
    boolean bn = true;
    CardLayout mycard;  //聲明布局,以後會用到
    File file_saved = null;
    JButton button_input_txt,   //按鈕的定義
            button_compiler_text,
            button_compiler,
            button_run_prom,
            button_see_doswin;
    JPanel p = new JPanel();
    JTextArea input_text = new JTextArea(); // 程序輸入區
    JTextArea compiler_text = new JTextArea();// 編譯錯誤顯示區
    JTextArea dos_out_text = new JTextArea();// 程序的輸出信息
    JTextField input_file_name_text = new JTextField();
    JTextField run_file_name_text = new JTextField();
    public FileWindow() {
        // TODO Auto-generated constructor stub
        super("Java語言編譯器");
        mycard = new CardLayout();
        compiler=new Thread(this);
        run_prom=new Thread(this);
        button_input_txt=new JButton("程序輸入區(白色)");
        button_compiler_text=new JButton("編譯結果區(粉紅色)");
        button_see_doswin=new JButton("程序運行結果(淺藍色)");
        button_compiler=new JButton("編譯程序");
        button_run_prom=new JButton("運行程序");
        p.setLayout(mycard);//設置卡片布局
        p.add("input",input_text);//定義卡片名稱
        p.add("compiler", compiler_text);
        p.add("dos",dos_out_text);
        add(p,"Center");

        compiler_text.setBackground(Color.pink); //設置顏色
        dos_out_text.setBackground(Color.cyan);
        JPanel p1=new JPanel();

        p1.setLayout(new GridLayout(3, 3)); //設置表格布局
        //添加組件
        p1.add(button_input_txt);
        p1.add(button_compiler_text);
        p1.add(button_see_doswin);
        p1.add(new JLabel("輸入編譯文件名(.java):"));
        p1.add(input_file_name_text);
        p1.add(button_compiler);
        p1.add(new JLabel("輸入應用程序主類名"));
        p1.add(run_file_name_text);
        p1.add(button_run_prom);
        add(p1,"North");

        //定義事件
        button_input_txt.addActionListener(this);
        button_compiler.addActionListener(this);
        button_compiler_text.addActionListener(this);
        button_run_prom.addActionListener(this);
        button_see_doswin.addActionListener(this);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        // TODO Auto-generated method stub
         if(e.getSource()==button_input_txt)
         {    //顯示程序輸入區
             mycard.show(p,"input");
         }
         else if(e.getSource()==button_compiler_text)
         {    //顯示編譯結果顯示區
             mycard.show(p,"compiler");
         }
         else if(e.getSource()==button_see_doswin)
         {    //顯示程序運行結果區
             mycard.show(p,"dos");
         }
         else if(e.getSource()==button_compiler)
         {    //如果是編譯按鈕,執行編譯文件的方法
             if(!(compiler.isAlive()))
             {
                 compiler=new Thread(this);
             }
             try {
                 compiler.start();

             } catch (Exception e2) {
                 // TODO: handle exception
                 e2.printStackTrace();
             }

             mycard.show(p,"compiler");

         }
         else if(e.getSource()==button_run_prom)
         {    //如果是運行按鈕,執行運行文件的方法
             if(!(run_prom.isAlive()))
             {
                 run_prom=new Thread(this);
             }
             try {
                 run_prom.start();
             } catch (Exception e2) {
                 // TODO: handle exception
                 e2.printStackTrace();
             }
             mycard.show(p,"dos");
         }

    }
    
    @Override
    public void run() {
        // TODO Auto-generated method stub
        if (Thread.currentThread() == compiler) {
            compiler_text.setText(null);
            String temp = input_text.getText().trim();
            byte[] buffer = temp.getBytes();
            int b = buffer.length;
            String file_name = null;
            file_name = input_file_name_text.getText().trim();

            try {
                file_saved = new File(file_name);
                FileOutputStream writefile = null;
                writefile = new FileOutputStream(file_saved);
                writefile.write(buffer, 0, b);
                writefile.close();
            } catch (Exception e) {
                // TODO: handle exception
                System.out.println("ERROR");
            }
            try {
                // 獲得該進程的錯誤流,才可以知道運行結果到底是失敗了還是成功。
                Runtime rt = Runtime.getRuntime();
                InputStream in = rt.exec("javac " + file_name).getErrorStream(); 
                // 通過Runtime調用javac命令。註意:“javac ”這個字符串是有一個空格的!!
                BufferedInputStream bufIn = new BufferedInputStream(in);
                byte[] shuzu = new byte[100];
                int n = 0;
                boolean flag = true;
                // 輸入錯誤信息
                while ((n = bufIn.read(shuzu, 0, shuzu.length)) != -1) {
                    String s = null;
                    s = new String(shuzu, 0, n);
                    compiler_text.append(s);
                    if (s != null) {
                        flag = false;
                    }
                }
                // 判斷是否編譯成功
                if (flag) {
                    compiler_text.append("Compile Succeed!");
                }
            } catch (Exception e) {
                // TODO: handle exception
            }
        } else if (Thread.currentThread() == run_prom) {
            // 運行文件,並將結果輸出到dos_out_text
            dos_out_text.setText(null);
            try {
                Runtime rt = Runtime.getRuntime();
                String path = run_file_name_text.getText().trim();
                Process stream = rt.exec("java " + path);// 調用java命令
                InputStream in = stream.getInputStream();
                BufferedInputStream bisErr = new BufferedInputStream(
                        stream.getErrorStream());
                BufferedInputStream bisIn = new BufferedInputStream(in);
                byte[] buf = new byte[150];
                byte[] err_buf = new byte[150];
                @SuppressWarnings("unused")
                int m = 0;
                @SuppressWarnings("unused")
                int i = 0;
                String s = null;
                String err = null;
                // 打印編譯信息及錯誤信息
                while ((m = bisIn.read(buf, 0, 150)) != -1) {
                    s = new String(buf, 0, 150);
                    dos_out_text.append(s);
                }
                while ((i = bisErr.read(err_buf)) != -1) {
                    err = new String(err_buf, 0, 150);
                    dos_out_text.append(err);
                }
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
    }
}
View Code

Main.java

技術分享圖片
package com.hijackykun.myedit;

import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

public class Main {

    public static void main(String[] args) {
        FileWindow win =new FileWindow();
        win.pack();//根據窗口裏面的布局及組件的preferredSize來確定frame的最佳大小
        win.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
        //setBounds(x,y,width,height); 
        //x:組件在容器X軸上的起點 y:組件在容器Y軸上的起點 width:組件的長度 height:組件的高度
        win.setBounds(200, 180, 550, 360);
        win.setVisible(true);
    }

}
View Code

參考來源:https://www.shiyanlou.com/courses/287

制作一個自己的 Java 編輯器