2.1 仿Windows計算器概述 Windows 計算器,是 Windows 操作系統自帶計算器,,可以幫助用戶完成數據的運算,它可分為“標準型“和“科學型”,本章的仿 Windows 計算器是標準型的 java 實現,標準型 Windows 計算器實現的主要功能有:四則運算;求倒數;求開方;存儲計算結果;讀取計算結果;累積計算結果。 在本章中,我們將使用到 JFrame 和 JPanel 兩個 Swing 容器,使用到 JTextField 和JButton 兩個 Swing 容器,使用 BorderLayout 和 GridLayout 做兩個布局器,以及使用到事件、事件監聽器和事件適配器等。 實現一個計算器,界面中需要提供各種輸入的按鈕,再以這些按鈕組成計算器的鍵盤,用戶點擊鍵盤輸入值后,就可以將其所輸入的值顯示到一個文本框中,運算后,再將結果顯示到文本框中。計算器的最終效果如圖 2.1 所示。
從圖 2.1 中可以看到,我們開發界面的時候,需要提供一個文本框在窗口的最上部,文本框下面再提供各個計算器的按鈕。 2.1.1 數學與其它符號介紹 在此計算器中,主要使用的數學運算有加、減、乘、除四則運算,或者對一個正數進行開方,或者對一個非 0 的數學求倒數,使用到的數學符號有: (1)加、減、乘、除,對應使用的符號是”+”,”-“,”*”,”/”; (2)開方與倒數,對應使用的符號是”sqrt”,”1/x”; (3)求結果使用的數學符號是”=”; (4)”%”號,如果使用此符號,第二個操作數就等于兩數相乘再除以 100。 除了用于數學運算的符號,Windows 計算器還提供對計算結果做存儲、讀取、累加、清除等操作,亦有對數字顯示框中的數字做退格操作,還可以清除上次計算結果或者全部結果: (1)使用符號”MC”,”MR”,”MS”,”M+”代表清除存儲結果、讀取存儲結果、保存存儲結果和累加存儲結果; (2)使用“Backspace”符號代表退格; (3)使用”CE”和”C”代表清除上次計算結果和清除所有計算結果。 四則運算在程序中可以直接使用 Java 運算符實現,實現開方可以調用 Math 類的 sqrt 方法,倒數可以使用 1 來除以原始的數字。當用戶需點擊“=”的時候,計算器就需要將最終的計算結果顯示到文本框中。其他的計算器功能都可以通過計算器內部的程序實現,例如使用某個字符串或者數字來保存相應的結果,如果需要計取、存儲、累加或者清除結果,可以通過改變或者讀取我們所保存的值來實現。 2.1.2 界面說明 界面中使用的 Swing 組件相對簡單,整個大窗口可以看作一個 JFrame 對象,在 JFrame 對象中,存放一個 JPanel 對象,我們需要為這個 JPanel 對象進行布局,將文本框(JTextField 對象)與各個計算器按鈕(JButton 對象)添加到這個 JPanel 中。在添加計算器按鈕的時候,我們可以使用 GridLayout布局處理器來進行網格狀布局,由于各個計算器按鈕都是以網格狀分布在界面中的,因此使用GridLayout 非常適合。
2.2 流程描述 用戶打開計算器后,在沒有關閉計算器之前,可以通過鼠標點擊”1”到”9” 數字鍵和點擊”+”,”-“,”*”,”/”鍵去輸入要運算結果的算術式,再通過點擊 “=”,”sqrt”,”1/x”等鍵去直接獲取計算結果,除外,還可以點擊”MC”,”MR” “MS”,”M+”鍵去清除、讀取、保存、累加計算顯示框中顯示的數字,還有清除上次結果、清除所有結果、退格等操作。從圖 2.2 中可以看出,計算器打開之后就開始監聽用戶的鼠標動作,如果輸入是關于計算結果或者 “MC”,”MR”,”MS”,”M+”,”Backspace”,”CE”,”C”等操作指令,而且沒有關閉計算器,就返回計算結果并顯示,如果不是,則不計算結果。接下來再繼續等待用戶的輸入。 本章的計算器并沒有復雜的流程,只需要簡單的操作,返回計算結果等。在實現計算器的過程中,我們需要注意的是,例如已經點擊了某個數字,再點擊運算符,那么程序需要記錄之前選點擊的數字,當用戶再次點擊運算符(非“=”)時,系統就需要將結果顯示到文本框中。因此在開發計算器的時候,我們需要注意用戶點擊的具體順序。 
2.3 建立計算器對象 實現一個計算器,我們需要建立一系列的對象來實現,例如,計算界面我們要建立一個界面類,還需要建立一個專門負責處理加、減、乘、除的基本計算類,還需要一個負責處理計算功能的業務類。本小節中只講解創建這三個基本的類,如果在開發的過程發現可以將一些行為或者屬性放置到一個新的對象中,那么可以再建立這些對象來完成需要實現的功能或者操作。 本章主要設計四個類來完成計算器的功能,界面類(CalFrame)—主要用來顯示計算器界面,功能類(CalService)—主要用于完成計算器中的邏輯功能,計算工具類(MyMath)—此類是工具類,用于處理大型數字的加減乘除,計算器類(Cal)—用于打開計算器,計算器中各個類的關系如圖 2.3 所示,從圖中可以看出,我們的界面類繼承了 java.swing.JFrame 類,計算器類使用了界面類,界面類使用了功能類,功能類使用了 MyMath 工具類,下面章節將對這些計算器的相關類作詳細介紹。
2.3.1 MyMath工具類 使用 float,double 兩種浮點基本類型來進行計算,容易損失精度,所以,我們使用一個自己定義了加,減,乘,除方法的類,此類使用 BigDecimal 來封裝基本類型,在不損失精度的同時,也可以進行超大數字的四則運算。為了方便調用,此類的方法全部都是靜態方法,可以直接用“類名.方法名”調用,這個類包含以下方法: (1)static double add( double num1, double num2 ),加法,使用來計算結果的數字是封裝后的num1 和 num2,并返回 double 類型。 (2)static double subtract ( double num1, double num2 ),減法,使用來計算結果的數字是封裝后的 num1 和 num2,并返回 double 類型。 (3)static double multiply ( double num1, double num2 ),乘法,使用來計算結果的數字是封裝后的 num1 和 num2,并返回 double 類型。 (4)static double divide ( double num1, double num2 ),除法,使用來計算結果的數字是封裝后的num1 和 num2,并返回 double 類型。 MyMath 類提供了基礎的四則運算方法,由于該類中所有的方法都是靜態的,因此外界可以直接調用。在實現 MyMath 的過程中需要注意的是,這幾個四則運算方法,參數都是 double 類型的,要進行運算的話,需要將 double 類型轉換成一個 BigDecimal 對象,我們可以使用以下代碼來創建一個BigDecimal 對象: new BigDecimal(String.valueOf(number)); 2.3.2 CalService類 CalService 類主要是用來處理計算器的業務邏輯,用戶在操作計算器時,此類將計算結果,并且返回,并且,會記錄計算器的狀態(用戶的上一步操作)。包含以下方法: (1)String callMethod( String cmd , String text ),調用方法并返回計算結果。 (2)String cal( String text , boolean isPercent ),用來計算加、減、乘、除法,并返回封裝成 String類型的結果。參數 text 是顯示框中的數字內容,boolean 類型的參數 isPercent 代表是否有”%”運算,如果有,便加上去。 (3)String setReciPRocal( String text ),用來計算倒數,并返回封裝成 String 類型的結果。 (4)String sqrt( String text ),用來計算開方,并返回封裝成 String 類型的結果。 (5)String setOp( String cmd , String text ),設置操作符號。 (6)String setNegative( String text ),設置正負數,當 text 是正數時,返回負數的數字字符串,反之,則返回正數的數字字符串。 (7)String catNum( String cmd, String text ),連接輸入的數字,每次點擊數字,就把把新加的數字追加到后面,并封裝成字符串返回。 (8)String backSpace( String text ),刪除最后一個字符,并返回結果。 (9)String mCmd( String cmd, String text ),用來實現”MC”,”MR”, “MS”,”M+”與存儲有關的功能。 (10)String clearAll(),清除所有計算結果。 (11)String clear( String text),清除上次計算結果。 CalService 類中的各個方法都是用于處理計算的邏輯,其中 callMethod 方法可以看作中一個中轉的方法,根據參數中的 cmd 值進行分發處理,例如調用該方法時將“CE”字符串作為 cmd,那么該方法就根據這個字符串再調用需要執行“CE”的方法。如果需要做更好的程序解耦,我們可以將這些做成一個狀態模式,將各個計算的方法都抽象成一個計算接口,該接口提供一個計算的方法,然后按照具體的情況,為該接口提供不同的實現,例如計算開方、計算倒數等實現,然后向 callMethod 傳入不同的實現類,直接調用接口方法。 2.3.3 CalFrame類 CalFrame 類繼承 javax.swing.Jframe 類,主要是用于計算器界面的實現,此類中,排版了計算器中各個組件的位置,為組件增加事件監聽器,用來監聽用戶的操作,并做調用相應的方法,主要包含以下方法: (1)void initialize(),初始化計算器界面。 (2)ActionListener getActionListener(),如果動作監聽器為空,則創建一個,并返回,如果不為空,直接返回。 (3)JTextField getTextField(),這個方法初始化輸入框。 (4)JButton[] getMButton(),此方法獲得計算器的存儲操作鍵。 (5)JButton[] getRButton(),此方法獲得計算器的結果操作鍵。 (6)JButton[] getNButton(),此方法獲得計算器的其它操作鍵。 由于 CalFrame 是界面類,因此所需要進行的業務處理并不多,更多的是監聽用戶的操作,并進行分發處理。這就有點像 web 應用中的 MVC 模式中的 V(視圖),并不處理任務的業務邏輯,主要職責是顯示相應的數據。在本章中,CalFrame 包括了一些監聽器,監聽界面事件并調用相關的業務方法,在實際開發中,我們可以將這些監聽器作為 MVC 模式中的 C(控制器)提取到另外的類中。
2.4 MyMath工具類實現 MyMath 是一個工具類,主要用于處理加、減、乘、除四則運算,我們已經確定了實現這四個方法的時候,都使用 BigDecimal 對象進行計算。由于我們定義 MyMath 方法的時候,所有方法的參數都是double類型的,因此我們可以提供一個工具方法來將double 轉換成 BigDecimal 類型。 以下代碼根據一個 double 類型轉換成一個 BigDecimal。
/*** 為一個 double 類型的數字創建 BigDecimal 對象* @param number* @return*/private static BigDecimal getBigDecimal(double number) {return new BigDecimal(number);}提供了這個工具方法后,我們可以在其他的計算方法中使用這個工具方法,選擇將 double 的參數轉換成 BigDecimal 對象,然后再進行具體的運算。 2.4.1 實現四則運算
//加法 public static double add(double num1,double num2){ BigDecimal first=getBigDeciaml(num1); BigDecimal second=getBigDeciaml(num2); return first.add(second).doubleValue(); } //減法 public static double subtract(double num1,double num2){ BigDecimal first=getBigDeciaml(num1); BigDecimal second=getBigDeciaml(num2); return first.subtract(second).doubleValue(); } //乘法 public static double multiply(double num1,double num2){ BigDecimal first=getBigDeciaml(num1); BigDecimal second=getBigDeciaml(num2); return first.multiply(second).doubleValue(); } //除法 public static double divide(double num1,double num2){ BigDecimal first=getBigDeciaml(num1); BigDecimal second=getBigDeciaml(num2); return first.divide(second).doubleValue(); }運算,除本章介紹的方法我,讀者可以查閱 Java 的 API 來學習該類的詳細使用。
2.5 計算器主界面 這里實現計算器的界面,是用java的Swing實現的,主要用到的類有javax.swing.JFrame(窗口),javax.swing.JButton(按鈕),javax.swing.JTextField(輸入框),并使用 java.awt.BorderLayout 和java.awt.GridLayout 進行布局。在這里,我們使用“自下而下”的方法去觀察此類,先看總體的排版實現,再看各個小組件的實現。為了方便布局,我們按相近的外觀把計算器分為四個部分,見圖 2.4:
2.5.1 初始化界面(initialize()方法) 此類就是一個 JFrame(繼承了 javax.swing.JFrame),用來做其它窗口或者組件的父容器,初始化計算器窗口的大概流程: (1)設置父窗口 JFrame 標題、布局管理器、是否可以改變等屬性。 (2)增加輸入與計算結果顯示框。對應圖 2.4 中的左上角那部分。 (3)增加左邊存儲操作鍵。 (4)增加結果操作鍵。 (5)增加數字與其它運算符。 由于按外觀相近的方式把組件分成了四部分,就方便程序中對相同屬性的組件統一地創建與設置屬性,對于界面的布局也更加地直觀與方便,觀察此圖,我們可以使用 BorderLayout 做總體布局,如圖2.5 所示。
以下代碼設置父窗口 JFrame 標題和設置是否可以改變大小的屬性。
//增加計算輸入框 TextField t=new TextField();
增加左邊存儲操作鍵,本類需要通過 getMButton()方法獲取一個保存 JButton 對象的數組,getMButton 方法我們將在 2.5.2 中實現。獲取數組后,遍歷數組,并把數組中的元素加到一個新建的JPanel 中,最后再把這個 JPanel 加到 JFrame 的相應位置://增加左邊存儲操作鍵 JButton[] mButton = getMButton(); //新建一個 panel,用于放置按鈕 JPanel panel1 = new JPanel(); //設置布局管理器 panel1.setLayout( new GridLayout( 5, 1, 0, 5 ) ); //迭代增加按鈕 for( JButton b : mButton ) panel1.add(b);
增加結果操作鍵,這些結果操作鍵包括:Back,CE,C。通過本類的 getRButton()方法獲取一個保存 JButton 對象的數組,獲取數組后,遍歷數組,并把數組中的元素加到一個新建的 JPanel 中,最后再把這個 JPanel 加到 JFrame 相應的位置,具體實現的代碼如下://增加結果操作鍵 JButton [] rButton=getRButton(); //新建一個panel類,用于放置按鈕 JPanel panel2=new JPanel(); //設置布局管理器 panel2.setLayout(new GridLayout(1,3)); //迭代增加按鈕 for(JButton b : rButton) panel2.add(b);
接下來將其他的按鍵加入到界面的 JPanel 對象中,這些操作鍵主要包括數字鍵和其他的一些運算鍵,我們同樣的通過一個 getNButton 方法來返回這些操作鍵對應的 JButton 對象,最后將這些 JButton對象加入到相應的 JPanel 中,加入到 JPanel 并設置相應布局的代碼如下:JButton [] nButton=getNButton(); //新建一個panel類,用于放置按鈕 JPanel panel3=new JPanel(); //設置布局管理器 panel3.setLayout(new GridLayout(4,5)); //迭代增加按鈕 for(JButton b : nButton) panel3.add(b);
最后整體布局: //計算器界面 JPanel panel=new JPanel(); panel.setLayout( new BorderLayout(1, 5)); panel.add(panel2,BorderLayout.NORTH); panel.add(panel3, BorderLayout.CENTER); getContentPane().add(t,BorderLayout.NORTH); getContentPane().add(panel1,BorderLayout.WEST); getContentPane().add(panel,BorderLayout.CENTER); setDefaultCloSEOperation(JFrame.EXIT_ON_CLOSE); //讓窗體居中顯示 setLocationRelativeTo(null); 在本小節中,我們通過 getMButton、getRButton 和 getNButton 方法來返回不同的 JButton 數組,然后再對這些數組進行遍歷,將每一個 JButton 加入到界面中。這一個返回 JButton 數組的方法并沒有實現,下面將介紹如何實現這三個方法。2.5.2 創建運算鍵 運算鍵主要包括數字鍵與基本運算鍵,數字鍵從 0 到 9,基本運算鍵包括開方、正負、小數點等鍵,主要實現計算器界面的 getNButton 方法即可。以下是該方法的實現。//創建運算按鍵 private JButton [] getNButton(){ //這個數組保存需要設置為紅色的操作符 String [] redButton={“/”,”*”,”-“,”+”,”=”}; JButton [] result=new JButton[nOp.length]; for(int i=0;i
以上代碼需要注意的是,我們需要提供一個紅色按鍵的字符串數組,在遍歷所有的需要創建的按鍵數組時,就需要作判斷,如果按鍵數組里面存在紅色按鍵數組的某個元素,就需要調用 JButton 的setForeground 方法來設置該按鈕的字體顏色。在代碼中我們不能看到該方法幫我們創建了哪些按鍵,代碼中使用了一個 nOp 的字符串數組來保存需要創建的按鍵,該數組包含的內容如下:private String[] nOp = { “7”, “8”, “9”, “/”, “sqrt”, “4”, “5”, “6”, “*”,”%”, “1”, “2”, “3”, “-“, “1/x”, “0”, “+/-“, “.”, “+”, “=” };
2.5.3 創建操作按鍵 操作按鍵的創建與運算鍵的創建基本一致,只是所有的按鍵的字體都必須是紅色的,創建操作按鈕,我們需要實現 getMButton 和 getRButton 方法,以下是這兩個方法的具體實現。//創建操作按鍵 private JButton [] getMButton(){ JButton [] result=new JButton[mOp.length+1]; result[0]=new JButton(“”); for(int i=0;i
getMButton 創建的是界面左側的操作鍵,getRButton 創建的是運作鍵上面的操作鍵,getMButton和 getRButton 創建的操作鍵如下://getMButton private String[] mOp = { “MC”, “MR”, “MS”, “M+” }; //getRButton private String[] rOp = { “Back”, “CE”, “C” };
創建完界面元素后,我們可以運行計算器,具體的效果如圖 2.4 所示。2.5.4 增加事件監聽器 在上一節中,我們注意到程序為 JButton 類型的組件增加了事件監聽器,這個事件監聽器是用來響應用戶的鼠標操作。我們使用 java.awt.event.ActionListener 接口來創建一個事件監聽器,主要是實現接口中的 actionPerformed( ActionEvent e )方法,當監聽器監聽到用戶的操作時,會自動調用此方法,并在此方法中處理業務邏輯,再把數據返回顯示給用戶。見以下代碼。//增加事件監聽器 ActionListener actionListener=new ActionListener(){ CalService service=new CalService(); //監聽鼠標點擊按鈕的方法 public void actionPerformed(ActionEvent e){ String cmd=e.getActionCommand();//獲取鼠標點擊的按鈕的內容 String result=null; try{ //計算操作結果 result=service.callMethod(cmd,t.getText()); } catch(Exception e1){ System.out.println(e1.getMessage()); } //處理button標記 if(cmd.indexOf(“MC”)==0){t.setText(“”);} else if(cmd.indexOf(“M”)==0 && service.store>0){t.setText(“M”);} if(cmd.equals(“C”)){t.setText(“0”);} if(cmd.equals(“CE”)){t.setText(“”);} if(cmd.equals(“Back”)){if(t.getText().equals(“”) || t.getText().equals(“0”)) {t.setText(“0”);} else { String s=t.getText(); t.setText(s.substring(0,s.length()-1)); }} if(cmd.indexOf(“+”)==0 || cmd.indexOf(“-“)==0 || cmd.indexOf(“*”)==0 || cmd.indexOf(“/”)==0) {t.setText(t.getText()+cmd);} //設置計算結果 if(result!=null){t.setText(result);} } };
從 上 面 代 碼 中 可 以 看 到 , 這 里 是 通 過 實 現 java.awt.event.ActionListener接口中的actionPerformed(ActionEvente)方法去創建一個java.awt.event.ActionListener 類型的內部類,并在actionPerformed 方法中處理業務邏輯。首先,調用 CalService 實例中的 callMethod 方法去處理計算,并把結果返回。result = service.callMethod( cmd, textField.getText() );
再設置標志存儲結果類型的存儲標記,如果是點擊“MC”按鈕,就把標記設置為空,如果是點擊“MS”,“MR”,“M+”,并且存儲結果大于 0,就把標記設置為“M”,這里弄不明白的讀者,可以先試著使用一下 windows 計算器的這幾個按鈕,再看這里就很容易理解了。if( cmd.indexOf(“MC”) == 0 ) { button.setText(“”); } else if( cmd.indexOf(“M”) == 0 && service.getStore() > 0 ) { button.setText(“M”); }
最后把計算結果設置到結果文本顯示框中,顯示給使用者。if( result != null ) { textField.setText( result ); }
在監聽器中,我們調用了 CalServer 的 callMethod 方法來取得操作的結果,換言之,界面中的每次點擊都會執行該方法,callMethod 我們并沒有提供任何實現,在下一小節,我們將實現該方法。2.6 計算業務處理 在 2.3 章節中,我們建立了一個類名為 CalService 的類來處理計算器的計算業務,該類處理了整個應用中的大部分業務,其中包括數字運算,存儲運算,操作結果等業務。有四個重要的屬性:firstNum代表第一個操作數,secondNum 代表第二個操作數,lastOp 代表上次用戶所做的操作, isSecondNum代表是否第二個操作數。2.6.1 計算四則運算結果 在使用計算器計算加、減、乘、除法的過程中,正常的情況應該是用戶先輸入第一個操作數,再點擊加、減、乘、除計算符號,再輸入第二個操作數,最后點“=”號計算出結果,所以這時用 firstNum去保存用戶輸入的第一個操作數,secondNum 去保存第二個操作數,lastOp 去保存計算符號或者其它操作,isSecondNum 用來判斷用戶是在輸入第幾個操作數。 在用戶輸入數字的時候(包括“0123456789.”),首先判斷是第一個操作數還是第二個,如果是第一個,就把用戶新輸入的數字追加到原來數字的后面,并做為結果返回;如果是第二個,直接返回結果,并把 isSecondNum 標志為 false,用戶繼續輸入數字的時候,就把數字追加到原來數字的后面做為結果返回,見以下代碼。//設置文本框中內容 public String catNum(String cmd,String text){ String result=cmd; //如果目前的text不等于0 if(!text.equals(“0”)){ if(isSecondNum){ //將isSecondNum標記為false isSecondNum=false; } else{ //則返回結果為text加上新點擊的數字 result=text+cmd; } } //如果有.開頭,則在前面補0 if(result.indexOf(“.”)==0){ result=”0”+result; } return result; }
當用戶點擊“+-*/”(四則運算)的時候,就把 lastOp 設置為其中一個符號,這個變量用來記錄用戶正要進行計算的類型,見以下代碼。//設置符號lastOp public String setOp(String cmd,String text){ //將此操作符設置為上一次的操作 this.lastOp=cmd; //設置第一個操作數的值 this.firstNum=text; //將第二個人操作數賦值為空 this.secondNum=null; //將isSecondNum標記為true isSecondNum=true; //返回空值 return null; }
在上面的代碼中,可以看到,除了設置 lastOp 外,還把輸入的數字設置給 firstNum,把 secondNum設置為空,并把 isSecondNum 設置為 true,代表下次輸入數字時,要清空輸入框并重新輸入。最后用戶點擊“=”號時,就是程序計算出最后結果的時候,此類的 String cal( String text,boolean isPercent )方法實現了此計算,注意到這個方法的第二個參數 isPercent,這是計算器的“%”號操作,如果有這種操作,第二個操作數就等于兩數相乘再除以100,請看以下代碼。//四則運算 public String cal(String text,boolean isPercent) throws Exception{ //初始化第二個操作數 this.secondNum=text; double secondResult=Double.valueOf(secondNum); //如果除數為0則不處理 if(secondNum.equals(“0”) && this.lastOp.equals(“/”)){return “0”;} //如果有%操作,則第二個操作數等于兩數相乘在除以100 if(isPercent){ secondResult=MyMath.multiply(Double.valueOf(firstNum),MyMath.divide(Double.valueOf(secondNum),100)); } //四則運算,返回結果賦給第一個操作數 if(this.lastOp.equals(“++”)){ firstNum=String.valueOf(MyMath.add(Double.valueOf(firstNum),secondResult)); } else if(this.lastOp.equals(“-“)){ firstNum=String.valueOf(MyMath.subtract(Double.valueOf(firstNum),secondResult)); } else if(this.lastOp.equals(“*”)){ firstNum=String.valueOf(MyMath.multiply(Double.valueOf(firstNum),secondResult)); } else if(this.lastOp.equals(“/”)){ double num=Double.valueOf(firstNum)/secondResult; //double num=MyMath.divide(Double.valueOf(firstNum),secondResul); firstNum=String.valueOf(num); } //給第二個操作數重新賦值 secondNum=null; //把isSecondNum標記為true this.isSecondNum=true; return firstNum; }
上面計算結果中,經歷了幾個步驟,首先,確定 secondNum 的值,如果 secondNum 為空,secondNum就等于最后輸入的數字,如果不為空,則等于原來的值,如果有"%"號操作,則 secondNum再等于兩數相乘除以 100 的結果;然后根據 lastOp 的值(+、-、*、/)去調用 MyMath 類中的 add、subtract、multiply、divide 方法,并把返回的結果保存到 firstNum;最后把 secondNum 設置為空,把 firstNum 當做結果返回。2.6.2 存儲操作 定義一個 double 類型的屬性store來充當存儲器,在用戶點擊“MC(清除)”、“M+(累加)”、“MR(讀取)”、“MS(保存)”操作時,就調用此方法,再根據得到的字符串去進行清除、累加、讀取、保存操作,見以下代碼。public String mCmd(String cmd,String text){ if(cmd.equals("M+")){ //如果是M+操作,就把計算結果累積到store中 store=MyMath.add(store,Double.valueOf(text)); } else if(cmd.equals("MC")){ //如果是MC操作,則清除store store=0; } else if(cmd.equals("MR")){ //如果是MR操作,則把store的值讀出來 isSecondNum=true; return String.valueOf(store); } else if(cmd.equals("MS")){ //如果是MS操作,則把計算結果保存到store store=Double.valueOf(text).doubleValue(); } return null;}
程序中提供了一個 store 的屬性用來保存計算結果,當用戶點擊了“M+”時,就將結果加到 store中,點擊了“MC”時,就將 store 設置為 0,點擊了“MR”,則將 store 的值讀取,點擊了“MS”,則將 store 設置為當前的結果。2.6.3 實現開方、倒數等 開方與倒數的計算實現都比較簡單,開方是直接使用 Math 類的 sqrt 方法去計算接收到的數值,并且返回結果:public String sqrt(String text){ //將isSecondNum標記為true this.isSecondNum=true; //計算結果并返回 return String.valueOf(Math.sqrt(Double.valueOf(text))); }
倒數是調用 MyMath 的 divide 方法去計算 1 與接收到的數值相除的值。public String setReciprocal(String text){ //如果text為0,則不求倒數 if(text.equals(“0”)){ return text; } else{ //將isSecondNum標記為true this.isSecondNum=true; //計算結果并返回 return String.valueOf(1/Double.valueOf(text)); } } public String setNegative(String text){ double text1=Double.valueOf(text); text1=-text1; return String.valueOf(text1); }
2.6.4 實現倒退操作 當我們的程序中得到用戶在界面輸入的相關數字時,如果用戶進行了倒退操作,我們可以將用戶輸入的數字進行截取,如果接收到的字符串是“0”或者為 null,則不作任何操作,直接返回,否則,我們將使用 String 的 substring 方法進行處理,將輸入字符串的最后一位截取。以下方法實現倒退操作。public String backSpace(String text){ return text.equals(“0”) || text.equals(“”)?”0”:text.substring(0,text.length()-1); }
2.6.5 清除計算結果 清除所有計算結果,把 firstNum 與 secondNum 都設置為原始值,并返回 firstNum,在 CalService中提供了一個 clearAll 方法,用于清除所有的計算結果。public String clearAll(){ //將第一,第二操作數恢復為默認值 this.firstNum=”0”; this.secondNum=null; return this.firstNum; } public String clear(String text){ text=null; return text; }
2.6.6 實現中轉方法(callMethod) 在前面的章節中,我們已經實現了各個方法,例如四則運算、開方、倒數、清除計算等,但是在界面的監聽器中,只會調用 CalService 的 callMethod 方法進行運算,因此我們需要對 callMethod 進行相關的實現。public String callMethod(String cmd,String text) throws Exception{ if(mString.indexOf(cmd)!=-1){ return mCmd(cmd,text); } else if(cmd.equals(“C”)){ return clearAll(); } else if(cmd.equals(“CE”)){ return clear(text); } else if(cmd.equals(“BACK”)){ return backSpace(“text”); } else if(numString.indexOf(cmd)!=-1){ return catNum(cmd,text); } else if(opString.indexOf(cmd)!=-1){ return setOp(cmd,text); } else if(cmd.equals(“=”)){ return cal(text,false); } else if(cmd.equals(“+/-“)){ return setNegative(text); } else if(cmd.equals(“1/x”)){ return setReciprocal(text); } else if(cmd.equals(“sqrt”)){ return sqrt(text); } else if(cmd.equals(“%”)){ return cal(text,true); } else{ return mCmd(cmd,text); } }
2.7 總結 完整代碼如下: CalFrame類:import java.util.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.*;
public class CalFrame extends JFrame { private static final long serialVersionUID=12L;//版本,必須要寫 private String[] nOp={“7”,”8”,”9”,”/”,”sqrt”,”4”,”5”,”6”,”*”, “%”,”1”,”2”,”3”,”-“,”1/x”,”0”,”+/-“,”.”,”++”,”=”}; private String[] mOp={“MC”,”MR”,”MS”,”M+”}; private String[] rOp = {“Back”,”CE”,”C”}; //定義文本框 TextField t=new TextField();//JTextArea t=new JTextArea();
public CalFrame(){ //設置窗口的標題 setTitle("計算器"); //設置大小 setSize(350,250); //設置為不可改變大小 setResizable(false); //定義窗體布局為邊界布局 setLayout(new BorderLayout());} //初始化界面 private void initialize(){ /** *增加左邊存儲操作鍵 */ JButton [] mButton=getMButton(); //新建一個panel類,用于放置按鈕 JPanel panel1=new JPanel(); //設置布局管理器 panel1.setLayout(new GridLayout(5,1)); //迭代增加按鈕 for(JButton b : mButton) panel1.add(b); /** *增加結果操作鍵 */ JButton [] rButton=getRButton(); //新建一個panel類,用于放置按鈕 JPanel panel2=new JPanel(); //設置布局管理器 panel2.setLayout(new GridLayout(1,3)); //迭代增加按鈕 for(JButton b : rButton) panel2.add(b); /** *增加數字與其他運算符操作鍵 */ JButton [] nButton=getNButton(); //新建一個panel類,用于放置按鈕 JPanel panel3=new JPanel(); //設置布局管理器 panel3.setLayout(new GridLayout(4,5)); //迭代增加按鈕 for(JButton b : nButton) panel3.add(b); //計算器界面 JPanel panel=new JPanel(); panel.setLayout( new BorderLayout(1, 5)); panel.add(panel2,BorderLayout.NORTH); panel.add(panel3, BorderLayout.CENTER); getContentPane().add(t,BorderLayout.NORTH); getContentPane().add(panel1,BorderLayout.WEST); getContentPane().add(panel,BorderLayout.CENTER); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//可要可不要 //讓窗體居中顯示 setLocationRelativeTo(null); } //創建運算按鍵 private JButton [] getNButton(){ //這個數組保存需要設置為紅色的操作符 String [] redButton={"/","*","-","++","="}; JButton [] result=new JButton[nOp.length]; for(int i=0;i<this.nOp.length;i++){ //新建按鈕 JButton b=new JButton(this.nOp[i]); //為按鈕增加事件 b.addActionListener(actionListener); //對redButton排序,才可以使用binarySearch方法 Arrays.sort(redButton); //如果操作符在redButton出現 if(Arrays.binarySearch(redButton,nOp[i])>=0){ b.setForeground(Color.red); } else{ b.setForeground(Color.blue); } result[i]=b; } return result; } //創建操作按鍵 private JButton [] getMButton(){ JButton [] result=new JButton[mOp.length+1]; result[0]=new JButton(""); for(int i=0;i<this.mOp.length;i++){ //新建按鈕 JButton b=new JButton(this.mOp[i]); //為按鈕增加事件 b.addActionListener(actionListener); //設置按鈕顏色 b.setForeground(Color.red); result[i+1]=b; } return result; } //創建其他按鈕 private JButton [] getRButton(){ JButton [] result=new JButton[rOp.length]; for(int i=0;i<this.rOp.length;i++){ //新建按鈕 JButton b=new JButton(this.rOp[i]); //為按鈕增加事件 b.addActionListener(actionListener); //設置按鈕顏色 b.setForeground(Color.red); result[i]=b; } return result; } //增加事件監聽器 ActionListener actionListener=new ActionListener(){ CalService service=new CalService(); //監聽鼠標點擊按鈕的方法 public void actionPerformed(ActionEvent e){ String cmd=e.getActionCommand();//獲取鼠標點擊的按鈕的內容 String result=null; try{ //計算操作結果 result=service.callMethod(cmd,t.getText()); } catch(Exception e1){ System.out.println(e1.getMessage()); } //處理button標記 if(cmd.indexOf("MC")==0){t.setText("");} else if(cmd.indexOf("M")==0 && service.store>0){t.setText("M");} if(cmd.equals("C")){t.setText("0");} if(cmd.equals("CE")){t.setText("");} if(cmd.equals("Back")){if(t.getText().equals("") || t.getText().equals("0")) {t.setText("0");} else { String s=t.getText(); t.setText(s.substring(0,s.length()-1)); }} if(cmd.indexOf("++")==0 || cmd.indexOf("-")==0 || cmd.indexOf("*")==0 || cmd.indexOf("/")==0) {t.setText(t.getText()+cmd);} //設置計算結果 if(result!=null){t.setText(result);} } }; public static void main(String[] args) throws Exception{ CalFrame calculator=new CalFrame (); calculator.initialize(); calculator.setVisible(true); }}
CalService類:public class CalService { String lastOp=null; String firstNum=null; String secondNum=null; String opString=”++-*/”; String numString=”0123456789.”; String mString=”MCM+MRMS”; boolean isSecondNum=false; double store=0;
/** *計算四則運算結果 *///設置文本框中內容public String catNum(String cmd,String text){ String result=cmd; //如果目前的text不等于0 if(!text.equals("0")){ if(isSecondNum){ //將isSecondNum標記為false isSecondNum=false; } else{ //則返回結果為text加上新點擊的數字 result=text+cmd; }} //如果有.開頭,則在前面補0 if(result.indexOf(".")==0){ result="0"+result; } return result;} //設置符號lastOp public String setOp(String cmd,String text){ //將此操作符設置為上一次的操作 this.lastOp=cmd; //設置第一個操作數的值 this.firstNum=text; //將第二個人操作數賦值為空 this.secondNum=null; //將isSecondNum標記為true isSecondNum=true; //返回空值 return null; } //四則運算 public String cal(String text,boolean isPercent) throws Exception{ //初始化第二個操作數 this.secondNum=text; double secondResult=Double.valueOf(secondNum); //如果除數為0則不處理 if(secondNum.equals(“0”) && this.lastOp.equals(“/”)){return “0”;} //如果有%操作,則第二個操作數等于兩數相乘在除以100 if(isPercent){ secondResult=MyMath.multiply(Double.valueOf(firstNum),MyMath.divide(Double.valueOf(secondNum),100)); } //四則運算,返回結果賦給第一個操作數 if(this.lastOp.equals(“++”)){ firstNum=String.valueOf(MyMath.add(Double.valueOf(firstNum),secondResult)); } else if(this.lastOp.equals(“-“)){ firstNum=String.valueOf(MyMath.subtract(Double.valueOf(firstNum),secondResult)); } else if(this.lastOp.equals(“*”)){ firstNum=String.valueOf(MyMath.multiply(Double.valueOf(firstNum),secondResult)); } else if(this.lastOp.equals(“/”)){ double num=Double.valueOf(firstNum)/secondResult; //double num=MyMath.divide(Double.valueOf(firstNum),secondResul); firstNum=String.valueOf(num); } //給第二個操作數重新賦值 secondNum=null; //把isSecondNum標記為true this.isSecondNum=true; return firstNum; }
/** *存儲操作數 */public String mCmd(String cmd,String text){ if(cmd.equals("M+")){ //如果是M+操作,就把計算結果累積到store中 store=MyMath.add(store,Double.valueOf(text)); } else if(cmd.equals("MC")){ //如果是MC操作,則清除store store=0; } else if(cmd.equals("MR")){ //如果是MR操作,則把store的值讀出來 isSecondNum=true; return String.valueOf(store); } else if(cmd.equals("MS")){ //如果是MS操作,則把計算結果保存到store store=Double.valueOf(text).doubleValue(); } return null;}
/** *實現開方,倒數,取反等 */public String sqrt(String text){ //將isSecondNum標記為true this.isSecondNum=true; //計算結果并返回 return String.valueOf(Math.sqrt(Double.valueOf(text)));}public String setReciprocal(String text){ //如果text為0,則不求倒數 if(text.equals("0")){ return text; } else{ //將isSecondNum標記為true this.isSecondNum=true; //計算結果并返回 return String.valueOf(1/Double.valueOf(text)); } } public String setNegative(String text){ double text1=Double.valueOf(text); text1=-text1; return String.valueOf(text1); }/** *實現倒退操作 */public String backSpace(String text){ return text.equals("0") || text.equals("")?"0":text.substring(0,text.length()-1);}/** *清除計算結果 */public String clearAll(){ //將第一,第二操作數恢復為默認值 this.firstNum="0"; this.secondNum=null; return this.firstNum;}public String clear(String text){ text=null; return text;} /** *實現中轉方法(callMethod) */public String callMethod(String cmd,String text) throws Exception{ if(mString.indexOf(cmd)!=-1){ return mCmd(cmd,text);} else if(cmd.equals("C")){ return clearAll();} else if(cmd.equals("CE")){ return clear(text);} else if(cmd.equals("BACK")){ return backSpace("text");} else if(numString.indexOf(cmd)!=-1){ return catNum(cmd,text);} else if(opString.indexOf(cmd)!=-1){ return setOp(cmd,text);} else if(cmd.equals("=")){ return cal(text,false);} else if(cmd.equals("+/-")){ return setNegative(text);} else if(cmd.equals("1/x")){ return setReciprocal(text);} else if(cmd.equals("sqrt")){ return sqrt(text);} else if(cmd.equals("%")){ return cal(text,true);} else{ return mCmd(cmd,text);}}
}
MyMath類:import java.math.*; public class MyMath { /** *為一個double類型的數據創建BigDecimal對象 *@param number *@return */ private static BigDecimal getBigDeciaml(double number){ return new BigDecimal(number); } //加法 public static double add(double num1,double num2){ BigDecimal first=getBigDeciaml(num1); BigDecimal second=getBigDeciaml(num2); return first.add(second).doubleValue(); } //減法 public static double subtract(double num1,double num2){ BigDecimal first=getBigDeciaml(num1); BigDecimal second=getBigDeciaml(num2); return first.subtract(second).doubleValue(); } //乘法 public static double multiply(double num1,double num2){ BigDecimal first=getBigDeciaml(num1); BigDecimal second=getBigDeciaml(num2); return first.multiply(second).doubleValue(); } //除法 public static double divide(double num1,double num2){ BigDecimal first=getBigDeciaml(num1); BigDecimal second=getBigDeciaml(num2); return first.divide(second).doubleValue(); } }
“` 將上述三個類放在同一個包calculator下即是完成仿Windows計算器的完整代碼。
新聞熱點
疑難解答