官术网_书友最值得收藏!

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 = "";
          }
主站蜘蛛池模板: 荆州市| 武强县| 瑞昌市| 湘阴县| 唐河县| 花垣县| 鹤峰县| 永春县| 乐东| 盈江县| 郯城县| 朝阳县| 马公市| 夹江县| 十堰市| 旬邑县| 永吉县| 墨玉县| 胶南市| 原平市| 高雄市| 乐至县| 满城县| 磐石市| 广丰县| 安阳市| 凌云县| 惠安县| 西和县| 石林| 宜阳县| 马山县| 潞西市| 安乡县| 全州县| 精河县| 县级市| 涿鹿县| 广平县| 宜兴市| 潜江市|