- Android Studio開發(fā)實(shí)戰(zhàn):從零基礎(chǔ)到App上線 (移動(dòng)開發(fā)叢書)
- 歐陽燊
- 2149字
- 2020-11-28 17:31:39
2.5 實(shí)戰(zhàn)項(xiàng)目:簡單計(jì)算器
到目前為止,雖然只學(xué)了一些Android的初級控件,但是也可以學(xué)以致用,即便只有這些簡單的布局和控件,也能夠做出實(shí)用的App。接下來我們設(shè)計(jì)并實(shí)現(xiàn)一個(gè)簡單計(jì)算器。
2.5.1 設(shè)計(jì)思路
計(jì)算器是人們?nèi)粘I钪凶畛S玫墓ぞ咧唬瑹o論在電腦上還是手機(jī)上,都少不了計(jì)算器的身影。以Windwos上的計(jì)算器為例,界面簡潔且十分實(shí)用,程序界面如圖2-31所示。

圖2-31 Windows的計(jì)算器
這個(gè)計(jì)算器界面主要分為兩部分,一部分是上面的文本框,用于顯示計(jì)算結(jié)果;另一部分是下面的幾排按鈕,用于輸入數(shù)字與各種運(yùn)算符。為了減少復(fù)雜度,我們可以精簡一些功能,只保留數(shù)字與加、減、乘、除四則運(yùn)算,另外補(bǔ)充一個(gè)開根號(hào)(求平方根)的運(yùn)算。至于App的顯示界面,基本與習(xí)慣的計(jì)算器界面保持一致,經(jīng)過對操作按鈕的適當(dāng)排列,調(diào)整后的設(shè)計(jì)效果如圖2-32所示。

圖2-32 簡單計(jì)算器的設(shè)計(jì)效果圖
這個(gè)計(jì)算器雖然小巧,但是基本囊括了本章的知識(shí)點(diǎn),先來看看用了哪些控件。
● 線性布局LinearLayout:計(jì)算器界面整體上是從上往下布局的,所以需要垂直方向的LinearLayout;下面部分每行都有4個(gè)按鈕,又需要水平方向的LinearLayout。
● 滾動(dòng)視圖ScrollView:雖然計(jì)算器界面不寬也不高,但是以防萬一,最好還是加個(gè)垂直方向的ScrollView。
● 文本視圖TextView:很明顯上方標(biāo)題“簡單計(jì)算器”就是TextView,下面的計(jì)算結(jié)果也需要使用TextView,而且是能夠自動(dòng)從下往上滾動(dòng)的TextView,即聊天室效果的文本視圖。
● 按鈕Button:絕大多數(shù)數(shù)字與運(yùn)算符按鈕都采用Button控件。
● 圖像視圖ImageView:暫時(shí)未用到。
● 圖像按鈕ImageButton:開根號(hào)的運(yùn)算符“√”雖然能夠打出來,但是右上角少了數(shù)學(xué)課本上的一橫,所以該按鈕要用一張標(biāo)準(zhǔn)的開根號(hào)圖片顯示,這就用到了ImageButton。
● 狀態(tài)列表圖形:每個(gè)按鈕都有按下和彈起兩種狀態(tài),這里定制了按鈕控件的自定義樣式,因此用到了狀態(tài)列表圖形。
● 形狀圖形:運(yùn)算結(jié)果用到的文本視圖邊框是圓角矩形,所以得給它定義一個(gè)shape文件,把shape定義的圓角矩形作為文本視圖的背景。
● 九宮格圖片:注意計(jì)算器界面左下角的“0”,該按鈕是其他按鈕的兩倍寬,如果使用普通圖片當(dāng)背景,勢必造成邊緣線被拉寬、拉模糊的問題,故而要采用點(diǎn)九圖片避免這種情況。
經(jīng)過對計(jì)算器效果圖的詳細(xì)分析,我們初步了解了所運(yùn)用的控件技術(shù),接下來就可以對界面進(jìn)行布局和排列了。
2.5.2 小知識(shí):日志Log/提示Toast
在正式編碼之前,讀者有必要了解一下Android中的運(yùn)行信息調(diào)試手段。例如,開發(fā)C程序時(shí),我們常常用printf函數(shù)輸出程序日志;開發(fā)Java程序時(shí),我們常常用System.out.println函數(shù)輸出程序日志。同樣,App開發(fā)也有相應(yīng)的函數(shù)輸出提示信息。提示信息可分為兩類,一類是給開發(fā)者看的,另一類是給用戶看的。
1. Log
給開發(fā)者看的提示信息要調(diào)用Log類的相應(yīng)方法,日志打印結(jié)果可在Android Studio界面下方的logcat小窗口查看。Log類各種方法的區(qū)別在于日志的等級,具體說明如下。
● Log.e:表示錯(cuò)誤信息,比如可能導(dǎo)致程序崩潰的異常。
● Log.w:表示警告信息。
● Log.i:表示一般消息。
● Log.d:表示調(diào)試信息,可把程序運(yùn)行時(shí)的變量值打印出來,方便跟蹤調(diào)試。
● Log.v:表示冗余信息。
2. Toast
給用戶看的提示信息要調(diào)用Toast類的相應(yīng)方法,提示文字會(huì)在屏幕下方以一個(gè)小窗口臨時(shí)展現(xiàn)。對于計(jì)算器來說,有好幾種情況需要提示用戶,如“被除數(shù)不能為0”“開根號(hào)的數(shù)值不能小于0”等。
Toast的簡單用法只需一行代碼就可以了,示例代碼如下:
Toast.makeText(MainActivity.this, "提示文字", Toast.LENGTH_SHORT).show();
另外,計(jì)算器每個(gè)按鈕的展示風(fēng)格基本相同,為了減少冗余代碼,可將相同的樣式定義寫在values目錄下的styles.xml文件中,然后在布局文件節(jié)點(diǎn)下增加style="@style/btn_cal"這樣的屬性定義。下面是styles.xml中計(jì)算器按鈕風(fēng)格定義的例子:
<style name="btn_cal"> <item name="android:layout_width">0dp</item> <item name="android:layout_height">match_parent</item> <item name="android:layout_weight">1</item> <item name="android:gravity">center</item> <item name="android:textColor">@color/black</item> <item name="android:textSize">30sp</item> <item name="android:background">@drawable/btn_nine_selector</item> </style>
2.5.3 代碼示例
看到這里,估計(jì)讀者對計(jì)算器App的布局和代碼框架都了然于胸了,接下來介紹一些業(yè)務(wù)邏輯判斷與基本的數(shù)學(xué)四則運(yùn)算。只要設(shè)計(jì)充分并且合理,編碼就會(huì)很快。計(jì)算器App運(yùn)行后的計(jì)算效果如圖2-33所示。

圖2-33 簡單計(jì)算器的運(yùn)行效果圖
編碼過程主要分為3個(gè)步驟:
步驟01 先想好代碼文件與布局文件的名稱,比如代碼文件取名CalculatorActivity.java、布局文件取名activity_calculator.xml。記得在AndroidManifest.xml中注冊acitivity節(jié)點(diǎn),不然App運(yùn)行時(shí)會(huì)報(bào)ActivityNotFoundException異常,具體是在application節(jié)點(diǎn)下補(bǔ)充一行聲明:
<activity android:name=".CalculatorActivity" />
步驟02 在res/layout目錄下創(chuàng)建布局文件activity_calculator.xml,按照簡單計(jì)算器的效果圖在里面填入各控件的布局結(jié)構(gòu),并指定相關(guān)的屬性定義。
步驟03 在項(xiàng)目的包名目錄下創(chuàng)建CalculatorActivity類,仿照MainActivity代碼在onCreate內(nèi)部的setContentView方法中填入?yún)?shù)R.layout.activity_calculator,表示該頁面使用activity_calculator.xml中定義的界面布局。接著編寫具體的控件操作與業(yè)務(wù)代碼。
下面是計(jì)算器App的主要業(yè)務(wù)代碼片段:
public void onClick(View v) { int resid = v.getId(); String inputText; if (resid == R.id.ib_sqrt) { inputText = "√"; } else { inputText = ((TextView) v).getText().toString(); } Log.d(TAG, "resid="+resid+", inputText="+inputText); if (resid == R.id.btn_clear) { clear(""); } else if (resid == R.id.btn_cancel) { if (operator.equals("") == true) { if (firstNum.length() == 1) { firstNum = "0"; } else if (firstNum.length() > 0) { firstNum = firstNum.substring(0, firstNum.length() -1); } else { Toast.makeText(this, "沒有可取消的數(shù)字了", Toast.LENGTH_SHORT).show(); return; } showText=firstNum; tv_result.setText(showText); }else{ if(nextNum.length()==1){ nextNum=""; }else if(nextNum.length()>0){ nextNum=nextNum.substring(0, nextNum.length()-1); }else{ Toast.makeText(this, "沒有可取消的數(shù)字了", Toast.LENGTH_SHORT).show(); return; } showText=showText.substring(0, showText.length()-1); tv_result.setText(showText); } }else if(resid==R.id.btn_equal){ if(operator.length()==0||operator.equals("=")==true){ Toast.makeText(this, "請輸入運(yùn)算符", Toast.LENGTH_SHORT).show(); return; }else if(nextNum.length()<=0){ Toast.makeText(this, "請輸入數(shù)字", Toast.LENGTH_SHORT).show(); return; } if(caculate()==true){ operator=inputText; showText=showText+"="+result; tv_result.setText(showText); }else{ return; } }else if(resid==R.id.btn_plus||resid==R.id.btn_minus ||resid==R.id.btn_multiply||resid==R.id.btn_divide){ if(firstNum.length()<=0){ Toast.makeText(this, "請輸入數(shù)字", Toast.LENGTH_SHORT).show(); return; } if(operator.length()==0||operator.equals("=")==true||operator.equals("√")==true){ operator=inputText; // 操作符 showText=showText+operator; tv_result.setText(showText); }else{ Toast.makeText(this, "請輸入數(shù)字", Toast.LENGTH_SHORT).show(); return; } }else if(resid==R.id.ib_sqrt){ if(firstNum.length()<=0){ Toast.makeText(this, "請輸入數(shù)字", Toast.LENGTH_SHORT).show(); return; }else if(Double.parseDouble(firstNum)<0){ Toast.makeText(this, "開根號(hào)的數(shù)值不能小于0", Toast.LENGTH_SHORT).show(); return; } result=String.valueOf(Math.sqrt(Double.parseDouble(firstNum))); firstNum=result; nextNum=""; operator=inputText; showText=showText+"√="+result; tv_result.setText(showText); Log.d(TAG, "result="+result+", firstNum="+firstNum+", operator="+operator); }else{ if(operator.equals("=")==true){ operator=""; firstNum=""; showText=""; } if(resid==R.id.btn_dot){ inputText="."; } if(operator.equals("")==true){ firstNum=firstNum+inputText; }else{ nextNum=nextNum+inputText; } showText=showText+inputText; tv_result.setText(showText); } } private String operator=""; // 操作符 private String firstNum=""; // 前一個(gè)操作數(shù) private String nextNum=""; // 后一個(gè)操作數(shù) private String result=""; // 當(dāng)前計(jì)算結(jié)果 private String showText=""; // 顯示的文本內(nèi)容 private boolean caculate(){ // 開始加減乘除四則運(yùn)算 if(operator.equals("+")==true){ result=String.valueOf(Arith.add(firstNum, nextNum)); } else if (operator.equals("-") == true) { result = String.valueOf(Arith.sub(firstNum, nextNum)); } else if (operator.equals("×") == true) { result = String.valueOf(Arith.mul(firstNum, nextNum)); } else if (operator.equals("÷") == true) { if ("0".equals(nextNum)) { Toast.makeText(this, "被除數(shù)不能為零", Toast.LENGTH_SHORT).show(); return false; } else { result = String.valueOf(Arith.div(firstNum, nextNum)); } } firstNum = result; nextNum = ""; return true; } private void clear(String text){ // 清空并初始化 showText = text; tv_result.setText(showText); operator = ""; firstNum = ""; nextNum = ""; result = ""; }
- Python快樂編程:人工智能深度學(xué)習(xí)基礎(chǔ)
- PHP網(wǎng)絡(luò)編程學(xué)習(xí)筆記
- C語言程序設(shè)計(jì)案例精粹
- Python機(jī)器學(xué)習(xí)編程與實(shí)戰(zhàn)
- Serverless架構(gòu)
- 學(xué)習(xí)正則表達(dá)式
- Working with Odoo
- Android系統(tǒng)原理及開發(fā)要點(diǎn)詳解
- MATLAB GUI純代碼編寫從入門到實(shí)戰(zhàn)
- 超簡單:Photoshop+JavaScript+Python智能修圖與圖像自動(dòng)化處理
- 計(jì)算機(jī)應(yīng)用基礎(chǔ)(第二版)
- HTML5移動(dòng)前端開發(fā)基礎(chǔ)與實(shí)戰(zhàn)(微課版)
- Learning NHibernate 4
- Python實(shí)戰(zhàn)指南:手把手教你掌握300個(gè)精彩案例
- GO語言編程從入門到實(shí)踐