有時(shí)候,除你自己外,沒(méi)有人能制作你所夢(mèng)想的工具。以下是如何開(kāi)始構(gòu)建你自己的文本編輯器。
有很多文本編輯器。有運(yùn)行在終端中、運(yùn)行在 GUI 中、運(yùn)行在瀏覽器和瀏覽器引擎中的。有很多是還不錯(cuò),有一些則是極好的。但是有時(shí)候,毫無(wú)疑問(wèn),最令人滿意的就是你自己構(gòu)建的編輯器。
毫無(wú)疑問(wèn):構(gòu)建一個(gè)真正優(yōu)秀的文本編輯器比表面上看上去要困難得多。但話說(shuō)回來(lái),建立一個(gè)基本的文本編輯器也不像你擔(dān)心的那樣難。事實(shí)上,大多數(shù)編程工具包已經(jīng)為你準(zhǔn)備好了文本編輯器的大部分組件。圍繞文本編輯的組件,例如菜單條,文件選擇對(duì)話框等等,是很容易落到實(shí)處。因此,雖然是中級(jí)的編程課程,但構(gòu)建一個(gè)基本的文本編輯器是出乎意料的有趣和簡(jiǎn)明。你可能會(huì)發(fā)現(xiàn)自己渴望使用一個(gè)自己構(gòu)造的工具,而且你使用得越多,你可能會(huì)有更多的靈感來(lái)增加它的功能,從而更多地學(xué)習(xí)你正在使用的編程語(yǔ)言。
為了使這個(gè)練習(xí)切合實(shí)際,最好選擇一種具有令人滿意的 GUI 工具箱的語(yǔ)言。有很多種選擇,包括 Qt 、FLTK 或 GTK ,但是一定要先評(píng)審一下它的文檔,以確保它有你所期待的功能。對(duì)于這篇文章來(lái)說(shuō),我使用 Java 以及其內(nèi)置的 Swing 小部件集。如果你想使用一種不同的語(yǔ)言或者一種不同的工具集,這篇文章在如何幫你處理這種問(wèn)題的方面也仍然是有用的。
不管你選擇哪一種,在任何主要的工具箱中編寫(xiě)一個(gè)文本編輯器都是驚人的相似。如果你是 Java 新手,需要更多關(guān)于開(kāi)始的信息,請(qǐng)先閱讀我的 猜謎游戲文章 。
工程設(shè)置
通常,我使用并推薦像 Netbeans 或 Eclipse 這樣的 IDE,但我發(fā)現(xiàn),當(dāng)學(xué)習(xí)一種新的語(yǔ)言時(shí),手工做一些工作是很有幫助的,這樣你就能更好地理解使用 IDE 時(shí)被隱藏起來(lái)的東西。在這篇文章中,我假設(shè)你正在使用文本編輯器和終端進(jìn)行編程。
在開(kāi)始前,為你自己的工程創(chuàng)建一個(gè)工程目錄。在工程文件夾中,創(chuàng)建一個(gè)名稱為 src
的目錄來(lái)容納你的源文件。
-
$ mkdir -p myTextEditor/src
-
$ cd myTextEditor
在你的 src
目錄中創(chuàng)建一個(gè)名稱為 TextEdit.java
的空白的文件:
-
$ touch src/TextEditor.java
在你最喜歡的文本編輯器中打開(kāi)這個(gè)空白的文件(我的意思是除你自己編寫(xiě)之外的最喜歡的一款文本編輯器),然后準(zhǔn)備好編碼吧!
包和導(dǎo)入
為確保你的 Java 應(yīng)用程序有一個(gè)唯一的標(biāo)識(shí)符,你必須聲明一個(gè) package
名稱。典型的格式是使用一個(gè)反向的域名,如果你真的有一個(gè)域名的話,這就特別容易了。如果你沒(méi)有域名的話,你可以使用 local
作為最頂層。像 Java 和很多語(yǔ)言一樣,行以分號(hào)結(jié)尾。
在命名你的 Java 的 package
后,你必須告訴 Java 編譯器(javac
)使用哪些庫(kù)來(lái)構(gòu)建你的代碼。事實(shí)上,這通常是你邊編寫(xiě)代碼邊添加的內(nèi)容,因?yàn)槟愫苌偈孪戎滥阕约核枰膸?kù)。然而,這里有一些庫(kù)是顯而易見(jiàn)的。例如,你知道這個(gè)文本編輯器是基于 Swing GUI 工具箱的,因此,導(dǎo)入 javax.swing.JFrame
和javax.swing.UIManager
和其它相關(guān)的特定庫(kù)。
-
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;
對(duì)于這個(gè)練習(xí)的目標(biāo),你可以提前預(yù)知你所需要的所有的庫(kù)。在真實(shí)的生活中,不管你喜歡哪一種語(yǔ)言,你都將在研究如何解決一些問(wèn)題的時(shí)候發(fā)現(xiàn)庫(kù),然后,你將它導(dǎo)入到你的代碼中,并使用它。不需要擔(dān)心 —— 如果你忘記包含一個(gè)庫(kù),你的編譯器或解釋器將警告你!
主窗口
這是一個(gè)單窗口應(yīng)用程序,因此這個(gè)應(yīng)用程序的主類是一個(gè) JFrame
,其附帶有一個(gè)捕捉菜單事件的 ActionListener
。在 Java 中,當(dāng)你使用一個(gè)現(xiàn)有的小部件元素時(shí),你可以使用你的代碼“擴(kuò)展”它。這個(gè)主窗口需要三個(gè)字段:窗口本身(一個(gè) JFrame
的實(shí)例)、一個(gè)用于文件選擇器返回值的標(biāo)識(shí)符和文本編輯器本身(JTextArea
)。
-
public final class TextEdit extends JFrame implements ActionListener {
-
private static JTextArea area;
-
private static JFrame frame;
-
private static int returnValue = 0;
令人驚奇的是,這數(shù)行代碼完成了實(shí)現(xiàn)一個(gè)基本文本編輯器的 80% 的工作,因?yàn)?nbsp;JtextArea
是 Java 的文本輸入字段。剩下的 80 行代碼大部分用于處理輔助功能,比如保存和打開(kāi)文件。
構(gòu)建一個(gè)菜單
JMenuBar
小部件被設(shè)計(jì)到 JFrame 的頂部,它為你提供你想要的很多菜單項(xiàng)。Java 不是一種 拖放式的編程語(yǔ)言,因此,對(duì)于你所添加的每一個(gè)菜單,你都還必須編寫(xiě)一個(gè)函數(shù)。為保持這個(gè)工程的可控性,我提供了四個(gè)函數(shù):創(chuàng)建一個(gè)新的文件,打開(kāi)一個(gè)現(xiàn)有的文件,保存文本到一個(gè)文件,和關(guān)閉應(yīng)用程序。
在大多數(shù)流行的工具箱中,創(chuàng)建一個(gè)菜單的過(guò)程基本相同。首先,你創(chuàng)建菜單條本身,然后創(chuàng)建一個(gè)頂級(jí)菜單(例如 “File” ),再然后創(chuàng)建子菜單項(xiàng)(例如,“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);
-
}
現(xiàn)在,所有剩余的工作是實(shí)施菜單項(xiàng)所描述的功能。
編程菜單動(dòng)作
你的應(yīng)用程序響應(yīng)菜單選擇,是因?yàn)槟愕?nbsp;JFrame
有一個(gè)附屬于它的 ActionListener
。在 Java 中,當(dāng)你實(shí)施一個(gè)事件處理程序時(shí),你必須“重寫(xiě)”其內(nèi)建的函數(shù)。這只是聽(tīng)起來(lái)可怕。你不是在重寫(xiě) Java;你只是在實(shí)現(xiàn)已經(jīng)被定義但尚未實(shí)施事件處理程序的函數(shù)。
在這種情況下,你必須重寫(xiě) actionPerformed
方法。因?yàn)樵?“File” 菜單中的所有條目都與處理文件有關(guān),所以在我的代碼中很早就定義了一個(gè) JFileChooser
。代碼其它部分被劃分到一個(gè) if
語(yǔ)句的子語(yǔ)句中,這起來(lái)像接收到什么事件就相應(yīng)地執(zhí)行什么動(dòng)作。每個(gè)子語(yǔ)句都與其它的子語(yǔ)句完全不同,因?yàn)槊總€(gè)項(xiàng)目都標(biāo)示著一些完全唯一的東西。最相似的是 “Open” 和 “Save”,因?yàn)樗鼈兌际褂?nbsp;JFileChooser
選擇文件系統(tǒng)中的一個(gè)位置來(lái)獲取或放置數(shù)據(jù)。
“New” 菜單會(huì)在沒(méi)有警告的情況下清理 JTextArea ,“Quit” 菜單會(huì)在沒(méi)有警告的情況下關(guān)閉應(yīng)用程序。這兩個(gè) “功能” 都是不安全的,因此你應(yīng)該想對(duì)這段代碼進(jìn)行一點(diǎn)改善,這是一個(gè)很好的開(kāi)始。在內(nèi)容還沒(méi)有被保存前,一個(gè)友好的警告是任何一個(gè)好的文本編輯器都必不可少的一個(gè)功能,但是在這里為了簡(jiǎn)單,這是未來(lái)的一個(gè)功能。
-
@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); }
-
}
-
}
從技術(shù)上來(lái)說(shuō),這就是這個(gè)文本編輯器的全部。當(dāng)然,并沒(méi)有真正做什么,除此之外,在這里仍然有測(cè)試和打包步驟,因此仍然有很多時(shí)間來(lái)發(fā)現(xiàn)缺少的必需品。假設(shè)你沒(méi)有注意到提示:在這段代碼中 肯定 缺少一些東西。你現(xiàn)在知道缺少的是什么嗎?(在 猜謎游戲文章 中被大量的提到。)
測(cè)試
你現(xiàn)在可以測(cè)試你的應(yīng)用程序。從終端中啟動(dòng)你所編寫(xiě)的文本編輯器:
-
$ java ./src/TextEdit.java
-
error: can’t find main(String[]) method in class: com.example.textedit.TextEdit
它看起來(lái)像在代碼中沒(méi)有獲得 main
方法。這里有一些方法來(lái)修復(fù)這個(gè)問(wèn)題:你可以在 TextEdit.java
中創(chuàng)建一個(gè) main
方法,并讓它運(yùn)行一個(gè) TextEdit
類實(shí)例,或者你可以創(chuàng)建一個(gè)單獨(dú)的包含 main
方法的文件。兩種方法都可以工作,但從大型工程的預(yù)期來(lái)看,使用后者更為明智,因此,使用單獨(dú)的文件與其一起工作使之成為一個(gè)完整的應(yīng)用程序的方法是值得使用的。
在 src
中創(chuàng)建一個(gè) Main.java
文件,并在最喜歡的編輯器中打開(kāi):
-
package com.example.textedit;
-
public class Main {
-
public static void main(String[] args) {
-
TextEdit runner = new TextEdit();
-
}
-
}
你可以再次嘗試,但是現(xiàn)在有兩個(gè)相互依賴的文件要運(yùn)行,因此你必須編譯代碼。Java 使用 javac
編譯器,并且你可以使用 -d
選項(xiàng)來(lái)設(shè)置目標(biāo)目錄:
-
$ javac src/*java -d .
這會(huì)在你的軟件包名稱 com/example/textedit
后創(chuàng)建一個(gè)準(zhǔn)確地模型化的新的目錄結(jié)構(gòu)。這個(gè)新的類路徑包含文件 Main.class
和 TextEdit.class
,這兩個(gè)文件構(gòu)成了你的應(yīng)用程序。你可以使用 java
并通過(guò)引用你的 Main 類的位置和 名稱(非文件名稱)來(lái)運(yùn)行它們:
-
$ java info/slackermedia/textedit/Main`
你的文本編輯器打開(kāi)了,你可以在其中輸入文字,打開(kāi)文件,甚至保存你的工作。
帶有單個(gè)下拉菜單的白色文本編輯器框,有 File、New、Open、Save 和 Quit 菜單
以 Java 軟件包的形式分享你的工作
雖然一些程序員似乎看起來(lái)認(rèn)可以各種各樣的源文件的形式分發(fā)軟件包,并鼓勵(lì)其他人來(lái)學(xué)習(xí)如何運(yùn)行它,但是,Java 讓打包應(yīng)用程序變得真地很容易,以至其他人可以很容易的運(yùn)行它。你已經(jīng)有了必備的大部分結(jié)構(gòu)體,但是你仍然需要一些元數(shù)據(jù)到一個(gè) Manifest.txt
文件中:
-
$ echo "Manifest-Version: 1.0" > Manifest.txt
用于打包的 jar
命令,與 tar 命令非常相似,因此很多選項(xiàng)對(duì)你來(lái)說(shuō)可能會(huì)很熟悉。要?jiǎng)?chuàng)建一個(gè) JAR 文件:
-
$ jar cvfme TextEdit.jar
-
Manifest.txt
-
com.example.textedit.Main
-
com/example/textedit/*.class
根據(jù)命令的語(yǔ)法,你可以推測(cè)出它會(huì)創(chuàng)建一個(gè)新的名稱為 TextEdit.jar
的 JAR 文件,它所需要的清單數(shù)據(jù)位于 Manifest.txt
中。它的主類被定義為軟件包名稱的一個(gè)擴(kuò)展,并且類自身是 com/example/textedit/Main.class
。
你可以查看 JAR 文件的內(nèi)容:
-
$ 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
如果你想看看你的元數(shù)據(jù)是如何被集成到 MANIFEST.MF
文件中的,你甚至可以使用 xvf
選項(xiàng)來(lái)提取它。
使用 java
命令來(lái)運(yùn)行你的 JAR 文件:
-
$ java -jar TextEdit.jar
你甚至可以 創(chuàng)建一個(gè)桌面文件 ,這樣,在單擊應(yīng)用程序菜單中的圖標(biāo)時(shí),應(yīng)用程序就會(huì)啟動(dòng)。
改進(jìn)它
在當(dāng)前狀態(tài)下,這是一個(gè)非?;镜奈谋揪庉嬈?,最適合做快速筆記或簡(jiǎn)短自述文檔。一些改進(jìn)(比如添加垂直滾動(dòng)條)只要稍加研究就能快速簡(jiǎn)單地完成,而另一些改進(jìn)(比如實(shí)現(xiàn)一個(gè)廣泛的偏好系統(tǒng))則需要真正的工作。
但如果你一直在想學(xué)一種新的語(yǔ)言,這可能是一個(gè)完美的自我學(xué)習(xí)實(shí)用工程。創(chuàng)建一個(gè)文本編輯器,如你所見(jiàn),它在代碼方面并不難對(duì)付,它在一定范圍是可控的。如果你經(jīng)常使用文本編輯器,那么編寫(xiě)你自己的文本編輯器可能會(huì)使你滿意和樂(lè)趣。因此打開(kāi)你最喜歡的文本編輯器(你寫(xiě)的那個(gè)),開(kāi)始添加功能吧!
原文地址:https://linux.cn/article-13038-1.html