javaSE綜合例項之記事本詳細解析--長篇(初學者)
第一次釋出:2020年12月26日
如有更新,將在更新筆記專欄記錄,歡迎學習交流
文章目錄:
1.記事本主介面
2.記事本主介面各版塊設計
3.檔案選單各項功能實現
4.編輯選單各項功能實現
5.格式選單各項功能實現
6.檢視選單各項功能實現
7.幫助選單各項功能實現
8.核心原始碼
9.完整原始碼連結
專案結構圖:
目前實現功能演示視訊:
尚未解決的問題:
(1)生成exe程式後,該如何讀取使用該程式開啟的檔案
(2)如何解決系統預設的剪下、複製、貼上、刪除等與程式內部設計的命令產生衝突(比如在程式裡貼上快捷鍵也是設定:Ctrl +V,當使用時,會貼上兩遍。解決思路,估計是有兩個剪貼簿)
(3)通用元件是否可以合併使用,類似前端頁面的共同部分,尚未優化
(4)列印、頁面設定的實際功能尚未實現,如何呼叫系統本身的程式尚未掌握
1.建立一個Note.java的主程式:通過下列程式碼就實現了視窗的建立,接下來就是各種選單及其功能的新增。
package com.study.main;
import javax.swing.*;
/**
* 第1步:建立主程式,繼承JFrame
* JFrame是實現視窗需要
*/
public class Note extends JFrame{
/**
* main方法程式開始執行的地方
* @param args
*/
public static void main(String[] args) {
new Note();//無參構造方法建立匿名物件
}
/**
* 無參構造方法
*/
public Note(){
this.initJFrame();
}
private void initJFrame() {
this.setTitle("無標題--記事本");
this.setSize(1200,1000);
this.setLocation(200,200);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
}
2.整體佈局的設想
1.整個佈局是採用BorderLayout佈局,分為北、中、南三部分
(1)北邊是工具選單皮膚
(2)中間是文字域部分
(3)南邊是狀態列顯示部分
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2.因此:步驟1中的程式碼需要做以下修改
(1)在initJFrame()方法下的第一行新增容器的佈局
this.setLayout(new BorderLayout());//設定容器佈局
(2)在src目錄下建立包:com.study.myPanel。包下建立三個類
1)工具欄皮膚類:ToolBarPanel,繼承JPanel
2)狀態列皮膚類:StatementPanel,繼承JPanel
3)文字域類:MyTextArea,繼承textArea
==================================================================================================================================================================================
(3)在Note.java類下宣告(2)中三個類以及StatePanel、JScrollPanel、JTextArea類,同時建立初始化皮膚的方法
1)Note.java類下的宣告:
private ToolBarPanel toolBarPanel;//工具條皮膚
private JScrollPane jScrollPane;//文字域皮膚
public MyTextArea textArea;//文字域
private JPanel Statement;//狀態列皮膚
private StatementPanel statementPanel;//狀態列具體內容皮膚
2)建立的方法
/**
* 新增選單項到容器中
*/
public void initMenu(){
//檔案、編輯、格式、檢視、幫助選單的具體內容
}
/**
* 初始化工具條選單皮膚
*/
private void initToolBarPanel(){
toolBarPanel=new ToolBarPanel();
}
/**
* 初始化具有滾動條的皮膚:用於文字域
*/
private void initJScrollPane(){
textArea=new MyTextArea();
jScrollPane=new JScrollPane(textArea);
}
/**
* 初始化狀態列皮膚
*/
private void initStatePanel(){
statePanel=new JPanel();//這個皮膚是為了裝狀態列皮膚
statePanel.setLayout(new FlowLayout(FlowLayout.RIGHT));//使用靠右對齊的流式佈局
statePanel.setSize(this.getWidth(),35);
JLabel jbl=new JLabel();
jbl.setSize(this.getWidth(),35);//建立這個標籤是為當隱藏狀態列具體內容時狀態列皮膚的高度不至於發生太大的變化
//狀態列的具體內容
statementPanel=new StatementPanel();//包含具體內容的皮膚
//把相關元件新增到狀態列皮膚中
statePanel.add(jbl);
statePanel.add(statementPanel);
}
==================================================================================================================================================================================
(4)在initJFrame()方法下的第二行開始依次新增皮膚到容器指定位置
this.initMenu();
this.initToolBarPanel();
this.initJScrollPane();
this.initStatePanel();
this.add(toolBarPanel,BorderLayout.NORTH);
this.add(jScrollPane,BorderLayout.CENTER);
this.add(statePanel,BorderLayout.SOUTH);
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3.將上述的初始化類以及相關方法新增完成
(1)initMenu()方法,使用到的元件如下,依次完成對應建立新增到容器(this)即可
//選單皮膚:新增到容器的方法是:this.setJMenuBar(menuBar);
JMenuBar menuBar;
JMenu file;//檔案選單
//依次是:新建、新視窗、開啟、儲存、另存為、頁面設定、列印、退出
JMenuItem newFile, newWindow, openFile, saveFile, anotherSaveFile, settingPage, printFile, exit;
JMenu edit;//編輯選單
//依次是:撤銷、剪下、複製、貼上、刪除、使用Bing查詢、查詢、查詢下一個、查詢上一個、替換、轉到、全選、時間/日期
JMenuItem revoke, cut, copy, paste, del, useBing, search, searchNext, searchPrevious, replace, turn, selectAll, datetime;
JMenu format;//格式選單
JCheckBoxMenuItem turnLine;//自動換行
JMenuItem fonts;//字型
//檢視選單
JMenu view, zoom;//檢視、縮放
JCheckBoxMenuItem state;//狀態列
JMenuItem enlarge, narrow, restore;//加大、縮小、恢復
JMenu help;//幫助選單
JMenuItem helps, sendMs, aboutNote;//檢視幫助、傳送反饋、關於記事本
==================================================================================================================================================================================
(2)initToolBarPanel()方法,完成該方法,完成ToolBarPanel類建立即可,ToolBarPanel類簡單如下:
/**
* 工具條皮膚的具體內容
*/
public class ToolBarPanel extends JPanel {
//工具條管理選單
JToolBar toolBar;
//選單條管理工具,依次是:新建、開啟、儲存、剪下、複製、貼上
JButton bt_newFile, bt_openFile, bt_saveFile, bt_cut, bt_copy, bt_paste;
/**
* 建立無參構造方法
*/
public ToolBarPanel(){
this.setLayout(new FlowLayout(FlowLayout.LEFT));//設定佈局為靠左對齊的流式佈局
this.initComponent();
}
/**
* 實現各種元件的建立新增工作
*/
private void initComponent() {
toolBar=new JToolBar();
//依次建立新增即可,示例一個如下
bt_newFile = new JButton(new ImageIcon("image/xj.jpg"));//建立具有影像填充的單擊按鈕
bt_newFile.setToolTipText("新建");//提示文字,當滑鼠放上去時顯示的文字
toolBar.add(bt_newFile);//新增到皮膚中
//其它元件。。。。。。
//最後新增到皮膚中
this.add(toolBar);
}
}
==================================================================================================================================================================================
(3)initJScrollPane()方法,還剩MyTextArea類未完成
public class MyTextArea extends JTextArea {
public MyTextArea(){
//其它設定
}
}
==================================================================================================================================================================================
(4)initStatePanel()方法,還剩StatementPanel類未完成
public class StatementPanel extends JPanel {
//狀態列標籤
private JLabel rowAndColumn, characterState, windows, encoding;
public StatementPanel(){
this.setLayout(new FlowLayout(FlowLayout.RIGHT));//皮膚佈局設定為靠右對齊的流式佈局
this.initComponent();
}
private void initComponent() {
//元件的建立略,新建新增即可,注意設定一定的空格
}
}
==================================================================================================================================================================================
4.至此,已經完成了所有基本元件的建立工作,效果如下圖
3.實現檔案選單的新增及其功能實現
AA.功能選單的實現,需要使用到監聽事件,因此首先需要給Note實現監聽事件的相關介面,這裡首先使用到的介面是:ActionListener及其方法。之後就是新增監聽了
1.實現介面及其方法
public class Note extends JFrame implements ActionListener {
......
@Override
public void actionPerformed(ActionEvent e) {
//實現監聽方法
}
}
2.新增監聽及其實現監聽方法
(1)Note.java下建立方法
public void initAddActionListener(){
//示例一個新增監聽
newFile.addActionListener(this);//實現新建檔案的監聽
//其它類似,不再重複
}
(2)在initJFrame()方法中,在建立完成元件之後,呼叫上述方法
this.initAddActionListener();
(3)在actionPerformed(ActionEvent e)()方法中,實現具體的監聽事件的呼叫
@Override
public void actionPerformed(ActionEvent e) {
//實現監聽方法,其它監聽類似
if(e.getSource==newFile)
//實現具體方法的呼叫,通常我們是建一個方法或一個類,然後實現具體的方法
}
==================================================================================================================================================================================
BB.以在同一個類中建立實現方法或建立實現類的方法為例,實現檔案選單的各個選項功能
1.新建:嘗試由易到難,逐步加深
1.1.新建檔案要求:
(1)文字域沒有內容:直接新建,同時建立新視窗,關閉舊視窗
(2)文字域有內容:先區分此時編輯的檔案是已經存在的檔案還是新建立的檔案
已經存在的檔案:內容是否有變化
1)有變化: 彈出選擇提示資訊:是否儲存,還是取消操作
根據選擇訊息執行:
儲存:直接執行儲存操作;
不儲存:執行(1)步驟
取消:沒有操作
2)沒有變化: 直接執行(1)步驟
新檔案:彈出選擇提示資訊:是否儲存,還是取消操作
1)儲存的操作:
<1>彈出儲存對話方塊,實現儲存,然後執行(1)步驟
2)不儲存的操作:直接執行(1)步驟
3)取消的操作:沒有操作
——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
1.2.逐步實現
(1)文字域沒有內容:直接新建,同時建立新視窗,關閉舊視窗
思路:01.新建則建立一個新的視窗物件,關閉則關閉一個就視窗物件。如何識別新舊?
思路:02.新舊識別可以通過其方法 isActive() 判斷,為true表示我們正在操作的視窗,反之則不是.當單個物件我們能很快知道當前視窗是哪個?但是如果我們開啟多個視窗呢?
思路:03.解決開啟多個視窗時,找到我們活動的那個視窗問題,可以使用一個list集合解決,如建立集合:List<Note> list=new ArrayList<>();使用了集合,又需要考慮一個問題:當關閉了物件,
我們需要移除這個物件,同時在它的位置新增新的物件,這樣集合中間就不會存在空值。那麼程式執行到哪裡實現哪個物件的刪除與新增呢,以確保新增與刪除的操作是幾乎同時進行的?
思路:04.根據程式執行過程可以找出,當新的視窗即將顯示可見之前進行刪除與新增的操作最合適。至於是哪個物件,遍歷集合,執行isActive()方法,誰為true就是誰。
思路:05.那麼如何把物件在集合的位置傳回去呢?通過構造一個有參構造方法即可,同時設定其為全域性變數,這樣不用在多個方法中傳參
(2)具體程式碼實現,我們需要把this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE):
1)我們需要把initJFrame()方法中
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);修改為:
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
2)建立實現方法actionCreateNewFile(),方法actionCreateNewFile()的具體內容:
<1>實現思路1如下:
this.dispose();//關閉視窗
new Note();//新建視窗
<2>實現思路2如下:
if(this.isActive()==true){//視窗當前活動狀態為真
this.dispose();
new Note();
}
<3>實現思路3如下:
aa.在Note.java下建立一個靜態的Note類集合:static List<Note> list=new ArrayList<>();
bb.在無參構造方法的第二行新增:list.add(this);表示通過無參構造建立物件,直接新增到集合末尾
cc.方法的具體內容
if(list !=null){//確保不為空的情況下執行
for(int i=0;i<list.size();i++){//遍歷物件集合
Note node=list.get(i);
if(node.isActive==true){//找到活動的視窗,即我們操作的視窗
node.dispose();
list.set(i,new Note());//指定位置新增物件
}
}
}
<4>實現思路4和5如下:
aa.把<3>中的方法做簡單修改:
把:node.dispose();和list.set(i,new Note());修改為
new Note(i);//呼叫有參構造方法建立新物件
break;//退出迴圈
bb.在Note中建立有參構造方法,同時建立一個全域性變數:int remark=-5;
public Note(int remark){
this.initJFrame();
}
aa.在initJFrame()方法的標題設定之前新增如下程式碼:
if(remark>=0){//用於表示是新建檔案時建立物件執行的程式碼
if(list !=null){//空值判斷
for (int i = 0; i < list.size(); i++) {//找到匹配值
if(i==remark){//實現方法
list.get(remark).dispose();//關閉指定視窗
list.set(remark,this);//新增新物件到集合指定位置 remark=-5;//還原預設值,不影響無參構造方法的使用
break;//退出迴圈
}
}
}
}
——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
(3)文字域有內容的實現步驟:
1)把(2)中做好的actionCreateNewFile()方法內容抽取出來稱為獨立的方法
public void createNewFile();
2)在actionCreateNewFile()方法中編寫方法體:
//1.判斷文字域是否有內容
if(textArea.getText() ==null || textArea.getText().equals("")){
this.createNewFile();//沒有內容直接新建
}else{
//2.有內容彈首先判斷是新檔案還是舊檔案
if(pathFile ==null){//全域性變數檔案是空,是新檔案
/*
Object[] options = {"儲存", "不儲存"};// 選擇訊息
int row = JOptionPane.showOptionDialog(null, "你是否要儲存檔案?", "記事本", JOptionPane.DEFAULT_OPTION,JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
if(row==0){//儲存
this.saveFileDialog();//開啟儲存檔案對話方塊
this.createNewFile();//執行建立方法
}else if(row==1){//不儲存
this.createNewFile();
}
*/
this.doActionAccordingToOptionMessage("new");
}else{//已經存在檔案
//判斷內容是否發生改變
if(fileContent.equals(textArea.getText())){//沒有發生改變
this.saveFiles(pathFile.getAbsolutePath());//執行直接儲存檔案的操作
}else{//彈出選擇對話方塊
/*
Object[] options = {"儲存", "不儲存"};// 選擇訊息
int row = JOptionPane.showOptionDialog(null, "你是否要儲存檔案?", "記事本", JOptionPane.DEFAULT_OPTION,JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
if(row==0){//儲存
this.saveFiles(pathFile.getAbsolutePath());//執行直接儲存檔案的操作
this.createNewFile();//執行建立方法
}else if(row==1){//不儲存
this.createNewFile();//執行建立方法
}
*/
this.doActionAccordingToOptionMessage("old");
}
}
}
3)在Note類下建立兩個全域性變數:
String fileContent="";//用於記錄開啟或已經儲存了的正在編輯的檔案內容
File pathFile=null;//用於記錄開啟或已經儲存了的正在編輯的檔案
4)建立saveFileDialog()方法:
//儲存檔案操作(引數是指明是第一次儲存,還是另存)
public void saveFileDialog(String title) {
JFileChooser chooser = new JFileChooser("C:\\Users\\shinelon\\Desktop");//預設開啟對話方塊顯示的位置
chooser.setDialogType(JFileChooser.SAVE_DIALOG);//對話方塊型別
chooser.setDialogTitle(title);//設定顯示的對話方塊標題
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);//顯示檔案和目錄的指令。
chooser.setSelectedFile(new File("新建文字文件.txt"));//預設儲存的檔名
chooser.showSaveDialog(null);//開啟對話方塊
chooser.setVisible(true);//顯示其可見
//獲取儲存檔案的絕對路徑和名稱
File selectedFile = chooser.getSelectedFile();//獲取儲存的檔案
if(!title.equals("新建儲存...")) {
this.updateParameters(File selectedFile);
}
this.saveFiles(savePath);//執行儲存檔案
}
5)建立saveFiles()方法:
//讀取檔案到指定位置
public void saveFiles(File selectedFile){
String savePath = selectedFile.getAbsolutePath();//獲取儲存檔案的
try {
PrintStream pl = new PrintStream(savePath);//列印流儲存的檔案位置
System.setOut(pl);//正確輸出列印流
System.out.println(textArea.getText());//輸入內容
pl.close();
} catch (IOException aa) {aa.printStackTrace();}
}
6)建立updateParameters(File selectedFile)方法:
updatTiltle="*"+selectedFile.getName();//更新未儲存標題
initTitle=selectedFile.getName();//更新已儲存標題
this.setTitle(initTitle);//更新標題
pathFile=selectedFile;//更新開啟的檔案
fileContent=textArea.getText();//更新開啟檔案的內容
7)發現 2)中訊息選擇部分的方法相似,我們可以拆分為兩個小方法,方便其它方法呼叫:
方法1:public int showOptionMessageByJOptionPane();
Object[] options = {"儲存", "不儲存"};// 選擇訊息
return JOptionPane.showOptionDialog(null, "你是否要儲存檔案?", "記事本", JOptionPane.DEFAULT_OPTION,JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
方法2:public void doActionAccordingToOptionMessage(String fileStyle);
int row=this.showOptionMessageByJOptionPane();
if(row==0){//儲存
if(fileStyle.equals("new"))
this.saveFileDialog("儲存...");//開啟儲存檔案對話方塊
else
this.saveFiles(pathFile.getAbsolutePath());//執行直接儲存檔案的操作
this.createNewFile();//執行建立方法
}else if(row==1){//不儲存
this.createNewFile();
}
——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
執行測試發現新建檔案存在問題:
(1)當文件有內容時,新建選擇不儲存並沒有新建視窗,也沒有關閉原來的視窗
解決方法:在Note類下建立一個全域性的靜態變數整數activeFrame,用於記錄當前活動視窗在集合中的位置,預設值為0,只有一個物件的情況下。
1)Note類下建立靜態成員變數:static int activeFrame=0;
2)修改createNewFile()方法:
if(list !=null){//確保不為空的情況下執行
for(int i=0;i<list.size();i++){//遍歷物件集合
if(i==activeFrame){//找到活動的視窗,即我們操作的視窗
new Note(i);
break;
}
}
}
3)修改initJFrame()方法中, if(remark>=0){},在break;退出迴圈前加上:activeFrame=i;
(2)新建的視窗的位置總是在固定的地方,大小也是固定的。
想要改變位置和大小,可以將initJFrame()方法中,this的設定部分做如下修改:
int width=1200,height=1000;//窗體尺寸大小
int x=200,y=200;//螢幕上顯示位置的初始值
if(remark >=0){
if(list !=null){
for (int i = 0; i < list.size(); i++) {
if(i==remark){
width=list.get(i).getWidth();//即將關閉活動窗體的寬
height=list.get(i).getHeight();//即將關閉活動窗體的高
x=list.get(i).getX();//即將關閉活動窗體與螢幕左邊的距離
y=list.get(i).getY();//即將關閉活動窗體與螢幕頂部的距離
list.get(remark).dispose();//關閉活動窗體
list.set(remark,this);//建立新窗體並新增到指定位置
activeFrame=i;//記錄當前活動窗體的下標
remark=-5;//還原初始值
break;
}
}
}
}
this.setTitle("無標題--記事本");
this.setSize(width,height);
this.setLocation(x,y);
this.setVisible(true);
思考01:為了不用到處使用activeFrame進行更新標記,使用執行緒來監控活動窗體
1)Note類實現Runnable介面,並且實現重寫其方法
2)程式碼編寫如下:
@Override
public void run() {
while (true){
try {
if(list !=null){
for (int i = 0; i < list.size(); i++) {
if(list.get(i).isActive()==true){
activeFrame=i;
}
}
}
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3)在initJFrame()方法最後啟用執行緒即可:new Thread(this).start();
4)刪除其它方法給activeFrame進行更新
思考02:儲存檔案過程中,如果選擇的儲存檔名在儲存目錄下已經存在,應該給予提示資訊,是否替換原來的檔案
解決方法:遍歷該資料夾下的所有同類格式的檔案,然後遍歷查詢是否有完全一樣的檔案,存在彈出詢問是否替換操作,不存在則直接執行下一步操作。
問題關鍵:如何在
具體程式碼:
(1)建立一個方法:public boolean existSameFile(File selectFile)
String name = selectFile.getName();
File parentFile = selectFile.getParentFile();
File[] files = parentFile.listFiles();
if (files != null) {
for (File file1 : files) {
if(name.equals(file1.getName()))
return true;
}
}
return false;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
(2)建立如下方法以及實現,判斷選擇的檔案是否已經存在:
private boolean replaceExistSameFile(File selectFile){
Object[] options = {"是", "否"};// 選擇訊息
int row=JOptionPane.showOptionDialog(null, selectFile.getName()+" 已存在。\n要替換它嗎?", "確認另存為", JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[1]);
return row ==0 ? true : false;
}
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
(3)修改saveFileDialog(String title)方法
//1.開啟儲存對話方塊
JFileChooser chooser = new JFileChooser("C:\\Users\\shinelon\\Desktop");//預設開啟對話方塊顯示的位置
chooser.setDialogType(JFileChooser.SAVE_DIALOG);//對話方塊型別
chooser.setDialogTitle(title);//設定顯示的對話方塊標題
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);//顯示檔案和目錄的指令。
chooser.setSelectedFile(new File("新建文字文件.txt"));//預設儲存的檔名
chooser.showSaveDialog(null);//開啟對話方塊
chooser.setVisible(true);//顯示其可見
System.out.println(JFileChooser.APPROVE_OPTION);
//2.獲取儲存檔案進行重名判斷
File selectedFile = chooser.getSelectedFile();//獲取儲存的檔案
boolean exist=this.existSameFile(selectedFile);//判斷選擇的檔案是否已經存在
while(exist){
if(this.replaceExistSameFile(selectedFile)){//詢問是否替代?true為替換,false不替換
break;
}else{
chooser = new JFileChooser("C:\\Users\\shinelon\\Desktop");//預設開啟對話方塊顯示的位置
chooser.setDialogType(JFileChooser.SAVE_DIALOG);//對話方塊型別
chooser.setDialogTitle(title);//設定顯示的對話方塊標題
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);//顯示檔案和目錄的指令。
chooser.setSelectedFile(new File("新建文字文件.txt"));//預設儲存的檔名
chooser.showSaveDialog(null);//開啟對話方塊
chooser.setVisible(true);//顯示其可見
selectedFile = chooser.getSelectedFile();//獲取儲存的檔案
exist=this.existSameFile(selectedFile);
}
}
//3.執行儲存檔案
this.saveFiles(selectedFile);//執行儲存檔案
//4.更新相關引數(只有在儲存和另存為時更新)
if (!title.equals("新建儲存...")) {
this.updateParameters(selectedFile);
}
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
新建檔案的基本工作已經完成。當然還有需要思考的地方,如:當生存.exe程式之後,如果已經建好的檔案用它開啟,
這時它該如何讀取開啟檔案的相關資訊,這個待學習瞭解,將單獨寫在其它文章中。屆時更新再新增連結。
==================================================================================================================================================================================
注:從這個功能開始,如果有重複呼叫方法,將直接給出方法名
2.新視窗:直接呼叫actionOpenNewWindows()方法,新建視窗呼叫無參構造即可,同時新增到list集合中,方法的具體內容如下:
list.add(new Note());
實驗發現,沒有起作用,儘管建立了新物件,但是會覆蓋之前的視窗,但是根據上面1中的問題解決方法,可以得到想要的結果。修改如下:
list.add(new Note(-1));
//activeFrame=list.size()-1;//該行使用執行緒之後就可省略了
問題:視窗顯示位置和大小一直是初始值
解決:只需修改一下上面1中問題2即可解決
int width=1200,height=1000;//窗體尺寸大小
int x=200,y=200;//螢幕上顯示位置的初始值
if(list !=null){
if(remark>=0){//建立新建檔案執行
for (int i = 0; i < list.size(); i++) {
if(i==remark){
width=list.get(i).getWidth();//即將關閉活動窗體的寬
height=list.get(i).getHeight();//即將關閉活動窗體的高
x=list.get(i).getX();//即將關閉活動窗體與螢幕左邊的距離
y=list.get(i).getY();//即將關閉活動窗體與螢幕頂部的距離
list.get(remark).dispose();//關閉活動窗體
list.set(remark,this);//建立新窗體並新增到指定位置
remark=-5;//記錄當前活動窗體的下標
break;
}
}
}else if(remark==-1){//建立新視窗執行
width = list.get(activeFrame).getWidth();//即將關閉活動窗體的寬
height = list.get(activeFrame).getHeight();//即將關閉活動窗體的高
x = list.get(activeFrame).getX();//即將關閉活動窗體與螢幕左邊的距離
y = list.get(activeFrame).getY();//即將關閉活動窗體與螢幕頂部的距離
}
}
this.setTitle("無標題--記事本");
this.setSize(width,height);
this.setLocation(x,y);
this.setVisible(true);
new Thread(this).start();
——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
思考:既然使用了執行緒進行活動視窗的監聽,而且建立新視窗可以直接用執行緒監聽的變數解決找到位置的問題,那麼上面的程式碼還可以進行優化:
(1)createNewFile()方法只需要一句話:new Note(activeFrame);可以用這句話替代所有呼叫該方法的地方。然後去除該方法。
(2)上述initJFrame()獲取視窗的位置資訊程式碼可以優化如下,這樣簡略了許多:
if (list != null&&remark !=-5) {//表示不是第一次啟動程式
//除第一次外,建立新檔案和新視窗都獲取活動視窗的位置資訊
width = list.get(activeFrame).getWidth();//即將關閉活動窗體的寬
height = list.get(activeFrame).getHeight();//即將關閉活動窗體的高
x = list.get(activeFrame).getX();//即將關閉活動窗體與螢幕左邊的距離
y = list.get(activeFrame).getY();//即將關閉活動窗體與螢幕頂部的距離
if (remark >= 0) {//建立新檔案開啟的視窗,需要移除關閉舊視窗和新增新視窗
list.get(activeFrame).dispose();//關閉活動窗體
list.set(activeFrame, this);//建立新窗體並新增到指定位置
remark = -5;//記錄當前活動窗體的下標
}
}
==================================================================================================================================================================================
3.開啟(注:這裡是選擇單檔案開啟)
3.1.思路分析:
(1)開啟檔案是在程式已經啟動的情況下進行的,開啟檔案首先是彈出選擇檔案的對話方塊
(2)獲取有效的開啟檔案,進行檔案的讀取,把檔案內容讀取到記事本的文字域中
(3)最後需要對相關變數進行更新:如檔案、標題、儲存內容等
3.2.程式碼實現,actionOpenFile()方法:
(1)彈出選擇檔案的對話方塊
//1.開啟對話方塊
JFileChooser chooser = new JFileChooser("C:\\Users\\shinelon\\Desktop");//預設開啟顯示的位置
chooser.setDialogTitle("選擇開啟檔案...");
chooser.setDialogType(JFileChooser.OPEN_DIALOG);//對話方塊型別
chooser.setSelectedFile(new File(".txt"));//只顯示字尾為“.txt”的檔案
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);//顯示檔案和目錄的指令。
chooser.showOpenDialog(this);//開啟對話方塊
chooser.setVisible(true);
//2.獲取選擇的檔案
File selectedFile = chooser.getSelectedFile();
if(selectedFile !=null){//執行(2)(3)}//需要判斷是否已經選擇了檔案
(2)把文字資訊讀取到文字域中
//3.讀取檔案到文字域
this.readFile(selectedFile);
(3)最後需要對相關變數進行更新:如檔案、標題、儲存內容等
//4.更新相關引數
this.updateParameters(selectedFile);
==================================================================================================================================================================================
4.儲存
4.1.思路分析:
(1)儲存檔案分新檔案和已經建立了開啟的檔案
(2)新檔案:執行彈出儲存對話方塊,執行儲存檔案的操作,同時更新相關變數
(3)已有的檔案:則直接執行儲存檔案的操作,同時更新相關變數
4.2.程式碼實現,方法actionSaveFile():
//1.執行判斷
if(pathFile !=null){//舊檔案
if(!fileContent.equals(textArea.getText())){//只在內容發生變化時執行
this.saveFiles(pathFile);//直接執行儲存檔案的方法
this.updateParameters(pathFile);//直接執行儲存檔案的方法
}
}else{//新檔案
this.saveFileDialog("儲存...");//執行儲存檔案對話方塊
}
//2.更新相關資訊
注:在this.saveFileDialog("儲存...");中已經實現
==================================================================================================================================================================================
5.另存為
5.1.思路分析:
(1)該操作執行和儲存新檔案一樣,僅是顯示標題和更新引數不一樣
5.2.程式碼實現,建立方法actionSaveFileNewPosition():
this.saveFileDialog("另存為...");
補充說明:實現標題的更換:
線上程方法的執行中,在Thread.sleep(10);的上一行加上以下程式碼即可實現
if(fileContent.equals(textArea.getText()))
this.setTitle(initTitle);
else
this.setTitle(updatTiltle);
==================================================================================================================================================================================
6.頁面設定(功能未實現,只做了一個介面,及介面裡面的操作)
記錄幾個最原始製作的思路:
(1)分層,主要是一層套著一層,使用了JTextFile來解決,包括哪些分層的標題
(2)橫向與縱向的變換,通過建立了水平和垂直的相關元件解決
(3)左右上下距離的變化,顯示內容裡面是一個文字域,通過改變文字域的位置和大小解決
(4)變換數值,設定了最小和最大值,以及不合要求時顯示的位置和大小,使用執行緒監聽變化
==================================================================================================================================================================================
7.列印設定(功能未實現,只做了一個介面)
基本是隻做了基本的介面顯示,尚未完成的功能待學習瞭解之後更新。
==================================================================================================================================================================================
8.退出程式
思路: (1)退出時判斷文字域是否有內容
(2)有內容,判斷是新建檔案還是已經存在的檔案
1)新建的檔案:pathFile==null;詢問是否儲存
01)儲存則執行開啟儲存對話方塊,然後退出
02)不儲存直接退出
03)取消沒有任何操作
2)舊檔案:pathFile !=null;判斷內容是否有變化
01)有變化詢問是否儲存
001)儲存則執行儲存檔案,然後退出
002)不儲存直接退出
003)取消沒有任何操作
02)沒變化直接退出
(3)沒有內容直接退出
程式碼實現:
(1)private void actionExit()方法
if(textArea.getText() !=null && !textArea.getText().equals("")){//1.判斷是否有內容
//2.判斷新舊檔案
if(pathFile !=null){//舊檔案
//3.舊檔案判斷內容是否改變
if (!fileContent.equals(textArea)) {//改變
//儲存選擇訊息
if(this.readExitSystem("old"))
return;//結束方法的執行
}
}else{//新檔案
if(this.readExitSystem("new"))
return;//結束方法的執行
}
}
System.exit(0);
(2)readExitSystem(String exitStyle)方法
int i = this.showOptionMessageByJOptionPane();
if(i==0)//儲存
if(exitStyle.equals("new"))
this.saveFileDialog("儲存為...");
else
this.saveFiles(pathFile);
else if(i==2)//取消
return true;
return false;
==================================================================================================================================================================================
檔案選單暫時做到如此,有待更新再從這裡開始補充(以上2020-12-23,週三)
效果演示視訊:
記事本檔案選單演示
4.編輯選單的詳細過程
一、準備工作:
1.觀察編輯選單的各項可操作前提
(1)文字域從未發生過任何變化時,撤銷命令不可執行
(2)文字域沒有內容時:剪下、複製、刪除、查詢、查詢上一個、查詢下一個不可以操作
(3)文字域有內容時,如果沒有選擇內容,剪下、複製、刪除不可以操作
(4)轉到命令在自動換行被選中時,不可以操作
2.給文字域新增單擊監聽、滑鼠監聽、鍵盤監聽;即讓Note實現上述介面,然後可以新增監聽
3.文字域一開始有顯示垂直方向的滾動條
==================================================================================================================================================================================
二、準備工作的實現程式碼:
1.解決第1個問題的(2)(3)項(最後一項功能實現解析)通過使用執行緒時刻對文字域內容的變化即可實現
程式碼實現:在run()方法中的迴圈裡邊的Thread.sleep(10);的上面新增如下程式碼:
//查詢操作只在有內容的情況下可以使用
if (textArea.getText() == null || textArea.getText().equals("")) {
search.setEnabled(false);
searchNext.setEnabled(false);
searchPrevious.setEnabled(false);
} else {
search.setEnabled(true);
searchNext.setEnabled(true);
searchPrevious.setEnabled(true);
}
//剪下、複製、刪除只有在選中的情況可以操作
if (textArea.getSelectedText() == null || textArea.getSelectedText().equals("")) {
cut.setEnabled(false);
copy.setEnabled(false);
del.setEnabled(false);
} else {
cut.setEnabled(true);
copy.setEnabled(true);
del.setEnabled(true);
}
2.給文字域新增滑鼠監聽、鍵盤監聽;即讓Note實現上述介面,然後可以新增監聽
在addListener()方法中新增文字域的上述監聽:
textArea.addMouseListener(this);
textArea.addKeyListener(this);
3.文字域一開始有顯示垂直方向的滾動條,在initJScrollPane()方法中滾動皮膚物件建立之後新增程式碼: jScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
==================================================================================================================================================================================
三、編輯選單功能分析與程式碼:
1.撤銷
1.1.之前不可操作功能未實現,它的實現應該是在initMenu()方法中建立之後設定為:
revoke.setEnable(false);
1.2.解決它何時可用,解決方法如下:
思路:只要文字域內容一有變化,該功能就可以永遠實現,直至程式退出。使用執行緒監聽
當if!textArea.getText().equals("")&&textArea.getText() !=null時設定其可用:revoke.setEnabled(true);
通過使用集合revokeList來建立初始的5個元素,使用構造程式碼塊初始化
{
revokeList.add("");//儲存原內容,點選撤銷第一次的內容
revokeList.add("");//儲存變化了的內容,點選撤銷第二次回見
revokeList.add("00");//儲存執行撤銷的命令
revokeList.add("00");//儲存是否更新第二個元素的命令
revokeList.add("00");//儲存是否更新第一個元素的命令
}
具體實現:
(1)在Note型別建立全域性變數:List<String> revokeList=new ArrayList<>();
(2)撤銷內容的變化與剪下、貼上、刪除、輸入內容有關。其中
剪下和刪除視為同一種操作,都是使內容變少,用“11”表示
貼上視為一種操作,輸入內容也視為單獨一種操作,分別用“22”和“33”表示
輸入內容記錄輸入前和停止輸入時的變化。
(3)建立方法void revokeContentChange();str是執行操作標誌
revokeList.set(0,textArea.getText());
revokeList.set(2,"88");//表示奇數次點選撤銷命令執行的識別符號
(4)建立執行撤銷命令方法void actionRevoke();
//剪下、刪除、貼上之後進入撤銷操作執行的命令
if(!revokeList.get(3).equals("88")&&(revokeList.get(4).equals("11")||revokeList.get(4).equals("22")||revokeList.get(4).equals("66"))) {
revokeList.set(1,textArea.getText());
}
if(revokeList.get(2).equals("55")){//反覆執行顯示撤銷之前的內容
textArea.setText(revokeList.get(1));
revokeList.set(2,"88");
//顯示返回時撤銷回來部分的顏色
new ActionSearch(textArea).showSelectionTextColor(textArea.getCaretPosition()-revokeList.get(1).length()+revokeList.get(0).length(),textArea.getCaretPosition());
}else{//反覆執行顯示撤銷之後的內容
textArea.setText(revokeList.get(0));
revokeList.set(2,"55");
}
revokeList.set(3,"88");
==================================================================================================================================================================================
2.剪下
2.1.使用工具系統的剪貼簿,由於複製和貼上均需使用到,所以就作為全域性變數,在Note類下獲取:
// 獲取系統剪貼簿物件
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
2.2.建立actionCut()方法,思路是把文字域選中的內容放到剪貼中,同時選中內容替換為"":
//0.撤銷內容更新判斷
if(!revokeList.get(4).equals("00")&&!revokeList.get(4).equals("11"))
this.revokeContentChange();
revokeList.set(3,"11");
revokeList.set(4,"11");
//1.獲取文字域選中的剪下內容
String selectedText = textArea.getSelectedText();
//2.將剪下內容放入轉換物件中
Transferable selection = new StringSelection(selectedText);
//3.文字域中將剪下部分用空字元替換
textArea.replaceRange("", textArea.getSelectionStart(), textArea.getSelectionEnd());
//4.將剪下內容轉換後的文字放置到剪貼簿中
clipboard.setContents(selection, null);
==================================================================================================================================================================================
3.複製
3.1.與剪貼相似,唯一區別就是不用把文字域內容替換 void actionCopy();
//1.獲取文字域選中的剪下內容
String selectedText = textArea.getSelectedText();
//2.將剪下內容放入轉換物件中
Transferable selection = new StringSelection(selectedText);
//3.將剪下內容轉換後的文字放置到剪貼簿中
clipboard.setContents(selection, null);
==================================================================================================================================================================================
4.貼上
4.1.執行方法void actionPaste();
//0.撤銷內容更新判斷
if(!revokeList.get(4).equals("00")&&!revokeList.get(4).equals("22"))
this.revokeContentChange();
revokeList.set(3,"22");
revokeList.set(4,"22");
//1.判斷剪貼簿中是否含有字串內容
if (clipboard.isDataFlavorAvailable(DataFlavor.stringFlavor)) {
try {
//2.獲取剪貼簿的文字內容
String data = (String) clipboard.getData(DataFlavor.stringFlavor);
//3.將文字選中內容替換成剪貼簿獲取的內容
textArea.replaceRange(data, textArea.getSelectionStart(), textArea.getSelectionEnd());
} catch (UnsupportedFlavorException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
==================================================================================================================================================================================
5.刪除
5.1.執行void actionDelete();把選中內容替換為""即可
//0.撤銷內容更新判斷
if(!revokeList.get(4).equals("00")&&!revokeList.get(4).equals("11"))
this.revokeContentChange();
revokeList.set(3,"11");
revokeList.set(4,"11");
//1.文字域中將刪除部分用空字元替換
textArea.replaceRange("", textArea.getSelectionStart(), textArea.getSelectionEnd());
==================================================================================================================================================================================
6.使用Bing搜尋,這個參考其它資料
6.1.執行方法void actionSearchByBing();
String url = "https://cn.bing.com/search";//搜尋的網址
this.openBrowser(url);//呼叫執行的方法
6.2.參考方法如下void openBrowser(String url):
try {
// 獲取作業系統的名字
String osName = System.getProperty("os.name", "");
if (osName.startsWith("Mac OS")) {
// 蘋果的開啟方式
Class<?> fileMgr = Class.forName("com.apple.eio.FileManager");
Method openURL = fileMgr.getDeclaredMethod("openURL", new Class[]{String.class});
openURL.invoke(null, new Object[]{url});
} else if (osName.startsWith("Windows")) {
// windows的開啟方式。
Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url);
} else {
// Unix or Linux的開啟方式
String[] browsers = {"firefox", "opera", "konqueror", "epiphany", "mozilla", "netscape"};
String browser = null;
for (int count = 0; count < browsers.length && browser == null; count++)
// 執行程式碼,在brower有值後跳出,
// 這裡是如果程式建立成功了,==0是表示正常結束。
if (Runtime.getRuntime().exec(new String[]{"which", browsers[count]}).waitFor() == 0) {
browser = browsers[count];
}
if (browser == null) {
throw new Exception("Could not find web browser");
} else {
// 這個值在上面已經成功的得到了一個程式。
Runtime.getRuntime().exec(new String[]{browser, url});
}
}
} catch (Exception e) {
e.printStackTrace();
}
==================================================================================================================================================================================
7.查詢(單獨建立一個類,繼承JDialog,實現ActionListener介面)
查詢類的基本結構Search.java:
public class Search extends JDialog implements ActionListener{
//查詢內容標籤與文字域
JLabel bq_content;
JTextField txf1;
//方向框設定
JLabel bq_up, bq_down;
JTextField txf2;
//分別表示查詢方向:向上和向下
JRadioButton rbtUp, rbtDown;
ButtonGroup rbg;
//查詢下一個和取消按鈕
JButton bt_searchNext, bt_Cancle;
//核取方塊,分別表示:區分大小寫、迴圈
JCheckBox jckCase, jckRecycle;
String searchText = "";//查詢內容
String replaceText = "";//替換內容
private DealTextArea dealTextArea;//含向上或向下查詢方法類
private JTextArea textArea;//文字域
//初始化查詢內容
{
String[] strings = ReadData.readDatas();//讀取初始查詢資料
searchText = strings[0];
replaceText = strings[1];
}
public Search(JTextArea textArea) {
//省略
}
public void initialComponent() {
//省略
}
public void addActionListener() {
//省略
}
public void initialDialog() {
//省略
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == bt_searchNext) {
//省略
} else if (e.getSource() == bt_Cancle) {
//省略
}
}
//text是查詢的內容,position是游標所在位置
public int doSearching(String text, int position) {
//省略
}
}
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
7.1.要求:
(1)區分向上和向下查詢
(2)進入查詢時,查詢內容自動填充上次查詢的文字內容
(3)區分大小寫查詢,預設不區分大小寫
(4)可以迴圈查詢
7.2.解決由簡入繁:
(1)區分向上和向下查詢
思路01:向上查詢,以游標所在位置開始,向前選擇查詢文字的長度,所得對比物件
如果和查詢內容一致,將其用藍色背景顯示。當游標所在的位置大於等於查詢文字時,進行查詢,否則查詢結束。
思路02:向下查詢,以游標所在位置開始,向後選擇查詢文字的長度,所得對比物件
如果和查詢內容一致,將其用藍色背景顯示。當游標所在的位置小於等於文字域長度-查詢文字時,進行查詢,否則查詢結束。
01.public void lookSameTextToUp();非迴圈向上查詢程式碼:
//1.獲取查詢內容及其長度
String searchText=txf1.getText();
int searchLen=searchText.length();
//2.獲取游標位置
int position=textArea.getCaretPosition();
//3.開始查詢
for(int i=position;i>=position-searchLen;i--){
//4.獲取查詢內容
textArea.setSelectionStart(i - searchLen);
textArea.setSelectionEnd(i);
String selectedText=textArea.getSelectedText();
//5.進行比較
if(searchText.equals(selectedText)){
textArea.setSelectionColor(Color.blue);//給找到的內容用藍色顯示出來
textArea.setCaretPosition(i - searchLen);//游標往前設定
}
}
小結:以上執行並不能得到想要的結果:每點選一次,往前找,如果有相同的,內容背景顯示藍色,點選下一次,再查詢下一個。如果查詢完了,給出提示:“已經到盡頭了,沒有查詢的內容了”
解決:
(1)迴圈的執行應該只是網上查詢,找到即結束返回查詢到的位置,在呼叫方法處設定其找到的內容背景顏色,如果在迴圈顯示,方法一結束,則無法再顯示了。
(2)游標位置、內容獲取,均可通過呼叫方法時再獲取
綜上,上面的方法應該進行改造
001.方法void lookSameTextToUp()改為:
int lookSameTextToUp(String searchText,int position);
內容如下:
//1.游標移位
position--;
//說明:比如單行:“得到得”。游標放在最後,位置是4,向上找到第一個,選中部分為最後一個“得”,此時游標位置還是4,
//如果不向前一位,那麼永遠只找到最後一個就結束。所以需減1
//2.獲取查詢內容及其長度
int searchLen=searchText.length();
//3.開始查詢
for(int i=position;i>=searchLen;i--){
//4.獲取查詢內容
//textArea.setSelectionStart(i - searchLen);
//textArea.setSelectionEnd(i);
//String selectedText=textArea.getSelectedText();
String selectedText=this.getSelectedText(i-searchLen,i);
//5.進行比較
if(searchText.equals(selectedText)){
return i-searchLen;//找到返回的選中內容開始部分
}
}
return -1; //沒找到返回-1
002.監聽事件查詢下一個執行如下內容:
//1.獲取查詢內容
String searchText = txf1.getText();
//2.獲取游標位置
int position = textArea.getCaretPosition();
//3.執行查詢
this.doSearching(searchText,position);
003.方法void doSearching(String searchText,int position):進行查詢方向的確定
int row=-1;
//3.查詢方向
if(rbtUp.isSelected()==true)//向上查詢
row=this.lookSameTextToUp(searchText,position);
else//向下查詢
row=this.lookSameTextToDown(searchText,position);
//4.執行查詢結果
this.showSelectionTextColor(row,row+searchText.length());
004.方法String getSelectedText(int start,int end)對001優化:該方法獲取選中內容
textArea.setSelectionStart(start);
textArea.setSelectionEnd(end);
return textArea.getSelectedText();
005.方法void showSelectionTextColor(int start,int end)優化:該方法顯示查詢結果
if(start !=-1){//找到
//設定選中內容及其背景色
textArea.setSelectionStart(start);
textArea.setSelectionEnd(end);
textArea.setSelectionColor(Color.blue);
}else{//沒找到
//設定文字域選中部分為文字域自身背景色,以及提示訊息
textArea.setSelectionColor(textArea.getBackground());
JOptionPane.showMessageDialog(null,"已經到盡頭了,沒有查詢的內容了");
}
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
02.public void lookSameTextToDown();非迴圈向下查詢程式碼,這裡和向上類似,直接列出優化的程式碼:
001.int lookSameTextToDown(String searchText,int position)方法向下查詢
//1.獲取查詢內容及其長度
int searchLen=searchText.length();
//2.開始查詢
for(int i=position;i<=textArea.getText().length()-searchLen;i++){
//3.獲取查詢內容
String selectedText=this.getSelectedText(i-searchLen,i);
//4.進行比較
if(searchText.equals(selectedText)){
return i-searchLen;//找到返回的選中內容開始部分
}
}
return -1; //沒找到返回-1
002.在001中呼叫的方法在01中均已實現
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
(2)進入查詢時,查詢內容自動填充上次查詢的文字內容
解決這個問題的思路就是:利用構造程式碼塊初始化查詢的內容
001.先在專案下建立一個文字資料夾files,資料夾下建立一個data.txt檔案
002.建立一個讀取和儲存檔案的類,裡面只有兩個靜態方法,一個讀取,一個儲存
003.類名:DataUtils.java
004.讀取檔案的方法:public static String[] readData();使用try()with{}結構,該結構可自動關閉流檔案
String[] str=new String[2];//陣列儲存查詢和替換內容
try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File("files/data.txt")), "UTF-8"))) {
str[0] = br.readLine();//論行讀取
str[1] = br.readLine();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return str;
005.儲存檔案的方法:public static void saveData(String searchText,String replaceText);使用try()with{}結構,該結構可自動關閉流檔案
try (BufferedWriter br = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File("files/data.properties")), "UTF-8"))) {
br.write(searchText+"\n");
br.write(replaceText);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
006.在Search類下建立兩個全域性變數String searchText="",replaceText="";
007.在Search類下建立構造程式碼塊
{
String[] datas=DataUtils.readData();
searchText=datas[0];
replaceText=datas[1];
}
008.在Search類下執行元件建立時,把查詢內容文字域的內容設定為searchText即可
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
(3)區分大小寫查詢,預設不區分大小寫
思考:區分大小寫應該在哪裡進行判斷比較好呢?需不需要單獨寫成一個方法?
001.在(1)中方法void doSearching(String searchText,int position)解決大小寫判斷最好
002.由於不少地方需要使用到,有必要單獨寫成一個方法: boolean isLowerCase();
if(jckCase.isSelected()==false){//不區分大小寫處理
return true;
return false;
003.在(1)中方法void doSearching(String searchText,int position)首先實現
if(this.isLowerCase())//不區分大小寫處理
searchText=searchText.toLowerCase();//統一小寫,也可以統一大寫)
004.在(1)中方法String getSelectedText(int start,int end)中實現文字域的統一小寫處理
if(this.isLowerCase())
return textArea.getSelectedText().toLowerCase();
return textArea.getSelectedText();
功能完成!
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
(4)可以迴圈查詢
分析:前面已經實現了按方向查詢,但是每個方向都有迴圈與非迴圈之分。
向上迴圈,每次找到最前面時,游標就設定到最後,從尾開始即可
向下迴圈,每次找到最後面時,游標就設定到最前,從頭開始即可
問題:迴圈查詢如果沒有怎麼辦?第幾遍判斷比較合適?
分析:至少全部過一遍才能確定,也就是至少從頭或從尾開始一輪才能決定
這樣,需要一個變數計算迴圈執行的次數,還要一個變數表示其是否找到過
實現:
001.建立一個方法獲取是否迴圈的布林值:boolean isReturnSearch();
if(jckRecycle.isSelected()==true)
return true;//迴圈
return false;//不迴圈
002.修改方法:void doSearching(String searchText,int position);步驟3
//3.查詢方向
if(rbtUp.isSelected()==true)//向上查詢
if(this.isReturnSearch())//迴圈查詢
row=this.lookSameTextToUpRecycle(searchText,position);
else
row=this.lookSameTextToUp(searchText,position);
else//向下查詢
if(this.isReturnSearch())//迴圈查詢
row=this.lookSameTextToDownRecycle(searchText,position);
else
row=this.lookSameTextToDown(searchText,position);
003.建立方法lookSameTextToUpRecycle(String searchText,int position);
if(position<=0)
position=textArea.getText().length();
int recycleTimes=0;//統計完整迴圈次數
boolean existSame=false;//假設沒找到
while(true){
for(int i=position;i>=searchLen;i--){
//4.獲取查詢內容
String selectedText=this.getSelectedText(i-searchLen,i);
//5.進行比較
if(searchText.equals(selectedText)){
return i-searchLen;//找到返回的選中內容開始部分
}
}
position=textArea.getText().length();
recycleTimes++;
if(recycleTimes>1){//第二遍開始進行是否存在相同內容判斷
if(existSame==false)//第二遍還沒找到,說明沒有,給出提示
return -1; //沒找到返回-1
}
}
004.建立方法lookSameTextToDownRecycle(String searchText,int position);
if(position>=textArea.getText().length())
position=0;
int recycleTimes=0;//統計完整迴圈次數
boolean existSame=false;//假設沒找到
while(true){
for(int i=position;i<=position-searchLen;i++){
//4.獲取查詢內容
String selectedText=this.getSelectedText(i,i+searchLen);
//5.進行比較
if(searchText.equals(selectedText)){
return i;//找到返回的選中內容開始部分
}
}
position=textArea.getText().length();
recycleTimes++;
if(recycleTimes>1){//第二遍開始進行是否存在相同內容判斷
if(existSame==false)//第二遍還沒找到,說明沒有,給出提示
return -1; //沒找到返回-1
}
}
觀察整理優化程式碼:for迴圈部分,還有while迴圈部分,很多相似,是否可以進行優化呢?優化整理如下:
(0)設定一個成員變數:int upTimes=0;用於檢測非迴圈向上查詢
(1)監聽事件呼叫方法
//1.獲取查詢內容
String searchText = txf1.getText();
//2.獲取游標位置
int position = textArea.getCaretPosition();
//3.執行查詢
this.doSearching(searchText,position);
(2)doSearching(String searchText,int position)方法執行:
//1.判斷是否區分大小寫
if(this.isLowerCase())//不區分大小寫
searchText=searchText.toLowerCase();
int row=-1;
//2.查詢方向與查詢方式
if(rbtUp.isSelected()==true)//向上查詢
if(this.isReturnSearch())//迴圈查詢
row=this.lookSameTextByRecycle("up",searchText,position);
else//非迴圈查詢
row=this.lookSameTextToUp("normal",searchText,position);
else//向下查詢
if(this.isReturnSearch())//迴圈查詢
row=this.lookSameTextByRecycle("down",searchText,position);
else//非迴圈查詢
row=this.lookSameTextToDown(searchText,position);
//3.執行查詢結果
this.showSelectionTextColor(row,row+searchText.length());
(3)showSelectionTextColor(int start,int end)方法,顯示結果
if(start !=-1){
textArea.setSelectionStart(start);
textArea.setSelectionEnd(end);
textArea.setSelectionColor(Color.blue);
}else{
textArea.setSelectionStart(textArea.getCaretPosition());
textArea.setSelectionEnd(textArea.getCaretPosition());
JOptionPane.showMessageDialog(null,"已經到盡頭了,沒有查詢的內容了");
}
(4)boolean isLowerCase()方法,判斷是否區分大小寫,區分返回false,否則返回true:
if(jckCase.isSelected()==false)
return true;//不區分大小寫處理
return false;
(5)boolean isReturnSearch()方法,判斷是否迴圈查詢,迴圈返回true,否則返回false:
if(jckRecycle.isSelected()==true)
return true;//迴圈
return false;//不迴圈
(6)int lookSameTextByRecycle(String direction,String searchText,int position)方法,返回查詢結果
upTimes=0;//恢復初始值
if(direction.equals("up")){//向上查詢
position--;
if(position<=0)
position=textArea.getText().length();//如果游標在最前面,則設定到最後面
}else{//向下查詢
if(position>=textArea.getText().length())
position=0;//如果游標在最後面,則設定到最前面
}
int row=-1;
int recycleTimes=0;//統計完整迴圈次數
boolean existSame=false;//假設沒找到
while(true){//迴圈查詢
if(direction.equals("up")){
row= this.lookSameTextToUp("recycle",searchText,position);
}else{
row=this.lookSameTextToDown(searchText,position);
}
if(row !=-1)
return row;
if(direction.equals("up")){
position=textArea.getText().length();
}else{
position=0;
}
recycleTimes++;
if(recycleTimes>1){//第二遍開始進行是否存在相同內容判斷
if(existSame==false)//第二遍還沒找到,說明沒有,給出提示
return -1; //沒找到返回-1
}
}
(7)int lookSameTextToUp(String style,String searchText,int position)方法,向上查詢
if(style.equals("normal")&&position<=textArea.getText().length()&&upTimes !=0)
position--;
int row=-1;
//1.獲取查詢內容及其長度
int searchLen=searchText.length();
//2.開始查詢
for(int i=position;i>=searchLen;i--){
//3.獲取查詢內容
String selectedText=this.getSelectedText(i-searchLen,i);
//4.進行比較
if(searchText.equals(selectedText)){
row= i-searchLen;
break;
}
}
upTimes++;
return row;
(8)String getSelectedText(int start,int end)方法,獲取文字域選擇內容
textArea.setSelectionStart(start);
textArea.setSelectionEnd(end);
if(this.isLowerCase())//不區分大小寫
return textArea.getSelectedText().toLowerCase();
return textArea.getSelectedText();
(9)int lookSameTextToDown(String searchText,int position)方法,向下查詢
upTimes=0;//恢復初始值
int row=-1;
//1.獲取查詢內容及其長度
int searchLen=searchText.length();
//2.開始查詢
for(int i=position;i<=textArea.getText().length()-searchLen;i++){
//3.獲取查詢內容
String selectedText=this.getSelectedText(i,i+searchLen);
//4.進行比較
if(searchText.equals(selectedText)){
row= i;
break;
}
}
return row;
==================================================================================================================================================================================
8.查詢下一個
8.1.分析:查詢下一個是向下非迴圈查詢,查詢的內容是歷史查詢內容儲存記錄(即最近一次查詢的內容)。因此,查詢內容的獲取就通過讀取儲存記錄即可。其它步驟均與查詢中相似
revoke.setEnable(false);
8.2.實現:
(1)執行方法:void actionSearchNext();
(2)具體實現:
//1.讀取儲存的記錄
String[] datas=DataUtils.readData();//第一個元素儲存的是查詢內容,第二個元素儲存的是替換內容。
//2.獲取查詢內容
String searchText=datas[0];
//3.獲取游標位置
int position=textArea.getCaretPosition();
//4.執行查詢操作(這個方法中有一個成員變數upTimes,解決方式在本類下定義一個一樣的變數即可)
int row=new Search(textArea).lookSameTextToDown(searchText,position);
//5.顯示查詢結果
new Search(textArea).showSelectionTextColor(row,row+searchText.length());
==================================================================================================================================================================================
9.查詢上一個
9.1.查詢下一個與查詢上一個思路一致。但是之前在查詢類中定義了一個全域性變數在方法中,如何解決呢?方式1就是在Note類下也定義一個和它一樣的全域性變數即可。
9.2.實現:
(1)Note類下定義成員變數:int upTiems=0;執行方法:void actionSearchPrevious();
(2)具體實現:
//1.讀取儲存的記錄
String[] datas=DataUtils.readData();//第一個元素儲存的是查詢內容,第二個元素儲存的是替換內容。
//2.獲取查詢內容
String searchText=datas[0];
//3.獲取游標位置
int position=textArea.getCaretPosition();
//4.執行查詢操作
int row=new Search(textArea).lookSameTextToUp("normal",searchText,position);
//5.顯示查詢結果
new Search(textArea).showSelectionTextColor(row,row+searchText.length());
觀察發現查詢上一個和查詢下一個的執行步驟幾乎相同,只是在第4步執行的查詢方向不一樣,因此可以優化:
(1)void actionSearchNext()方法:
this.actionSearchNextOrPrevious("next");
(2)void actionSearchPrevious()方法:
this.actionSearchNextOrPrevious("previous");
(3)void actionSearchNextOrPrevious(String direction)方法:
//1.讀取儲存的記錄
String[] datas=DataUtils.readData();//第一個元素是查詢內容,第二個元素是替換內容。
//2.獲取查詢內容
String searchText=datas[0];
//3.獲取游標位置
int position=textArea.getCaretPosition();
//4.執行查詢操作
int row=-1;
if(direction.equals("next"))
row=new Search(textArea).lookSameTextToDown(searchText,position);
else
row=new Search(textArea).lookSameTextToUp("normal",searchText,position);
//5.顯示查詢結果
new Search(textArea).showSelectionTextColor(row,row+searchText.length());
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
問題:這樣呼叫方法也會把查詢的對話方塊彈出來,這不是想要的結果,如何解決呢?
解決:建立一個處理類,沒有介面,專門處理這些查詢,方便後面替換操作時呼叫
步驟01.建立處理類:ActionSearch.java
該類有一個私有屬性:private MyTextArea textArea;
一個無參構造,一個有參有參構造,有參構造如下:
public ActionSearch(MyTextArea textArea){this.textArea=textArea;}
步驟02.Search類方法的提取與修改以及ActionSearch類的設計
步驟03.逐個方法提取,原則:不受本類的過度約束
提取方法列舉(由簡單到複雜):
方法1:顯示查詢結果的方法可以提取設計為:
public void showSelectionTextColor(int start,int end);
內容如下:
if(start !=-1){
textArea.setSelectionStart(start);
textArea.setSelectionEnd(end);
textArea.setSelectionColor(Color.blue);
}else{
textArea.setSelectionStart(textArea.getCaretPosition());
textArea.setSelectionEnd(textArea.getCaretPosition());
JOptionPane.showMessageDialog(null,"已經到盡頭了,沒有查詢的內容了");
}
方法2:獲取文字域選擇內容的方法可以提取修改如下:
//引數isCase是表示是否區分大小寫,true是不區分,false區分
public String getSelectedText(boolean isCase,int start,int end);
內容如下:
textArea.setSelectionStart(start);
textArea.setSelectionEnd(end);
if(isCase)//true表示不區分大小寫,統一做小寫處理
return textArea.getSelectedText().toLowerCase();
return textArea.getSelectedText();
方法3:迴圈查詢可以提取:
//引數isCase是表示是否區分大小寫,true是不區分,false區分
public int lookSameTextByRecycle(String direction,String searchText,int position);
內容如下(之前的update引數可以在呼叫時處理):
if(direction.equals("up")){//向上查詢
position--;
if(position<=0)
position=textArea.getText().length();//如果游標在最前面,則設定到最後面
}else{//向下查詢
if(position>=textArea.getText().length())
position=0;//如果游標在最後面,則設定到最前面
}
int row=-1;
int recycleTimes=0;//統計完整迴圈次數
boolean existSame=false;//假設沒找到
while(true){//迴圈查詢
if(direction.equals("up")){
row= this.lookSameTextToUp("recycle",searchText,position);
}else{
row=this.lookSameTextToDown(searchText,position);
}
if(row !=-1)
return row;
if(direction.equals("up")){
position=textArea.getText().length();
}else{
position=0;
}
recycleTimes++;
if(recycleTimes>1){//第二遍開始進行是否存在相同內容判斷
if(existSame==false)//第二遍還沒找到,說明沒有,給出提示
return -1; //沒找到返回-1
}
}
方法4:向上查詢方法可以提取:
//引數isCase是表示是否區分大小寫,true是不區分,false區分
public int lookSameTextToUp(String style,int upTimes,String searchText,int position);
內容如下:
if(style.equals("normal")&&position<=textArea.getText().length()&&upTimes !=0)
position--;
int row=-1;
//1.獲取查詢內容及其長度
int searchLen=searchText.length();
//2.開始查詢
for(int i=position;i>=searchLen;i--){
//3.獲取查詢內容
String selectedText=this.getSelectedText(i-searchLen,i);
//4.進行比較
if(searchText.equals(selectedText)){
row= i-searchLen;
break;
}
}
upTimes++;
return row;
方法5:向下查詢方法可以提取:
//引數isCase是表示是否區分大小寫,true是不區分,false區分
public int lookSameTextToDown(String searchText,int position);
內容如下(之前的update引數可以在呼叫時處理):
int row=-1;
//1.獲取查詢內容及其長度
int searchLen=searchText.length();
//2.開始查詢
for(int i=position;i<=textArea.getText().length()-searchLen;i++){
//3.獲取查詢內容
String selectedText=this.getSelectedText(i,i+searchLen);
//4.進行比較
if(searchText.equals(selectedText)){
row= i;
break;
}
}
return row;
然後對應修改Search類和查詢上一個和查詢下一個的方法呼叫即可。
技巧:如果不是多次呼叫,就用匿名物件呼叫方法,如果多次使用,可以建立一個私有的成員物件
==================================================================================================================================================================================
10.替換(單獨建立一個類,繼承JDialog,實現ActionListener介面)
類設計如下:
//替換對話介面
public class Replace extends JDialog implements ActionListener{
//查詢與替換的標籤、輸入框
JLabel bq_sear, bq_rep;
JTextField txf_searContent, txf_repContent;
//查詢下一個、替換、全部替換、取消按鈕
JButton bt_searNext, bt_RepOne, bt_RepAll, bt_Cancle;
//核取方塊,分別表示:區分大小寫、迴圈
JCheckBox jckCase, jckRecycle;
//宣告並初始化查詢和替換的值
String searchText = "";
String replaceText = "";
private MyTextArea textArea;
private ActionSearch actionSearch;
//構造程式碼塊執行讀取儲存查詢替換內容的檔案
{
String[] strings = ReadData.readDatas();
searchText = strings[0];
replaceText = strings[1];
}
public Replace(MyTextArea textArea) {
this.textArea=textArea;
actionSearch=new ActionSearch(textArea);
//有參構造方法
}
public void initialComponent() {
//元件建立
}
public void addActionListener() {
//新增監聽事件
}
public void initialDialog() {
//對話方塊設定
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == bt_searNext) {
//單個向下查詢
} else if (e.getSource() == bt_RepOne) {
//逐個替換
} else if (e.getSource() == bt_RepAll) {//全部替換
//全部替換
} else if (e.getSource() == bt_Cancle) {
//退出前儲存查詢替換文字框的內容
SaveData.saveDatas(txf_searContent.getText(), txf_repContent.getText());
this.dispose();
}
}
}
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
10.1.分析:
(1)替換是可以實現向下查詢,也可以實現向下的迴圈查詢
(2)具有區分大小寫選擇
(3)替換按鈕是點選一次,替換一次查詢到的內容,同時替換的內容背景色是藍色,替換結束給出提示
(4)全部替換是一次性執行全部替換,並且給出提示
10.2.替換僅是多了一項把查詢到的內容替換成指定內容,其處理方式方法並沒有太多的區別。
10.3.詳細解析過程:
(0)建立方法:boolean isLowerCase();
if(jckCase.isSelected()==false)//不區分大小寫
return true;
return false;
(1)查詢下一個,執行過程和查詢類中是一樣的
//1.獲取查詢內容
String searchText = txf_searContent.getText();
//2.判斷是否區分大小寫
boolean isCase=this.isLowerCase();
if(isCase)
searchText=searchText.toLowerCase();
//3.獲取游標位置
int position=textArea.getCaretPosition();
//4.執行查詢方法
int row=-1;
if(jckRecycle.isSelected()==true)//迴圈查詢
row=actionSearch.lookSameTextByRecycle
("down",0,isCase,searchText,position);
else//非迴圈查詢
row=actionSearch.lookSameTextToDown(isCase,searchText,position);
//5.顯示查詢結果
actionSearch.showSelectionTextColor(row,row+searchText.length());
(2)替換功能:單個替換與全部替換進行了優化。單個功能是找到並替換,同時顯示背景色為藍色,當是最後一個結束時,不再顯示背景色,同時給出提示;而全部替換始終沒有背景色,結束直接給提示資訊。
注意:逐個替換的開始位置以游標所在位置開始往後替換,不會從頭開始!
001.在監聽事件下:逐個替換和全部替換呼叫的都是同一個方法,只是引數值不一樣。
this.actionReplaceText("notRecycle");//逐個替換
this.actionReplaceText("recycle");//全部替換
002.方法void actionReplaceText(String actionStyle);
//1.獲取查詢內容與替換內容
searchText = txf_searContent.getText();
replaceText = txf_repContent.getText();
//2.判斷當前是否有選中的內容
int row = -1;
String selectedText = textArea.getSelectedText();
if (selectedText != null && selectedText.equals(searchText)) {//有,先進行替換,然後顯示替換文字背景色為藍色
int selectionStart = textArea.getSelectionStart();
int selectionEnd = textArea.getSelectionEnd();
textArea.replaceRange(replaceText,selectionStart,selectionEnd);
actionSearch.showSelectionTextColor(selectionStart, selectionEnd);
} else {//沒有則先查詢再替換
//3.判斷是否區分大小寫
boolean isCase=this.isLowerCase();
if(isCase)
searchText=searchText.toLowerCase();
if(actionStyle.equals("recycle")){//全部替換
while (true){
row=this.showReplaceResult("recycle",isCase);
if(row ==-1)//替換結束,退出迴圈
break;
}
}else{//逐個替換
this.showReplaceResult("notRecycle",isCase);
}
}
003.方法int showReplaceResult(String actionStyle,boolean isCase);
//1.獲取游標位置
int position=textArea.getCaretPosition();
//2.執行方法
int row=actionSearch.lookSameTextByRecycle
("down",0,isCase,searchText,position);
if(row !=-1) {
//3.顯示查詢結果
textArea.replaceRange(replaceText, row, row+searchText.length());
if(!actionStyle.equals("recycle"))
actionSearch.showSelectionTextColor
(row, row+searchText.length());
}else{
textArea.setSelectionStart(textArea.getCaretPosition());
textArea.setSelectionEnd(textArea.getCaretPosition());
if(!actionStyle.equals("recycle"))
JOptionPane.showMessageDialog(null,"替換結束,已到文末!");
else
JOptionPane.showMessageDialog(null,"替換結束,已全部替換!");
}
return row;
是否還可以再優化一些,需要再琢磨了,比如迴圈、大小寫判斷是否可以放到actionSearch類中。
思考:是否可以使用String的各種方法來實現查詢替換呢?是可以的。待日後更新貼上!
==================================================================================================================================================================================
11.轉到
11.1.當點選時,彈出對話方塊,輸入跳轉到的行號(記事本是從0開始的,為了習慣,一般0行顯示1)
建立這麼一個類:TurningLine繼承自JDialog;程式碼很少,直接貼上實現的方法:
//轉到指定行
public class TurningPage extends JDialog implements ActionListener {
JLabel bq1;
JTextField txf1;
JButton bt1, bt2;
private JTextArea textArea;
//文字域物件
public TurningPage(JTextArea textArea) {//省略}
public void initialComponent() {
//省略
}
public void initialDialog() {
//省略
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == bt1) {//確定
//獲取文字域總行數
int lineNum = textArea.getLineCount();
//獲取輸入的跳轉行數
int turnNum = Integer.parseInt(txf1.getText());
if (turnNum > lineNum&&turnNum <=0) {
JOptionPane.showMessageDialog(this, "行數超過了總行數", "記事本 - 跳轉",
JOptionPane.PLAIN_MESSAGE);
return;//結束方法的執行,不關閉對話視窗
} else {
try {
//跳轉到指定行:行數從0開始計,所以要減一。這裡的意思是從游標所在行偏移至指定行。
textArea.setCaretPosition(textArea.getLineStartOffset(turnNum - 1));
} catch (Exception ee) {
}
this.dispose();//關閉對話視窗
}
} else if (e.getSource() == bt2) {
this.dispose();//取消,退出對話方塊
}
}
}
==================================================================================================================================================================================
12.全選
12.1.設定文字選中區域,即設定開始和結束地方即可
方法:void actionSelectAll();
textArea.setSelectionStart(0);//開始位置
textArea.setSelectionEnd(textArea.getText().length());//結束位置
textArea.setSelectionColor(Color.blue);//選中區域顏色
==================================================================================================================================================================================
13.時間/日期:方法 void actionCurrentTimeAndDate();
13.1.方法1:新增在文末:
textArea.append("\t\t" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
13.2.方法2:新增在選中位置:
textArea.replaceRange(""+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), textArea.getSelectionStart(), textArea.getSelectionEnd());
==================================================================================================================================================================================
補充知識:以上已經實現了編輯檔案選單的所有功能,但是還有鍵盤輸入事件未監聽和實現,只需實現監聽,呼叫對應方法即可。
補充問題:
001.當關閉視窗右上角退出按鈕時,也需要做到對文字域內容的監聽。如果有內容執行判斷是否是新舊檔案,是否需要儲存,同時視窗物件是從集合中移除,如果是最後一個物件,退出程式。
002.預設情況下,可以使用鍵盤操作,但是應該實現鍵盤的監聽操作,例如輸入字元,刪除字元等,以實現撤銷命令的預期結果。
003.在查詢和替換對話中,當關閉或退出對話方塊時,應該將查詢文字和替換文字儲存到檔案中。
呼叫DataUtils.saveData(searchText,replaceText);//這兩變數是全域性變數
實現001.Note類實現WindowListener介面及其方法,其中稍微修改一下第三節中退出命令的操作方法即可:
(1)需要在initJFrame()方法最後新增物件的視窗監聽:this.addWindowListener(this);
@Override
public void windowClosing(WindowEvent e) {//據視窗右上角按鈕關閉
this.closeOrExitWindows();//呼叫執行方法
}
(2)修改退出方法:
private void actionExit(){
this.closeOrExitWindows();
}
(3)建立方法:void closeOrExitWindows()
if(list !=null){//需要在集合不為空的情況下執行
if(list.size()<=1){
if(this.exitCurrentWindows()==false)//儲存或不儲存命令執行結束返回值
System.exit(-1);//退出程式
else//取消命令,正常顯示視窗
this.setDefaultCloseOperation(JFrame.NORMAL);
}else{//多個物件時,只關閉當前使用物件
if(this.exitCurrentWindows()==false) {
list.get(activeFrame).dispose();//關閉該視窗
list.remove(activeFrame);//移除該物件
}
}
}
(4)修改方法:boolean exitCurrentWindows()
//1.判斷是否有內容
if (textArea.getText() != null && !textArea.getText().equals("")) {
//2.判斷新舊檔案
if (pathFile != null) {//舊檔案
//3.舊檔案判斷內容是否改變
if (!fileContent.equals(textArea)) {//改變
//儲存選擇訊息
if (this.readExitSystem("old"))
return true;
}
} else {//新檔案
if (this.readExitSystem("new"))
return true;
}
}
return false;
(5)剩下的呼叫方法在第3節檔案選單。
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
實現002.對視窗的命令只分析鍵盤輸入與刪除:
(1)實現鍵盤輸入時撤銷內容變化的監聽:
@Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {//按下空格鍵
if (!revokeList.get(4).equals("66")&&!revokeList.get(4).equals("00"))
this.revokeContentChange();
revokeList.set(3,"66");
revokeList.set(4,"66");
}
}
(2)實現鍵盤刪除時撤銷內容變化的監聽:
if(e.getKeyCode()==KeyEvent.VK_BACK_SPACE) {
if(textArea.getCaretPosition()>0)
textArea.replaceRange("", textArea.getCaretPosition(), textArea.getCaretPosition());//退格鍵刪除
}else if(e.getKeyCode()==KeyEvent.VK_DELETE) {
this.actionDelete();//delete鍵刪除
}
==================================================================================================================================================================================
關於查詢和替換,以及查詢上一個和查詢下一個,它們是相互關聯的。
(1)如果查詢中選擇了迴圈查詢、區分大小寫,那麼替換中也是對應選擇了的
(2)查詢上一個或下一個是否要迴圈查詢、是否區分大小寫,完全是由查詢或替換退出時的狀態決定的。
(3)所以需要對查詢和替換部分進行優化處理,為了不過多改動,應該是使用儲存檔案的方法進行處理
解決方案:在退出時儲存是否區分大小寫和是否迴圈狀態進行儲存,這樣在初始化時就可以直接使用了
001.修改儲存和讀取檔案的方法(都是靜態方法)
儲存方法:void saveDatas(String searchText,String replaceText,String cases,String re);
//cases是表示是否區分大小寫,re表示是否迴圈
br.write(searchText+","+replaceText+","+cases+","+re);
讀取方法:String[] readDatas();
String str=br.readLine();
return str.split(",");//表示以“,”為標誌分隔成一個字串陣列
餘下具體實現參見文末專案完整程式碼整理
==================================================================================================================================================================================
5.格式選單功能實現與程式碼示例
格式選單功能項分析:
1.自動換行:
(1)選中時水平滾動條消失,可以實現自動換行。同時轉到功能不可操作(轉到是編輯選單項)
(2)不選中時,水平滾動條存在,不會自動換行,需要按Enter鍵,轉到某行功能可以操作
(3)初始狀態,不選中,具有水平滾動條,不會自動換行
2.功能實現:
(1)初始狀態具有水平滾動條,需要在初始化滾動皮膚時設定顯示水平滾動條,即在方法
initJScrollPane()中設定:
textArea.setLineWrap(false);//設定文字域自動換行功能為false
jScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);即可。
(2)新增監聽,實現方法:void actionAutoLineWrap();
if(turnLine.isSelected()==true) {//自動換行被選中執行
turn.setEnabled(false);//編輯選單“轉到”不可操作
textArea.setLineWrap(true);//設定文字域自動換行功能為真
jScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);//水平滾動條消失
}else {//未被選中
turn.setEnabled(true);//編輯選單“轉到”可操作
textArea.setLineWrap(false);//設定文字域自動換行功能為false
jScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);//水平滾動條顯示
}
==================================================================================================================================================================================
3.字型(單獨建立一個對話方塊類):
(1)字型、字形、字號是通過列表顯示
(2)點選確認時,應用到文字域中
4.功能實現:
(1)介面實現,就是簡單的swing元件應用,省略。
(2)功能的實現也是簡單。
(3)列舉自認為比較需要加強學習的知識點:
001.獲取系統字型庫的名稱
//獲取系統字型庫
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
//獲取所有系統字型庫裡字型的名稱
String[] lt1Str = ge.getAvailableFontFamilyNames();
002.給文字域設定字型
//獲取選擇的字型名稱、樣式、大小
String fontName = txf_fontName.getText();
String fontStyle = txf_fontStyle.getText();
int fontSize = this.setSize(txf_fontSize.getText());
//設定具體字型
textArea.setFont(new Font(fontName, 5,fontSize));
6.檢視選單功能實現與程式碼示例
功能1:狀態列顯示與隱藏
實現分析:設計的時候狀態列是有兩個皮膚,只要把包含具體內容的那個皮膚實現隱藏與顯示即可。
奇數次單擊該功能時,隱藏;偶數次單擊該功能時,顯示。這是一個核取方塊,使用其方法isSelected()即可實現:
if(state.isSelected()==true)//選中
statementPanel.setVisible(false);//不可見
else//未選中
statementPanel.setVisible(true);//可見
==================================================================================================================================================================================
功能2:狀態列的行號和列號是根據游標的位置一直在變化中,字號的百分比也隨字號增減而改變
(1)獲取行號和列號的時刻準確資料,首先需要MyTextArea類實現介面CaretListener的方法
(2)在MyTextArea類新增一個有參構造方法和一個私有屬性:
private StatementPanel statementPanel;
public MyTextArea(StatementPanel statementPanel){
this.statementPanel=statementPanel;//通過有參構造確保在同一個視窗中操作的是同一個物件
this.addCaretListener(this);//給物件新增監聽
}
(3)在MyTextArea類實現監聽方法:
@Override
public void caretUpdate(CaretEvent e) {
if (e.getSource() == this) {
try {
int offset = e.getDot(); // 獲取插入符號的位置,即游標位置
int rows = this.getLineOfOffset(offset);// 偏移的行
int columns = e.getDot() - this.getLineStartOffset(rows) + 1;// 偏移的列=游標位置-偏移行的長度
statementPanel.changeRowAndColumnText(" 第" + (rows + 1) + "行,第" + columns + "列 ");
} catch (Exception ee) {
ee.printStackTrace();
}
}
}
(4)在StatementPanel類新增一個改變屬性內容的方法void changeRowAndColumnText(String str):
rowAndColumn.setText(str);
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
(5)百分比變化與字號的增大與縮小有關,具體實現在功能3,可先實現如下方法(6)
(6)在StatementPanel類新增一個改變百分比的方法void changeCharacterSize(String str):
characterSize.setText(str);
==================================================================================================================================================================================
功能3:實現字型的增大、減小、恢復
要求:單擊增大或減小時,以10%的遞增或遞減,範圍[10%,500%]。點選恢復時,顯示改變之前的正常字號大小
實現分析:
(1)獲取正常字號的值:應該是在遞增或遞減或恢復時第一次執行前獲取是符合要求的。不能一開始建立物件就獲取,因為中途可能會有改變。
(2)如何知道是第一次執行,使用全域性變數記錄:int fontChangeTimes=0;
(3)如何儲存初始字號,使用全域性變數記錄:Font fontChange=null;
(4)更新文字域字號變化的方法:void textAreaFontSizeChange(String action);
if(fontChangeTimes==0) //判斷是否是最初狀態,最初狀態記錄初始font值
fontChange= textArea.getFont();
if(action.equals("enlarge") && fontChangeTimes<40)//次數小於40均可增大
fontChangeTimes++;
else if(action.equals("narrow") && fontChangeTimes>10)//次數大於10均可減小
fontChangeTimes--;
int size=(int)(fontChange.getSize()*(1+0.1*fontChangeTimes));//獲取字號值
if(action.equals("restore"))//直接設定初始值
textArea.setFont(fontChange);
else
textArea.setFont(new Font(fontChange.getName(),fontChange.getStyle(),size));//更新文字域字號
statementPanel.changeCharacterSize(size+"%");//更新狀態列的百分比
(5)增大、縮小、恢復呼叫方法省略。
7.檢視功能如果是連結到指定網址的話,在編輯選單中已經實現,只需把地址傳入呼叫方法即可。
8.從頭梳理,以及主要核心程式碼
第一部分:Note.java程式入口在該類
package com.study.main;
......
/**
* 第1步:建立主程式,繼承JFrame
* JFrame是實現視窗需要
*/
public class Note extends JFrame implements ActionListener, MouseListener, KeyListener, WindowListener, Runnable {
//選單皮膚
private JMenuBar menuBar;
//檔案選單
private JMenu file;
private JMenuItem newFile, newWindow, openFile, saveFile, anotherSaveFile, settingPage, printFile, exit;
//編輯選單
private JMenu edit;
private JMenuItem revoke, cut, copy, paste, del, useBing, search, searchNext, searchPrevious, replace, selectAll, datetime;
public JMenuItem turn;
//格式選單
private JMenu format;
private JCheckBoxMenuItem turnLine;
private JMenuItem fonts;
//檢視選單
private JMenu view, zoom;
private JCheckBoxMenuItem state;
private JMenuItem enlarge, narrow, restore;
//幫助選單
private JMenu help;
private JMenuItem helps, sendMs, aboutNote;
//工具條皮膚
private ToolBarPanel toolBarPanel;
//文字域皮膚
public JScrollPane jScrollPane;
//文字域
public MyTextArea textArea;
//狀態列皮膚
private JPanel statePanel;
//狀態列具體內容皮膚
private StatementPanel statementPanel;
//記錄開啟視窗的集合
public static List<Note> list = new ArrayList<>();
//記錄活動的視窗
public static int activeFrame = 0;
//標記建立視窗的方式:-1是代表開啟新視窗,整數表示新建檔案視窗
int remark = -5;
//未儲存檔案的標題
public String updateTiltle = "*無標題--記事本";
//儲存了檔案的標題
public String initTitle = "無標題--記事本";
//執行具體方法類
private ActionDeals actionDeals;
//獲取輸入內容
private String inputContent="";
//獲取游標點選時的位置
private int startPosition=0;
/**
* main方法程式開始執行的地方
*
* @param args
*/
public static void main(String[] args) {
list.add(new Note());//無參構造方法建立匿名物件
}
/**
* 無參構造方法
*/
public Note() {
this.initJFrame();
}
/**
* 有參構造方法
*/
public Note(int remark) {
this.remark = remark;
this.initJFrame();
}
/**
* 新增選單項到容器中
*/
public void initMenu() {
//檔案、編輯、格式、檢視、幫助選單的具體內容
//具體內容略(都是一些建立物件)
this.setJMenuBar(menuBar);
}
/**
* 初始化工具條選單皮膚
*/
private void initToolBarPanel() {
toolBarPanel = new ToolBarPanel();
}
/**
* 初始化具有滾動條的皮膚:用於文字域
*/
private void initJScrollPane() {
textArea = new MyTextArea(statementPanel);
jScrollPane = new JScrollPane(textArea);
jScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
jScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
}
/**
* 初始化狀態列皮膚
*/
private void initStatePanel() {
statePanel = new JPanel();//這個皮膚是為了裝狀態列皮膚
statePanel.setLayout(new FlowLayout(FlowLayout.RIGHT));//使用流式佈局管理器
//具體內容略(都是一些建立物件)
//把相關元件新增到狀態列皮膚中
statePanel.add(statementPanel);
statePanel.add(jbl);
}
/**
* 新增元件的監聽事件
*/
private void addListener() {
//具體內容略(都是一些新增監聽操作)
}
/**
* 視窗的整體設計
*/
private void initJFrame() {
//具體內容略(在之前的選單檔案中已基本分析完)
}
@Override
public void actionPerformed(ActionEvent e) {
//具體內容略(都是一些對應監聽物件的執行方法呼叫)
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
//具體內容略(都是一些對應監聽物件的執行方法呼叫)
}
@Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {//鬆開空格鍵
//0.獲取變化的內容
inputContent=textArea.getText().substring(startPosition,textArea.getCaretPosition());
//1.設定撤消提示為:true,表示沒有執行撤消操作
actionDeals.revoking=true;
//2.判斷是否為第一次操作
actionDeals.carryContentChange("22");
//3.執行撤銷內容的更新
actionDeals.updateRevokeText("22",inputContent);
//4.更新起點
startPosition=textArea.getCaretPosition();
//5.更新inputContent
inputContent="";
}
}
@Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3) {//單擊右鍵
//右鍵彈出的列表選項選單
MyPopupMenu myPopupMenu = new MyPopupMenu(textArea, actionDeals);
//表示在使用容器顯示,顯示在右邊
myPopupMenu.show(e.getComponent(), e.getX(), e.getY());
}else if(e.getSource()==textArea){
startPosition=textArea.getCaretPosition();
}
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void windowOpened(WindowEvent e) {
}
//右上角關閉執行的方法
@Override
public void windowClosing(WindowEvent e) {
actionDeals.actionCloseOrExitWindows();
}
@Override
public void windowClosed(WindowEvent e) {
}
@Override
public void windowIconified(WindowEvent e) {
}
@Override
public void windowDeiconified(WindowEvent e) {
}
@Override
public void windowActivated(WindowEvent e) {
}
@Override
public void windowDeactivated(WindowEvent e) {
}
@Override
public void run() {
while (true) {
try {
//查詢操作只在有內容的情況下可以使用
if (textArea.getText() == null || textArea.getText().equals("")) {
search.setEnabled(false);
searchNext.setEnabled(false);
searchPrevious.setEnabled(false);
} else {
search.setEnabled(true);
searchNext.setEnabled(true);
searchPrevious.setEnabled(true);
}
//剪下、複製、刪除只有在選中的情況可以操作
if (textArea.getSelectedText() == null || textArea.getSelectedText().equals("")) {
cut.setEnabled(false);
copy.setEnabled(false);
del.setEnabled(false);
toolBarPanel.bt_copy.setEnabled(false);
toolBarPanel.bt_cut.setEnabled(false);
} else {
cut.setEnabled(true);
copy.setEnabled(true);
del.setEnabled(true);
toolBarPanel.bt_copy.setEnabled(true);
toolBarPanel.bt_cut.setEnabled(true);
}
if (list != null) {
for (int i = 0; i < list.size(); i++) {
if (list.get(i) !=null&&list.get(i).isActive() == true) {
activeFrame = i;
}
}
}
if (!textArea.getText().equals("") && textArea.getText() != null) {
revoke.setEnabled(true);
}
//時刻顯示選中內容的顏色
textArea.setSelectionColor(Color.BLUE);
if (actionDeals.fileContent.equals(textArea.getText()))
this.setTitle(initTitle);
else
this.setTitle(updateTiltle);
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
==================================================================================================================================================================================
第二部分:具體功能實現類
1.ActionDeals.java類,包含處理監聽事件呼叫的各種具體方法
package com.study.service;
.......
public class ActionDeals {
private MyTextArea textArea;
private ActionSearch actionSearch;
private StatementPanel statementPanel;
//記錄開啟或者已經儲存了的新建檔案
private File pathFile = null;
//記錄開啟或者已經儲存了的新建檔案內容
public String fileContent = "";
//記錄字號變化次數,用於改變字號的命令
private int fontChangeTimes = 0;
//記錄字型初始值,用於改變字號的命令
private Font fontChange = null;
// 獲取系統剪貼簿物件
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
//記錄文字域的集合,用於撤銷命令的操作
public List<String> revokeList = new ArrayList<>();
//宣告當前視窗物件集合
private Note note;
//記錄是否連續剪下或刪除同一處前的內容
public static List<Integer> countRevoke=new ArrayList<>();
//初始化revokeList集合
{
revokeList.add("");
revokeList.add("");
revokeList.add("00");
revokeList.add("00");
revokeList.add("00");
countRevoke.add(0);
countRevoke.add(0);
}
public ActionDeals() {
}
public ActionDeals(MyTextArea textArea, StatementPanel statementPanel, Note note) {
this.textArea = textArea;
this.statementPanel = statementPanel;
this.note=note;
actionSearch = new ActionSearch(textArea);
}
//檔案選單部分的功能實現
/**
* 檔案選單之新建步驟01:
* <p>
* 建立新檔案的執行方法
*/
public void actionCreateNewFile() {
//1.判斷文字域是否有內容
if (textArea.getText() == null || textArea.getText().equals("")) {
new Note(note.activeFrame);//沒有內容直接新建
} else {
//2.有內容彈首先判斷是新檔案還是舊檔案
if (pathFile == null) {//全域性變數檔案是空,是新檔案
this.doActionAccordingToOptionMessage("new");
} else {//舊檔案
//3.判斷內容是否發生改變
if (fileContent.equals(textArea.getText())) {//沒有發生改變
DataUtils.saveFileData(textArea.getText(), pathFile.getAbsolutePath());//直接執行儲存檔案的操作
} else {//發生改變
this.doActionAccordingToOptionMessage("old");//有變化執行儲存檔案操作
}
}
}
}
/**
* 檔案選單之新建步驟02:
* <p>
* 執行新舊檔案儲存操作
*
* @param fileStyle 新檔案("new")或舊檔案("old")
*/
public void doActionAccordingToOptionMessage(String fileStyle) {
//1.獲取詢問對話方塊結果值:0是儲存,1是不儲存,2是取消
int row = this.showOptionMessageByJOptionPane();
//2.根據返回結果執行對應命令
if (row == 0) {//儲存
if (fileStyle.equals("new"))
this.saveFileDialog("新建儲存...");//開啟儲存檔案對話方塊
else
DataUtils.saveFileData(textArea.getText(), pathFile.getAbsolutePath());//執行直接儲存檔案的操作
new Note(note.activeFrame);//執行建立新視窗,同時關閉舊視窗的方法
} else if (row == 1) {//不儲存
new Note(note.activeFrame);
}
}
/**
* 檔案選單之新建步驟03:
* <p>
* 是否儲存訊息詢問
*
* @return 返回選擇結果int型別
*/
public int showOptionMessageByJOptionPane() {
Object[] options = {"儲存", "不儲存", "取消"};// 選擇訊息
return JOptionPane.showOptionDialog(null, "你是否要儲存檔案?", "記事本", JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
}
/**
* 檔案選單之新建步驟04:
* 檔案選單之儲存檔案步驟02:
* 檔案選單之檔案另存為步驟02:
* <p>
* 開啟儲存檔案對話方塊
*
* @param title 對話方塊標題
*/
public void saveFileDialog(String title) {
//1.開啟儲存對話方塊
JFileChooser chooser = new JFileChooser("C:\\Users\\shinelon\\Desktop");//預設開啟對話方塊顯示的位置
chooser.setDialogType(JFileChooser.SAVE_DIALOG);//對話方塊型別
chooser.setDialogTitle(title);//設定顯示的對話方塊標題
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);//顯示檔案和目錄的指令。
chooser.setSelectedFile(new File("新建文字文件.txt"));//預設儲存的檔名
chooser.showSaveDialog(null);//開啟對話方塊
chooser.setVisible(true);//顯示其可見
System.out.println(JFileChooser.APPROVE_OPTION);
//2.獲取儲存檔案進行重名判斷
File selectedFile = chooser.getSelectedFile();//獲取儲存的檔案
//檔案不為空,表示選擇有檔案,否則不執行操作
if (selectedFile != null) {
boolean exist = this.existSameFile(selectedFile);//判斷選擇的檔案是否已經存在
while (exist) {
if (this.replaceExistSameFile(selectedFile)) {//替換同名檔案
break;
} else {//不替換
chooser = new JFileChooser("C:\\Users\\shinelon\\Desktop");//預設開啟對話方塊顯示的位置
chooser.setDialogType(JFileChooser.SAVE_DIALOG);//對話方塊型別
chooser.setDialogTitle(title);//設定顯示的對話方塊標題
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);//顯示檔案和目錄的指令。
chooser.setSelectedFile(new File("新建文字文件.txt"));//預設儲存的檔名
chooser.showSaveDialog(null);//開啟對話方塊
chooser.setVisible(true);//顯示其可見
selectedFile = chooser.getSelectedFile();//獲取儲存的檔案
exist = this.existSameFile(selectedFile);
}
}
//3.執行儲存檔案
DataUtils.saveFileData(textArea.getText(), selectedFile.getAbsolutePath());
//4.更新相關引數(只有在儲存單擊事件和另存為單擊事件下更新)
if (!title.equals("新建儲存...")) {
this.updateParameters(selectedFile);
}
}
}
/**
* 檔案選單之新建步驟05:
* <p>
* 判斷儲存選擇的檔案是否重名
*
* @param selectFile 判斷檔案
* @return 結果返回布林值,true表示重名,false表示不重名
*/
public boolean existSameFile(File selectFile) {
//1.獲取檔名
String name = selectFile.getName();
//2.獲取檔案所在當前最近一級資料夾下的所有檔案
File[] files = selectFile.getParentFile().listFiles();
//3.遍歷對比
if (files != null) {
for (File file1 : files) {
if (name.equals(file1.getName()))//檔名相同
return true;
}
}
return false;
}
/**
* 檔案選單之新建步驟06:
* <p>
* 判斷是否替換重名檔案
*
* @param selectFile 重名檔案
* @return 返回結果布林值,true表示替換,否表示不替換
*/
public boolean replaceExistSameFile(File selectFile) {
Object[] options = {"是", "否"};// 選擇訊息
int row = JOptionPane.showOptionDialog(null, selectFile.getName() + " 已存在。\n要替換它嗎?", "確認另存為", JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[1]);
return row == 0 ? true : false;
}
/**
* 檔案選單之新建步驟08:
* 檔案選單之開啟檔案步驟02:
* 檔案選單之儲存檔案步驟03:
* <p>
* 更新顯示標題、檔案資訊
*
* @param selectedFile 更新的檔案
*/
public void updateParameters(File selectedFile) {
note.updateTiltle = "*" + selectedFile.getName();
note.initTitle = selectedFile.getName();
note.setTitle(note.initTitle);
pathFile = selectedFile;
fileContent = textArea.getText();
}
/**
* 檔案選單之開啟視窗:
* <p>
* 開啟新視窗
*/
public void actionOpenNewWindows() {
note.list.add(new Note(-1));
}
/**
* 檔案選單之開啟檔案步驟01:
* <p>
* 開啟檔案
*/
public void actionOpenFile() {
//1.開啟對話方塊
JFileChooser chooser = new JFileChooser("C:\\Users\\shinelon\\Desktop");//預設開啟顯示的位置
chooser.setDialogTitle("選擇開啟檔案...");
chooser.setDialogType(JFileChooser.OPEN_DIALOG);//對話方塊型別
chooser.setSelectedFile(new File(".txt"));//只顯示字尾為“.txt”的檔案
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);//顯示檔案和目錄的指令。
chooser.showOpenDialog(null);//開啟對話方塊
chooser.setVisible(true);
//2.獲取選擇的檔案
File selectedFile = chooser.getSelectedFile();
if (selectedFile != null) {
//3.讀取檔案到文字域
textArea.setText(DataUtils.readFileData(selectedFile.getAbsolutePath()));
//4.更新相關引數
this.updateParameters(selectedFile);
//5.更新撤銷相關內容
this.carryContentChange("22");
this.updateRevokeText("22","");
}
}
/**
* 檔案選單之儲存檔案步驟01:
* <p>
* 儲存檔案
*/
public void actionSaveFile() {
//1.判斷將要儲存檔案是否是新檔案
if (pathFile != null) {//舊檔案
//2.直接執行儲存檔案的方法
DataUtils.saveFileData(textArea.getText(), pathFile.getAbsolutePath());
//3.直接執行儲存檔案的方法
this.updateParameters(pathFile);
} else {//新檔案
//2.呼叫儲存對話方塊儲存新檔案
this.saveFileDialog("儲存...");
}
}
/**
* 檔案選單之檔案另存為步驟01:
* <p>
* 檔案另存為
*/
public void actionSaveFileNewPosition() {
//直接呼叫儲存對話方塊
this.saveFileDialog("另存為...");
}
/**
* 檔案選單之頁面設定
*/
public void actionSettingPage() {
new SettingPage();
}
/**
* 檔案選單之列印
*/
public void actionPrintSetting() {
new PrintSetting();
}
/**
* 檔案選單之退出當前視窗步驟01:
* <p>
* 退出當前視窗
*/
public void actionCloseOrExitWindows() {
//1.判斷當前集合是否存在物件
if (note.list != null) {
//2.集合物件的個數
if (note.list.size() <= 1) {//單個物件
//3.文字域是否有內容判斷處理,儲存與不儲存均退出程式,否則正常顯示
if (this.exitCurrentWindows())//執行儲存或不儲存命令或沒有內容
System.exit(0);//退出程式
else
note.setDefaultCloseOperation(JFrame.NORMAL);
} else {
//3.文字域是否有內容判斷處理,儲存與不儲存均關閉當前視窗,同時移除出集合,否則不執行操作
if (this.exitCurrentWindows()) {//執行儲存或不儲存命令或沒有內容
note.dispose();//關閉該視窗
note.list.remove(note.activeFrame);//移除該物件
}else{
note.setDefaultCloseOperation(JFrame.NORMAL);
}
}
}
}
/**
* 檔案選單之退出當前視窗步驟02:
*
* @return 返回布林值結果,儲存和不儲存或沒有內容返回true,取消返回false
*/
public boolean exitCurrentWindows() {
//1.判斷是否有內容
if (textArea.getText() != null && !textArea.getText().equals("")) {//有內容
//2.判斷新舊檔案
if (pathFile != null) {//舊檔案
//3.舊檔案判斷內容是否改變
if (fileContent.equals(textArea)==true) {//內容改變
//儲存選擇訊息
if (this.readExitSystem("old") == false)//沒有儲存,選擇了取消
return false;
}
} else {//新檔案
if (this.readExitSystem("new")==false)//沒有儲存,選擇了取消
return false;
}
}
return true;
}
/**
* 檔案選單之退出當前視窗步驟02:
* <p>
* 儲存詢問判斷
*
* @param exitStyle 檔案型別:新檔案或者舊檔案
* @return 返回布林值結果,儲存和不儲存返回true,取消返回false
*/
public boolean readExitSystem(String exitStyle) {
//1.獲取選擇結果,0儲存,1不儲存,2取消
int i = this.showOptionMessageByJOptionPane();
//2.執行選擇結果
if (i == 0) {//儲存
if (exitStyle.equals("new"))
this.saveFileDialog("儲存為...");
else
DataUtils.saveFileData(textArea.getText(), pathFile.getAbsolutePath());
}else if (i == 2) {//取消
return false;
}
return true;
}
//編輯選單部分的功能實現
/**
* 編輯選單之撤消實現步驟01:
* <p>
* 撤消功能
*/
public void actionRevoke() {
//1.執行撤消命令
if (revokeList.get(2).equals("55")==true) {//偶次數執行撤消命令
//2.內容顯示
textArea.setText(revokeList.get(1));
revokeList.set(2, "88");
} else {//奇次數執行撤銷命令(從點選撤消命令開始到點選或執行其它操作為止,再次點選則重新計數)
//2.內容顯示
textArea.setText(revokeList.get(0));
revokeList.set(2, "55");
}
}
/**
* 編輯選單之剪下或複製的功能實現
*
* @param action 執行的操作:剪下("cut")或複製("copy")
*/
public void actionCutOrCopy(String action) {
//1.獲取文字域選中的剪下內容
String selectedText = textArea.getSelectedText();
//2.將剪下內容放入轉換物件中
Transferable selection = new StringSelection(selectedText);
if (action.equals("cut")) {
//3.撤銷內容更新判斷
this.carryContentChange("11");
//4.文字域中將剪下部分用空字元替換
textArea.replaceRange("", textArea.getSelectionStart(), textArea.getSelectionEnd());
//5.執行撤銷內容的更新
this.updateRevokeText("11",selectedText);
}
//4.將剪下內容轉換後的文字放置到剪貼簿中
clipboard.setContents(selection, null);
}
/**
* 編輯選單之貼上功能實現
*/
public void actionPaste() {
//1.判斷剪貼簿中是否含有字串內容
if (clipboard.isDataFlavorAvailable(DataFlavor.stringFlavor)) {
try {
//2.獲取剪貼簿的文字內容
String data = (String) clipboard.getData(DataFlavor.stringFlavor);
//3.撤銷內容更新判斷
this.carryContentChange("22");
//4.將文字選中內容替換成剪貼簿獲取的內容
textArea.replaceRange(data, textArea.getSelectionStart(), textArea.getSelectionEnd());
//5.執行撤銷內容的更新
this.updateRevokeText("22",data);
} catch (UnsupportedFlavorException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 編輯選單之刪除功能實現
*/
public void actionDel() {
//1.獲取將要刪除的內容
String deleteContent=textArea.getSelectedText();
//2.撤銷內容更新判斷
this.carryContentChange("11");
//3.文字域中將刪除部分用空字元替換
textArea.replaceRange("", textArea.getSelectionStart(), textArea.getSelectionEnd());
//4.執行撤銷內容的更新
this.updateRevokeText("11",deleteContent);
}
/**
* 編輯選單之撤消實現步驟02:
* <p>
* 判斷是否執行更新撤消內容
*/
public void carryContentChange(String str) {
if (revokeList.get(4).equals(str)==false){
revokeList.set(0, revokeList.get(1));
revokeList.set(2, "88");
revokeList.set(3,str);
}
}
/**
* 執行撤銷內容的更新
* @param str 執行什麼操作:剪下、貼上、刪除、輸入
* @param actionContent 改變的內容部分
*/
public void updateRevokeText(String str,String actionContent){
//1.是否重複執行相同操作
if(revokeList.get(3).equals(str)==true&&revokeList.get(4).equals(str)==true){//是
//2.當前游標的位置
countRevoke.set(1,textArea.getCaretPosition());
//3.分操作判斷
if(str.equals("11")){//剪下和刪除
//4.不是連續同一個地方的操作
if(countRevoke.get(0) != countRevoke.get(1)&&countRevoke.get(0)!=countRevoke.get(1)+actionContent.length())//非連續操作
revokeList.set(0,revokeList.get(1));
//5.更新恢復內容
revokeList.set(1,textArea.getText());
}else if(str.equals("22")){//輸入和貼上
//4.不是連續同一個地方的操作
if(countRevoke.get(1) != (countRevoke.get(0)+actionContent.length()))//連續在後面貼上
revokeList.set(0,revokeList.get(1));
//5.更新恢復內容
revokeList.set(1,textArea.getText());
}
//6.更新最初游標位置(操作前)
countRevoke.set(0,countRevoke.get(1));
}else{
revokeList.set(1,textArea.getText());
//6.更新最初游標位置(操作前)
countRevoke.set(0,textArea.getCaretPosition());
System.out.println("初始值:"+countRevoke.get(0));
}
//7.更新標記字串
revokeList.set(3,str);
revokeList.set(4,str);
}
/**
* 編輯選單之使用Bing搜尋功能實現
*/
public void actionUseBing() {
String url = "https://cn.bing.com/search";//搜尋的網址
actionSearch.openBrowser(url);//呼叫執行的方法
}
/**
* 編輯選單之查詢替換相關功能實現
*
* @param DO_ACTION 操作命令引數
* DO_ACTION = 0 表示執行查詢功能
* DO_ACTION = 1 表示執行查詢下一個功能
* DO_ACTION = 2 表示執行查詢上一個功能
* DO_ACTION = 3 表示執行替換功能
*/
public void actionAboutSearchOrReplace(Integer DO_ACTION) {
if (DO_ACTION == 0)
new Search(textArea);
else if (DO_ACTION == 1)
actionSearch.actionSearchNextOrPrevious("down");
else if (DO_ACTION == 2)
actionSearch.actionSearchNextOrPrevious("up");
else if (DO_ACTION == 3)
new Replace(textArea);
}
/**
* 編輯選單之轉到指定行功能實現
*/
public void actionTurn() {
new TurningLine(textArea);
}
/**
* 編輯選單之全選功能實現
*/
public void actionSelectAll() {
actionSearch.showSelectionTextColor("", 0, textArea.getText().length(), "");
}
/**
* 編輯選單之時間/日期功能實現
*/
public void actionDatetime() {
textArea.replaceRange("" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), textArea.getSelectionStart(), textArea.getSelectionEnd());
}
//格式選單功能實現
/**
* 格式選單之自動換行功能實現
*/
public void actionTurnLine(boolean turnLineIsSelected) {
if (turnLineIsSelected) {//選中核取方塊,自動換行
note.turn.setEnabled(false);//轉到指定行不可操作
textArea.setLineWrap(true);//文字域可以自動換行
note.jScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);//不顯示水平滾動條
} else {
note.turn.setEnabled(true);
textArea.setLineWrap(false);
note.jScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);//顯示水平滾動條
}
}
/**
* 格式選單之字型設定功能實現
*/
public void actionFonts() {
new MyFont(textArea);
}
//檢視選單功能實現
/**
* @param DO_ACTION DO_ACTION = 10 表示恢復功能
* DO_ACTION = 12 表示縮放功能
* DO_ACTION = 21 表示增大功能
*/
public void actionTextAreaFontSizeChange(Integer DO_ACTION) {
System.out.println("times =" + fontChangeTimes);
if (fontChangeTimes == 0) //判斷是否是最初狀態,最初狀態記錄初始font值
fontChange = textArea.getFont();
if (DO_ACTION == 21 && fontChangeTimes < 40)//次數小於40均可增大
fontChangeTimes++;
else if (DO_ACTION == 12 && fontChangeTimes > -9)//次數大於10均可減小
fontChangeTimes--;
int size = (int) (fontChange.getSize() * (1 + 0.1 * fontChangeTimes));//獲取字號值
if (DO_ACTION == 10) {//直接設定初始值
fontChangeTimes = 0;
textArea.setFont(fontChange);
} else {
textArea.setFont(new Font(fontChange.getName(), fontChange.getStyle(), size));//更新文字域字號
}
statementPanel.changeCharacterSize((100 + fontChangeTimes * 10) + "%");//更新狀態列的百分比
}
/**
* 檢視功能之狀態列功能實現
*/
public void actionState(boolean stateIsSelected) {
if (stateIsSelected)
statementPanel.setVisible(false);
else
statementPanel.setVisible(true);
}
//幫助選單功能實現
/**
* 幫助選單之檢視幫助功能實現
*/
public void actionHelps() {
String url="https://cn.bing.com/search?q=%E8%8E%B7%E5%8F%96%E6%9C%89%E5%85%B3+windows+10+%E4%B8%AD%E7%9A%84%E8%AE%B0%E4%BA%8B%E6%9C%AC%E7%9A%84%E5%B8%AE%E5%8A%A9&filters=guid:%224466414-zh-hans-dia%22%20lang:%22zh-hans%22&form=T00032&ocid=HelpPane-BingIA&rdr=1&rdrig=D377191E0D1B4E5FA77F134253B637C1\n";
actionSearch.openBrowser(url);
}
//剩餘兩個幫助選單功能未實現
}
==================================================================================================================================================================================
2.ActionSearch.java實現查詢替換的具體功能
package com.study.service;
......
public class ActionSearch implements MouseListener {
private MyTextArea textArea;
int row = -1;
public ActionSearch(){}
public ActionSearch(MyTextArea textArea){
this.textArea=textArea;
textArea.addMouseListener(this);
}
//查詢上一個和查詢下一個的執行方法
public void actionSearchNextOrPrevious(String direction){
//1.讀取儲存的記錄
String[] datas=DataUtils.readDatas();//第一個元素是查詢內容,第二個元素是替換內容。
//2.獲取查詢內容、是否區分大小寫,是否迴圈
String searchText=datas[0];
String cases=datas[2];
String re=datas[3];
boolean isCase=false;
if(cases.equals("false")){
searchText=searchText.toLowerCase();
isCase=true;
}
//3.獲取游標位置
int position=textArea.getCaretPosition();
//4.執行查詢操作
if(direction.equals("down")){
if(re.equals("true")) //迴圈查詢
row=this.lookSameTextByRecycle("down",isCase,searchText,position);
else
row=this.lookSameTextToDown(isCase,searchText,position);
}else {
System.out.println("row ="+row);
if(row !=-1)
if(row <searchText.length())
position=0;
else
position=row;
if(re.equals("true")) //迴圈查詢
row=this.lookSameTextByRecycle("up",isCase,searchText,position);
else
row=this.lookSameTextToUp(isCase,searchText,position);
}
//5.顯示查詢結果
this.showSelectionTextColor(direction,row,row+searchText.length(),searchText);
}
public void showSelectionTextColor(String direction,int start,int end,String searchText){
if(start !=-1){
textArea.setSelectionStart(start);
textArea.setSelectionEnd(end);
}else{
if(direction.equals("up"))
textArea.setCaretPosition(0);
else
textArea.setCaretPosition(textArea.getText().length());
JOptionPane.showMessageDialog(null,"找不到“"+searchText+"”");
}
}
//引數isCase是表示是否區分大小寫,true是不區分,false區分
public String getSelectedText(boolean isCase,int start,int end){
textArea.setSelectionStart(start);
textArea.setSelectionEnd(end);
if(isCase)//true表示不區分大小寫,統一做小寫處理
return textArea.getSelectedText().toLowerCase();
return textArea.getSelectedText();
}
//查詢對話方塊執行方法
public void doSearching(boolean upOrDown,boolean isRecycle,boolean isCase,String searchText){
int position=textArea.getCaretPosition();
//3.查詢方向
if (upOrDown == true) {//向上查詢
System.out.println("row ="+row);
if(row !=-1){
if(row <searchText.length())
position=0;
else
position=row;
}
if (isRecycle) { //迴圈查詢
row = this.lookSameTextByRecycle("up",isCase, searchText, position);
} else {
row = this.lookSameTextToUp(isCase, searchText, position);
}
//upTimes++;
//4.執行查詢結果
this.showSelectionTextColor("up",row,row+searchText.length(),searchText);
}else {//向下查詢
//upTimes = 0;
if (isRecycle) //迴圈查詢
row = this.lookSameTextByRecycle("down", isCase, searchText, position);
else
row = this.lookSameTextToDown(isCase, searchText, position);
//4.執行查詢結果
this.showSelectionTextColor("down",row,row+searchText.length(),searchText);
}
}
public int lookSameTextByRecycle(String direction,boolean isCase,String searchText,int position){
int recycleTimes=0;//統計完整迴圈次數
boolean existSame=false;//假設沒找到
while(true){//迴圈查詢
if(direction.equals("up")){
row= this.lookSameTextToUp(isCase,searchText,position);
}else{
row=this.lookSameTextToDown(isCase,searchText,position);
}
if(row !=-1)
return row;
if(direction.equals("up")){
position=textArea.getText().length();
}else{
position=0;
}
recycleTimes++;
if(recycleTimes>1){//第二遍開始進行是否存在相同內容判斷
if(existSame==false)//第二遍還沒找到,說明沒有,給出提示
return -1; //沒找到返回-1
}
}
}
public int lookSameTextToUp(boolean isCase,String searchText,int position){
int num=-1;
//1.獲取查詢內容及其長度
int searchLen=searchText.length();
//2.正常開始查詢
for(int i=position;i>=searchLen;i--){
//3.獲取查詢內容
String selectedText=this.getSelectedText(isCase,i-searchLen,i);
//4.進行比較
if(searchText.equals(selectedText)){
num= i-searchLen;
break;
}
}
return num;
}
public int lookSameTextToDown(boolean isCase,String searchText,int position){
int num=-1;
//1.獲取查詢內容及其長度
int searchLen=searchText.length();
//2.開始查詢
for(int i=position;i<=textArea.getText().length()-searchLen;i++){
//3.獲取查詢內容
String selectedText=this.getSelectedText(isCase,i,i+searchLen);
//4.進行比較
if(searchText.equals(selectedText)){
num= i;
break;
}
}
return num;
}
public void openBrowser(String url){
try {
// 獲取作業系統的名字
String osName = System.getProperty("os.name", "");
if (osName.startsWith("Mac OS")) {
// 蘋果的開啟方式
Class<?> fileMgr = Class.forName("com.apple.eio.FileManager");
Method openURL = fileMgr.getDeclaredMethod("openURL", new Class[]{String.class});
openURL.invoke(null, new Object[]{url});
} else if (osName.startsWith("Windows")) {
// windows的開啟方式。
Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url);
} else {
// Unix or Linux的開啟方式
String[] browsers = {"firefox", "opera", "konqueror", "epiphany", "mozilla", "netscape"};
String browser = null;
for (int count = 0; count < browsers.length && browser == null; count++)
// 執行程式碼,在brower有值後跳出,
// 這裡是如果程式建立成功了,==0是表示正常結束。
if (Runtime.getRuntime().exec(new String[]{"which", browsers[count]}).waitFor() == 0) {
browser = browsers[count];
}
if (browser == null) {
throw new Exception("Could not find web browser");
} else {
// 這個值在上面已經成功的得到了一個程式。
Runtime.getRuntime().exec(new String[]{browser, url});
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void mouseClicked(MouseEvent e) {
if(e.getSource()==textArea)
if(textArea.getCaretPosition()==textArea.getText().length()||textArea.getCaretPosition()==0)
row=-1;
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
}
==================================================================================================================================================================================
3.其它類不再列舉,基本都是一些元件的建立,沒什麼需要特別分析和理解了。
9.第一版記事本完整程式碼連結:
相關文章
- SwiftUI 官方畫圖例項詳細解析SwiftUI
- GIT初學者詳細指令學習Git
- Electron 的初學者詳細指南
- python綜合學習七之TensorFlow初識Python
- 一篇掌握SpringBoot+SpringCache+Redis超詳細例項Spring BootGCRedis
- Android專案常用功能綜合例項Android
- Flutter中表單元件綜合運用例項Flutter元件
- 資料庫索引:綜合詳細指南資料庫索引
- MySQL事務學習筆記(一) 初遇篇MySql筆記
- Spring 事務學習筆記(一) 初遇篇Spring筆記
- Spring事務管理(詳解+例項)Spring
- javase學習記錄之------生成者消費者之等待喚醒機制Java
- Python簡單函式迴圈綜合例項Python函式
- Spring MVC之例項初體驗SpringMVC
- Web安全之CSRF例項解析Web
- Hadoop 學習系列(三)之 YARN 詳細解析HadoopYarn
- Hadoop 學習系列(二)之 HDFS 詳細解析Hadoop
- redis學習筆記(詳細)——高階篇Redis筆記
- JUnit5學習之八:綜合進階(終篇)
- C語言例項解析精粹學習筆記——19C語言筆記
- 深入淺出Win32多執行緒程式設計--之綜合例項Win32執行緒程式設計
- Spring中Bean的例項化詳細流程SpringBean
- Python 3 學習筆記之類與例項Python筆記
- JAVASE之JAVA泛型篇Java泛型
- Java攻城獅第二季綜合練習——初學者好懂版Java
- 【pandas學習筆記】綜合整理筆記
- ClickHouse(15)ClickHouse合併樹MergeTree家族表引擎之GraphiteMergeTree詳細解析
- ClickHouse(13)ClickHouse合併樹MergeTree家族表引擎之CollapsingMergeTree詳細解析
- ClickHouse(11)ClickHouse合併樹MergeTree家族表引擎之SummingMergeTree詳細解析
- ClickHouse(12)ClickHouse合併樹MergeTree家族表引擎之AggregatingMergeTree詳細解析
- 大資料初學者必備的詳細版學習路線圖大資料
- Python適合初學者學習嗎?Python
- 零基礎學習 Python 之細說類屬性 & 例項Python
- 【深度學習篇】---CNN和RNN結合與對比,例項講解深度學習CNNRNN
- Java 介面實現多型 -- 膝上型電腦綜合例項Java多型
- 基於QT錄製PCM音訊例項詳細QT音訊
- Hadoop入門(二)之 HDFS 詳細解析Hadoop
- 記事本