使用 Java 構建你自己的文本編輯器

有時候,除你自己外,沒有人能製作你所夢想的工具。以下是如何開始構建你自己的文本編輯器。

有很多文本編輯器。有運行在終端中、運行在 GUI 中、運行在瀏覽器和瀏覽器引擎中的。有很多是還不錯,有一些則是極好的。但是有時候,毫無疑問,最令人滿意的就是你自己構建的編輯器。

毫無疑問:構建一個真正優秀的文本編輯器比表面上看上去要困難得多。但話說回來,建立一個基本的文本編輯器也不像你擔心的那樣難。事實上,大多數編程工具包已經爲你準備好了文本編輯器的大部分組件。圍繞文本編輯的組件,例如菜單條,文件選擇對話框等等,是很容易落到實處。因此,雖然是中級的編程課程,但構建一個基本的文本編輯器是出乎意料的有趣和簡明。你可能會發現自己渴望使用一個自己構造的工具,而且你使用得越多,你可能會有更多的靈感來增加它的功能,從而更多地學習你正在使用的編程語言。

爲了使這個練習切合實際,最好選擇一種具有令人滿意的 GUI 工具箱的語言。有很多種選擇,包括 Qt 、FLTK 或 GTK ,但是一定要先評審一下它的文檔,以確保它有你所期待的功能。對於這篇文章來說,我使用 Java 以及其內置的 Swing 小部件集。如果你想使用一種不同的語言或者一種不同的工具集,這篇文章在如何幫你處理這種問題的方面也仍然是有用的。

不管你選擇哪一種,在任何主要的工具箱中編寫一個文本編輯器都是驚人的相似。如果你是 Java 新手,需要更多關於開始的信息,請先閱讀我的 猜謎遊戲文章 。

工程設置

通常,我使用並推薦像 Netbeans 或 Eclipse 這樣的 IDE,但我發現,當學習一種新的語言時,手工做一些工作是很有幫助的,這樣你就能更好地理解使用 IDE 時被隱藏起來的東西。在這篇文章中,我假設你正在使用文本編輯器和終端進行編程。

在開始前,爲你自己的工程創建一個工程目錄。在工程文件夾中,創建一個名稱爲 src 的目錄來容納你的源文件。

$ mkdir -p myTextEditor/src
$ cd myTextEditor

在你的 src 目錄中創建一個名稱爲 TextEdit.java 的空白的文件:

$ touch src/TextEditor.java

在你最喜歡的文本編輯器中打開這個空白的文件(我的意思是除你自己編寫之外的最喜歡的一款文本編輯器),然後準備好編碼吧!

包和導入

爲確保你的 Java 應用程序有一個唯一的標識符,你必須聲明一個 package 名稱。典型的格式是使用一個反向的域名,如果你真的有一個域名的話,這就特別容易了。如果你沒有域名的話,你可以使用 local 作爲最頂層。像 Java 和很多語言一樣,行以分號結尾。

在命名你的 Java 的 package 後,你必須告訴 Java 編譯器(javac)使用哪些庫來構建你的代碼。事實上,這通常是你邊編寫代碼邊添加的內容,因爲你很少事先知道你自己所需要的庫。然而,這裏有一些庫是顯而易見的。例如,你知道這個文本編輯器是基於 Swing GUI 工具箱的,因此,導入 javax.swing.JFrame 和javax.swing.UIManager 和其它相關的特定庫。

package com.example.textedit;

import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.filechooser.FileSystemView;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;

對於這個練習的目標,你可以提前預知你所需要的所有的庫。在真實的生活中,不管你喜歡哪一種語言,你都將在研究如何解決一些問題的時候發現庫,然後,你將它導入到你的代碼中,並使用它。不需要擔心 —— 如果你忘記包含一個庫,你的編譯器或解釋器將警告你!

主窗口

這是一個單窗口應用程序,因此這個應用程序的主類是一個 JFrame ,其附帶有一個捕捉菜單事件的 ActionListener 。在 Java 中,當你使用一個現有的小部件元素時,你可以使用你的代碼“擴展”它。這個主窗口需要三個字段:窗口本身(一個 JFrame 的實例)、一個用於文件選擇器返回值的標識符和文本編輯器本身(JTextArea)。

public final class TextEdit extends JFrame implements ActionListener {
private static JTextArea area;
private static JFrame frame;
private static int returnValue = 0;

令人驚奇的是,這數行代碼完成了實現一個基本文本編輯器的 80% 的工作,因爲 JtextArea 是 Java 的文本輸入字段。剩下的 80 行代碼大部分用於處理輔助功能,比如保存和打開文件。

構建一個菜單

JMenuBar 小部件被設計到 JFrame 的頂部,它爲你提供你想要的很多菜單項。Java 不是一種 拖放式的編程語言,因此,對於你所添加的每一個菜單,你都還必須編寫一個函數。爲保持這個工程的可控性,我提供了四個函數:創建一個新的文件,打開一個現有的文件,保存文本到一個文件,和關閉應用程序。

在大多數流行的工具箱中,創建一個菜單的過程基本相同。首先,你創建菜單條本身,然後創建一個頂級菜單(例如 “File” ),再然後創建子菜單項(例如,“New”、“Save” 等)。

public TextEdit() { run(); }

public void run() {
    frame = new JFrame("Text Edit");

    // Set the look-and-feel (LNF) of the application
    // Try to default to whatever the host system prefers
    try {
      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
      Logger.getLogger(TextEdit.class.getName()).log(Level.SEVERE, null, ex);
    }

    // Set attributes of the app window
    area = new JTextArea();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(area);
    frame.setSize(640, 480);
    frame.setVisible(true);

    // Build the menu
    JMenuBar menu_main = new JMenuBar();

    JMenu menu_file = new JMenu("File");

    JMenuItem menuitem_new = new JMenuItem("New");
    JMenuItem menuitem_open = new JMenuItem("Open");
    JMenuItem menuitem_save = new JMenuItem("Save");
    JMenuItem menuitem_quit = new JMenuItem("Quit");

    menuitem_new.addActionListener(this);
    menuitem_open.addActionListener(this);
    menuitem_save.addActionListener(this);
    menuitem_quit.addActionListener(this);

    menu_main.add(menu_file);

    menu_file.add(menuitem_new);
    menu_file.add(menuitem_open);
    menu_file.add(menuitem_save);
    menu_file.add(menuitem_quit);

    frame.setJMenuBar(menu_main);
    }

現在,所有剩餘的工作是實施菜單項所描述的功能。

編程菜單動作

你的應用程序響應菜單選擇,是因爲你的 JFrame 有一個附屬於它的 ActionListener 。在 Java 中,當你實施一個事件處理程序時,你必須“重寫”其內建的函數。這只是聽起來可怕。你不是在重寫 Java;你只是在實現已經被定義但尚未實施事件處理程序的函數。

在這種情況下,你必須重寫 actionPerformed方法。因爲在 “File” 菜單中的所有條目都與處理文件有關,所以在我的代碼中很早就定義了一個 JFileChooser 。代碼其它部分被劃分到一個 if 語句的子語句中,這起來像接收到什麼事件就相應地執行什麼動作。每個子語句都與其它的子語句完全不同,因爲每個項目都標示着一些完全唯一的東西。最相似的是 “Open” 和 “Save”,因爲它們都使用 JFileChooser 選擇文件系統中的一個位置來獲取或放置數據。

“New” 菜單會在沒有警告的情況下清理 JTextArea ,“Quit” 菜單會在沒有警告的情況下關閉應用程序。這兩個 “功能” 都是不安全的,因此你應該想對這段代碼進行一點改善,這是一個很好的開始。在內容還沒有被保存前,一個友好的警告是任何一個好的文本編輯器都必不可少的一個功能,但是在這裏爲了簡單,這是未來的一個功能。

@Override
public void actionPerformed(ActionEvent e) {
    String ingest = null;
    JFileChooser jfc = new JFileChooser(FileSystemView.getFileSystemView().getHomeDirectory());
    jfc.setDialogTitle("Choose destination.");
    jfc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);

    String ae = e.getActionCommand();
    if (ae.equals("Open")) {
        returnValue = jfc.showOpenDialog(null);
        if (returnValue == JFileChooser.APPROVE_OPTION) {
        File f = new File(jfc.getSelectedFile().getAbsolutePath());
        try{
            FileReader read = new FileReader(f);
            Scanner scan = new Scanner(read);
            while(scan.hasNextLine()){
                String line = scan.nextLine() + "\n";
                ingest = ingest + line;
        }
            area.setText(ingest);
        }
    catch ( FileNotFoundException ex) { ex.printStackTrace(); }
}
    // 保存
    } else if (ae.equals("Save")) {
        returnValue = jfc.showSaveDialog(null);
        try {
            File f = new File(jfc.getSelectedFile().getAbsolutePath());
            FileWriter out = new FileWriter(f);
            out.write(area.getText());
            out.close();
        } catch (FileNotFoundException ex) {
            Component f = null;
            JOptionPane.showMessageDialog(f,"File not found.");
        } catch (IOException ex) {
            Component f = null;
            JOptionPane.showMessageDialog(f,"Error.");
        }
    } else if (ae.equals("New")) {
        area.setText("");
    } else if (ae.equals("Quit")) { System.exit(0); }
  }
}

從技術上來說,這就是這個文本編輯器的全部。當然,並沒有真正做什麼,除此之外,在這裏仍然有測試和打包步驟,因此仍然有很多時間來發現缺少的必需品。假設你沒有注意到提示:在這段代碼中 肯定 缺少一些東西。你現在知道缺少的是什麼嗎?(在 猜謎遊戲文章 中被大量的提到。)

測試

你現在可以測試你的應用程序。從終端中啓動你所編寫的文本編輯器:

$ java ./src/TextEdit.java
error: can’t find main(String[]) method in class: com.example.textedit.TextEdit

它看起來像在代碼中沒有獲得 main 方法。這裏有一些方法來修復這個問題:你可以在 TextEdit.java 中創建一個 main 方法,並讓它運行一個 TextEdit 類實例,或者你可以創建一個單獨的包含 main 方法的文件。兩種方法都可以工作,但從大型工程的預期來看,使用後者更爲明智,因此,使用單獨的文件與其一起工作使之成爲一個完整的應用程序的方法是值得使用的。

在 src 中創建一個 Main.java 文件,並在最喜歡的編輯器中打開:

package com.example.textedit;

public class Main {
  public static void main(String[] args) {
  TextEdit runner = new TextEdit();
  }
}

你可以再次嘗試,但是現在有兩個相互依賴的文件要運行,因此你必須編譯代碼。Java 使用 javac 編譯器,並且你可以使用 -d 選項來設置目標目錄:

$ javac src/*java -d .

這會在你的軟件包名稱 com/example/textedit 後創建一個準確地模型化的新的目錄結構。這個新的類路徑包含文件 Main.class 和 TextEdit.class ,這兩個文件構成了你的應用程序。你可以使用 java 並通過引用你的 Main 類的位置和 名稱(非文件名稱)來運行它們:

$ java info/slackermedia/textedit/Main`

你的文本編輯器打開了,你可以在其中輸入文字,打開文件,甚至保存你的工作。

帶有單個下拉菜單的白色文本編輯器框,有 File、New、Open、Save 和 Quit 菜單

以 Java 軟件包的形式分享你的工作

雖然一些程序員似乎看起來認可以各種各樣的源文件的形式分發軟件包,並鼓勵其他人來學習如何運行它,但是,Java 讓打包應用程序變得真地很容易,以至其他人可以很容易的運行它。你已經有了必備的大部分結構體,但是你仍然需要一些元數據到一個 Manifest.txt 文件中:

$ echo "Manifest-Version: 1.0" > Manifest.txt

用於打包的 jar 命令,與 tar 命令非常相似,因此很多選項對你來說可能會很熟悉。要創建一個 JAR 文件:

$ jar cvfme TextEdit.jar
Manifest.txt
com.example.textedit.Main
com/example/textedit/*.class

根據命令的語法,你可以推測出它會創建一個新的名稱爲 TextEdit.jar 的 JAR 文件,它所需要的清單數據位於 Manifest.txt 中。它的主類被定義爲軟件包名稱的一個擴展,並且類自身是 com/example/textedit/Main.class 。

你可以查看 JAR 文件的內容:

$ jar tvf TextEdit.jar
0 Wed Nov 25 META-INF/
105 Wed Nov 25 META-INF/MANIFEST.MF
338 Wed Nov 25 com/example/textedit/textedit/Main.class
4373 Wed Nov 25 com/example/textedit/textedit/TextEdit.class

如果你想看看你的元數據是如何被集成到 MANIFEST.MF 文件中的,你甚至可以使用 xvf 選項來提取它。

使用 java 命令來運行你的 JAR 文件:

$ java -jar TextEdit.jar

你甚至可以 創建一個桌面文件 ,這樣,在單擊應用程序菜單中的圖標時,應用程序就會啓動。

改進它

在當前狀態下,這是一個非常基本的文本編輯器,最適合做快速筆記或簡短自述文檔。一些改進(比如添加垂直滾動條)只要稍加研究就能快速簡單地完成,而另一些改進(比如實現一個廣泛的偏好系統)則需要真正的工作。

但如果你一直在想學一種新的語言,這可能是一個完美的自我學習實用工程。創建一個文本編輯器,如你所見,它在代碼方面並不難對付,它在一定範圍是可控的。如果你經常使用文本編輯器,那麼編寫你自己的文本編輯器可能會使你滿意和樂趣。因此打開你最喜歡的文本編輯器(你寫的那個),開始添加功能吧!


via: https://opensource.com/article/20/12/write-your-own-text-editor

作者:Seth Kenlon 選題:lujun9972 譯者:robsean 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章