《Android应用开发攻略》——1.15 程序:Android OS下的小费计算器Tipster

简介: 本节书摘来自华章计算机《Android应用开发攻略》一书中的第1章,第1.15节,作者:(美)达尔文(Darwin, I. F.)著, 更多章节内容可以访问云栖社区“华章计算机”公众号查看。

1.15 程序:Android OS下的小费计算器Tipster

Sunit Katkar
1.15.1 问题
当你和朋友前往饭店就餐并且希望计算各自的账单和小费时,可能陷入许多手动计算和分歧之中。你希望使用一个应用程序,简单地将小费比例加到总额上,并且按照就餐人数分配。Tipster就是Android中的一个实现,用它展示一个完整的应用程序。
1.15.2 解决方案
这是一个简单的练习,使用Android中的基本GUI元素,然后用一些简单的计算和事件驱动UI代码将它们组合起来。将用到如下GUI组件:
TableLayout
该组件很好地提供了对屏幕布局的控制。可以使用HTML的Table标记来设计窗口部件。
TableRow
这个组件定义了TableLayout中的一行。类似于HTML TR和TD标记的组合。
TextView
这个View子类为屏幕上显示的静态文本提供标签。
EditText
这个View子类提供输入用的文本字段。
RadioGroup
组合单选按钮。
RadioButton
提供单选按钮
Button
常规按钮。
View
将使用View创建具有特定高度和颜色属性的视觉分隔符。
1.15.3 讨论
Android用XML文件来设计窗口部件的布局。在示例项目中,Eclipse的Android插件生成用于布局的main.xml文件。该文件包含基于XML的不同窗口部件及其容器的定义。
strings.xml文件包含应用程序中的所有字符串资源。默认的icon.png文件用于应用程序图标。
然后是自动生成的R.java文件(修改main.xml时将会更新)。这个文件包含为每个布局和窗口部件定义的常量。不要手动编辑该文件;当修改XML文件时,插件会自动进行相应的修改。
在例子中,Tipster.java是用于Activity的主Java文件。
攻略1.4和各种Google教程强调了该插件的使用方法。使用Eclipse插件,创建Android项目Tipster。最终的结果将是外观类似于图1-39的项目布局。
创建布局并放置窗口部件
本攻略的最终目标是创建一个类似于图1-39的布局。
对于这个屏幕布局,将使用如下布局和窗口部件:
TableLayout
提供对屏幕布局的控制。这个布局使用HTML Table标记范例来设计窗口部件的布局。
TableRow
定义TableLayout中的行,类似于HTML TR和TD标记的组合。
TextView
这个View子类为屏幕上显示的静态文本提供标签。
EditText
这个View子类提供输入数值的文本字段。
RadioGroup
组合单选按钮。
RadioButton
提供单选按钮。
Button
常规按钮。
View
使用View类创建具有特定高度和颜色属性的视觉分隔符。
因为你所构建的应用程序中将大量使用这些窗口部件,所以要自己动手熟悉它们。当你查看布局和窗口部件的Javadoc时,仔细观察XML属性。这将帮助你建立main.xml布局文件中的用法与访问该文件的Java代码(Tipster.java和R.java)之间的关联。
Eclipse ADT还有一个可视化布局编辑器,以及单独的UI工具DroidDraw,这两者都可以通过从工具面板上拖放窗口部件来创建布局,就像所有表单设计工具一样。但是,我建议你手动地在XML中创建布局,至少在Android的初学阶段要这么做。以后,当你学习到XML布局API的微妙之处,可以将这一任务交给上述工具。
布局文件main.xml包含布局信息(见例1-6)。TableRow部件在TableLayout中创建一行,可以使用任意多个TableRow。在这个教程中,将使用8个TableRow,其中5个用于按钮下面视觉分隔符之上的窗口部件,另外三个用于按钮和分隔符之下的结果区域。
例1-6:/res/layout/main.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- Using table layout to have HTML table like control over layout -->
<TableLayout
        android:id="@+id/TableLayout01"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:stretchColumns="1"
        xmlns:android="http://schemas.android.com/apk/res/android">
<!—第1行:文本标签放在第0列,
    文本字段放在第2列并允许跨跃2列。
    这一行共有4列 -->
   <TableRow>
   <TextView
           android:id="@+id/txtLbl1"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_column="0"
           android:text="@string/textLbl1"/>
   <EditText①
           android:id="@+id/txtAmount"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:numeric="decimal"
           android:layout_column="2"
           android:layout_span="2"
           />
   </TableRow>
<!--第2行:文本标签放在第0列,
    文本字段放在第2列并允许跨跃2列。
    这一行共有4列  -->
   <TableRow>
   <TextView
           android:id="@+id/txtLbl2"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_column="0"
           android:text="@string/textLbl2"/>
   <EditText
           android:id="@+id/txtPeople"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:numeric="integer"
           android:layout_column="2"
           android:layout_span="3"/>
   </TableRow>
<!--第3行:只在第0列有一个文本标签 -->
   <TableRow>
   <TextView
           android:id="@+id/txtLbl3"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@string/textLbl3"/>
   </TableRow>
<!—第4行: RadioButton组成的RadioGroup放在第0列,
    跨越3列,该行的每个表格单元有一个单选按钮。
    最后一个单元(4)有用于输入自定义小费比例的文本字段-->
   <TableRow>
   <RadioGroup
           android:id="@+id/RadioGroupTips"
           android:orientation="horizontal"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_column="0"
           android:layout_span="3"
           android:checkedButton="@+id/radioFifteen">
           <RadioButton android:id="@+id/radioFifteen"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
                   android:text="@string/rdoTxt15"
                   android:textSize="15sp" />
           <RadioButton android:id="@+id/radioTwenty"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
                   android:text="@string/rdoTxt20"
                   android:textSize="15sp" />
           <RadioButton android:id="@+id/radioOther"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
                   android:text="@string/rdoTxtOther"
                   android:textSize="15sp" />
   </RadioGroup>
           <EditText
                   android:id="@+id/txtTipOther"
                   android:layout_width="fill_parent"
                   android:layout_height="wrap_content"
                   android:numeric="decimal"/>
   </TableRow>
<!--这行用于放置 Calculate 和Reset按钮,
    Calculate按钮放在第2列,Reset放在第3列 -->
   <TableRow>
   <Button
           android:id="@+id/btnReset"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_column="2"
           android:text="@string/btnReset"/>
   <Button
           android:id="@+id/btnCalculate"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_column="3"
           android:text="@string/btnCalculate"/>
   </TableRow>
<!—TableLayout允许其他视图插入TableRow元素之间,
    所以可以插入一个空白视图造成分隔线。这个分隔视
    图用于隔离按钮下面的区域,该区域将显示计算结果
    的TableLayout-->
   <View
           android:layout_height="2px"
           android:background="#DDFFDD"
           android:layout_marginTop="5dip"
           android:layout_marginBottom="5dip"/>
<!-- 这一行也是用来在第2列放置结果文本视图的
    (第0列)文本视图中的结果-->
   <TableRow android:paddingBottom="10dip" android:paddingTop="5dip">
   <TextView
           android:id="@+id/txtLbl4"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_column="0"
           android:text="@string/textLbl4"/>
   <TextView
           android:id="@+id/txtTipAmount"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_column="2"
           android:layout_span="2"/>
   </TableRow>
   <TableRow android:paddingBottom="10dip" android:paddingTop="5dip">
   <TextView
           android:id="@+id/txtLbl5"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_column="0"
           android:text="@string/textLbl5"/>
   <TextView
           android:id="@+id/txtTotalToPay"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_column="2"
           android:layout_span="2"/>
   </TableRow>
   <TableRow android:paddingBottom="10dip" android:paddingTop="5dip">
   <TextView
                  android:id="@+id/txtLbl6"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_column="0"
           android:text="@string/textLbl6"/>
   <TextView
           android:id="@+id/txtTipPerPerson"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_column="2"
           android:layout_span="2"/>
   </TableRow>
  <!—所有行和部件结束-->
</TableLayout>

TableLayout与TableRow
研究Main.xml之后,你会发现TableLayout和TableRow的使用很简单:创建TableLayout,然后插入TableRow。现在,可以自由地在TableRow中插入任何其他窗口部件,例如TextView、EditView等。
仔细观察属性,特别是android:stretchColumns、android:layout_column和 android:layout_span,可以用这些属性,按照使用常规HTML表格的方法放置窗口部件。我建议你关注这些属性的链接,研究它们对TableLayout的影响。
控制输入值
控制输入值:查看main.xml文件中①处的EditText部件。这是第一个文本输入字段,用于输入账单“总额”,只允许输入数字。可以接受小数,因为真正的饭店账单都包含元和分,而不仅仅是元。所以将android:numeric属性值类型设置为decimal。这样,该输入字段中允许输入整数值(如10)和小数值(如10.12),而不允许其他任何类型的输入。
这是简单明了的输入值控制方法,可以省去在Tipster.java文件中编写验证代码的麻烦,确保用户不会输入不正确的值。Android基于XML的约束功能相当强大而实用。你应该研究特定窗口部件的所有可能属性,从设置约束的XML快捷方法中获得最大的益处。除非我在这个版本中完全没有用到,否则我希望在未来的版本中,Android能考虑在android:numeric加入范围,便于我们定义接受的数值范围。
因为(据我所知)目前还没有范围属性,以后你将会看到,我们必须检查特定的值(如0或者空值),以确保小费计算不会失败。
分析Tipster.java
现在,我们来看看控制应用程序的Tipster.java文件。这是一个主类,完成布局、事件处理和应用程序逻辑。
Android Eclipse插件在项目中创建Tipster.java文件的默认代码如例1-7所示。
例1-7:/src/com/examples/tipcalc/Tipster.java的代码段1

package com.examples.tipcalc;
import android.app.Activity;
public class Tipster extends Activity {
    /** 在活动第一次创建时调用 */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}

Tipster类扩展了android.app.Activity类。活动(activity)是用户所能进行的单一中心工作。Activity类负责窗口的创建和UI布局。必须调用setContentView(View view)方法将UI放入Activity中。所以可以将Activity看做空的外部框架,然后填入你的UI。
现在看看例1-8中的Tipster.java类片段。首先定义作为类的成员窗口部件。仔细研读这一例子①到②的部分,作为以后的参考。
然后,用findViewById(int id)方法定位窗口部件。当在Eclipse中清理并构建项目时,在main.xml文件中定义的每个部件ID自动在R.java文件中定义。(如果将Eclipse设置为自动构建,当更新main.xml时,R.java文件立刻更新)。
每个窗口部件都从View类继承而来,提供特殊的GUI功能。TextView类提供了在UI上放置标签的手段,而EditText则提供了一个文本字段。查看例1-8中③~⑥对应的部分,你可以看到如何使用findViewById()寻找窗口部件。
例1-8:/src/com/examples/tipcalc/Tipster.java的代码段2

public class Tipster extends Activity {
    // 应用程序中的窗口部件
    private EditText txtAmount;①
    private EditText txtPeople;
    private EditText txtTipOther;
    private RadioGroup rdoGroupTips;
    private Button btnCalculate;
    private Button btnReset;
    private TextView txtTipAmount;
    private TextView txtTotalToPay;
    private TextView txtTipPerPerson;②
    // 选中的按钮ID
    private int radioCheckedId = -1;
    /**在活动第一次创建时调用*/
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        // Access the various widgets by their id in R.java
        txtAmount = (EditText) findViewById(R.id.txtAmount);③
        //On app load, the cursor should be in the Amount field
        txtAmount.requestFocus();④
        txtPeople = (EditText) findViewById(R.id.txtPeople);
        txtTipOther = (EditText) findViewById(R.id.txtTipOther);
        rdoGroupTips = (RadioGroup) findViewById(R.id.RadioGroupTips);
        btnCalculate = (Button) findViewById(R.id.btnCalculate);
        //On app load, the Calculate button is disabled
        btnCalculate.setEnabled(false);⑤
        btnReset = (Button) findViewById(R.id.btnReset);
        txtTipAmount = (TextView) findViewById(R.id.txtTipAmount);
        txtTotalToPay = (TextView) findViewById(R.id.txtTotalToPay);
        txtTipPerPerson = (TextView) findViewById(R.id.txtTipPerPerson);⑥
        // On app load, disable the Other Tip Percentage text field
        txtTipOther.setEnabled(false);⑦

解决易用性或者可用性问题
应用程序必须达到其他已经发布的应用程序或者网页的易用性水平。简而言之,增加易用特性能够带来好的用户体验。为此,再次查看例1-8。
关注使用View类的requestFocus()方法的地方④。因为EditText部件继承自View类,该方法适用于它。因此,当应用程序加载时,总额(Total Amount)文本字段将得到焦点,光标出现在该控件中。这与流行Web应用程序的屏幕登录相似——在登录屏幕中光标出现在用户名文本字段中。
现在再看看Calculate(计算)按钮⑤,通过调用Button部件上的setEnabled(Boolean enabled)将它禁用。这样,用户在输入必要的字段值之前,用户无法单击它。如果在总额和人数字段未输入值的情况下允许用户单击Calculate按钮,就必须编写捕捉这些情况的验证代码,从而必须向用户显示有关空值的弹出式警告,这会增加不必要的代码和用户交互。当用户看到Calculate按钮禁用时,就能很明显地看出,除非输入所有值,否则小费无法计算。
在例1-8中的⑦,这里还禁用了Other Tip Percentage(其他的小费比例)文本字段。这是因为应用程序加载时默认选中“15% tip”单选按钮,这一默认选择通过main.xml文件完成。main.xml中用如下的语句选择了“15% tip”单选按钮:

android:checkedButton="@+id/radioFifteen"

RadioGroup属性android:checkedButton允许选择组中默认的一个RadioButton部件。
使用过流行桌面和Web应用程序的大部分用户都熟悉“disabled widgets enabled on certain conditions”(在某种条件下启用已经禁用的部件)范例。增加这种小的便利功能总是能使应用更加易用,用户体验也更加丰富。
处理UI事件
和流行的Windows、Java Swing、 Flex和其他UI框架一样,Android也提供了事件模型,可以监听UI中由用户交互引起的事件。我们来看看如何在应用中使用Android事件模型。
首先关注UI中的单选按钮。我们希望知道,用户选择了哪一个单选按钮,因为这能够确定应用程序中的小费比例。使用静态接口OnCheckedChangeListener()“监听”单选按钮,当按钮选择状态变化时将会得到通知。
在应用程序中,希望在选中Other(其他)单选按钮时才启用Other Tip Percentage文本字段。当“15% tip”和“20% tip”单选按钮选中时,我们希望禁用文本字段。除此之外,还要添加一些有利于易用性的逻辑。之前已经讨论过,不应该在必要的所有字段中输入有效值之前启用Calculate按钮。对于这三个单选按钮,应该确保在如下两个条件下启用Calculate按钮:
Other单选按钮选中,且Other Tip Percentage文本字段输入了有效值。
“15% tip”或“20% tip”单选按钮选中,且Total Amount和No. of People文本字段输入了有效值。
例1-9中对单选按钮进行了处理。源代码注释已经做出了很好的解释:
例1-9:/src/com/examples/tipcalc/Tipster.java的代码片段3

/*
 * 在单选按钮组上附加一个OnCheckedChangeListener监控被用户选中的单选按钮
 */
 rdoGroupTips.setOnCheckedChangeListener(new OnCheckedChangeListener() {
 @Override
 public void onCheckedChanged(RadioGroup group, int checkedId) {
    // Enable/disable Other Tip Percentage field
    if (checkedId == R.id.radioFifteen
               || checkedId == R.id.radioTwenty) {
        txtTipOther.setEnabled(false);
        /*
         *如果Total Amount和No. of People字段的值有效,
         *启用Calculate按钮
         */
        btnCalculate.setEnabled(txtAmount.getText().length() > 0
                  && txtPeople.getText().length() > 0);
    }
    if (checkedId == R.id.radioOther) {
       // enable the Other Tip Percentage field
       txtTipOther.setEnabled(true);
       // set the focus to this field
       txtTipOther.requestFocus();
       /*
        *如果Total Amount和No. of People字段的值有效,
        *启用Calculate按钮。在此之前还要确认用户输入了Other Tip Percentage值
        */
       btnCalculate.setEnabled(txtAmount.getText().length() > 0
               && txtPeople.getText().length() > 0
               && txtTipOther.getText().length() > 0);
    }
    // 确定用户选择的小费比例
    radioCheckedId = checkedId;
   }
});

监控文本字段中的键盘活动
前面已经提到,除非文本字段输入了有效值,否则Calculate按钮不能启用。因此,必须确保Calculate按钮仅在Total Amount、No. of People和Other Tip Percentage文本字段中输入有效值之后启用。Other Tip Percentage文本字段仅在选中Other Tip Percentage单选按钮时启用。
我们不必担心输入值的类型、用户是否输入负数或者字母,因为android:numeric属性已经定义了文本字段,限制了用户所能输入的类型。只需要确保输入值存在。
使用静态接口OnKeyListener(),该接口在按下键时通知我们。通知在实际按键发送到EditText部件之前就会到达。
例1-10和例1-11处理文本字段中的按键事件。和例1-9中一样,源代码中的注释也很好地解释了代码的功能。
例1-10:/src/com/examples/tipcalc/Tipster.java的代码片段4

/*
 * 在Tip Amount、No. of People和Other Tip Percentage字段中附加一个KeyListener
 */
txtAmount.setOnKeyListener(mKeyListener);
txtPeople.setOnKeyListener(mKeyListener);
txtTipOther.setOnKeyListener(mKeyListener);

注意,只创建一个监听器,而不是为每个文本字段都创建匿名/内部监听器。我不确定自己的风格更好或者值得推荐,但是如果监听器将要执行一些公用的操作,我就总以这种风格编写代码。所有文本字段都有公共的一个关注点:它们都不应该为空,只有在它们有非空值时,才应该启用Calculate按钮。
例1-11:代码片段5,摘自KeyListener.java

/*
 *用于Total Amount、No of People和Other Tip Percentage字段的KeyListener,
 *我们需要应用这个键盘监听器检查如下的条件:
 *1)如果用户选择了Other Tip Percentage,用户应该在Other Tip Percentage字段输入有效的值
 *在用户输入有效值时启用Calculate按钮
 * 
 *2)如果用户没有在Total Amount和No. of People fields输入值,
 *我们不能进行计算。所以我们只在用户输入有效值时启用Calculate按钮
 */
private OnKeyListener mKeyListener = new OnKeyListener() {
    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
    switch (v.getId()) {①
    case R.id.txtAmount:②
    case R.id.txtPeople:③
        btnCalculate.setEnabled(txtAmount.getText().length() > 0
                && txtPeople.getText().length() > 0);
        break;
    case R.id.txtTipOther:④
        btnCalculate.setEnabled(txtAmount.getText().length() > 0
                && txtPeople.getText().length() > 0
                && txtTipOther.getText().length() > 0);
        break;
    }
    return false;
    }
};

在例1-11的①处,检查View类的ID。记住,因为在main.xml文件中的定义,每个窗口部件都有唯一的ID。之后,在生成的R.java类中定义了这些值。
在②和③处,如果总金额或者人数字段中发生了键盘事件,将检查字段中输入的值,确保用户对这两个字段都没有留空。
在④处,检查用户是否选择Other单选按钮,然后确定Other文本字段不为空。还要再次确定金额或者人数字段是否为空。
KeyListener的目的现在很清晰了:确保所有文本字段非空,仅在这种情况下启用Calculate按钮。
监听按钮单击
现在来看Calculate和Reset(复位)按钮。我们使用静态接口OnClickListener()来通知用户何时单击这些按钮。
和文本字段一样,只创建一个监听器,在监听器中检测被单击的按钮。根据单击的按钮,调用calculate() 或reset()方法。
例1-12说明了为按钮添加单击事件监听器的方法。
例1-12:/src/com/examples/tipcalc/Tipster.java的代码片段6

/*为Calculate 和Reset按钮附加监听器* /
btnCalculate.setOnClickListener(mClickListener);
btnReset.setOnClickListener(mClickListener);
例1-13说明了如何通过检查接受单击事件的View类的ID,检测单击的是哪一个按钮。
例1-13:/src/com/examples/tipcalc/Tipster.java的代码片段7
/**
 * Calculate和Reset按钮的ClickListener,根据单击的按钮调用对应方法
 */
private OnClickListener mClickListener = new OnClickListener() {
    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.btnCalculate) {
            calculate();
        } else {
            reset();
        }
    }
};

复位应用程序
当用户单击Reset按钮时,文本字段应该清除,选中默认的“15% tip”单选按钮,应该清除所有计算结果。
例1-14展示了reset()方法。
例1-14:/src/com/examples/tipcalc/Tipster.java的代码片段8

/**
 *复位屏幕底部的结果文本视图以及文本字段和单选按钮
 */
private void reset() {
    txtTipAmount.setText("");
    txtTotalToPay.setText("");
    txtTipPerPerson.setText("");
    txtAmount.setText("");
    txtPeople.setText("");
    txtTipOther.setText("");
    rdoGroupTips.clearCheck();
    rdoGroupTips.check(R.id.radioFifteen);
    // set focus on the first field
    txtAmount.requestFocus();
}

验证计算小费的输入
前面已经提到,我们限制了文本字段中用户所能输入的值的类型。但是,用户仍然可以在总金额、人数和其他小费比例字段中输入0值,这会造成计算中除以0等错误。
如果用户输入0,必须显示一个弹出式警告框,要求用户输入非0值。用showErrorAlert(String errorMessage, final int fieldId)方法处理这一任务,稍后将对此进行更详细的讨论。
首先,看看例1-15中展示的calculate()方法。注意用户的值是如何解析为双精度值的。
现在注意①和②中对0值的检查。如果用户输入0,显示一个弹出警告框对用户加以警告。接下来看看③,因为用户选择了Other单选按钮,所以Other Tip Percentage文本字段启用,还必须检查小费率是否为0。
当应用程序加载时,默认选中“15% tip”单选按钮。如果用户改变了选择,将选中的单选按钮ID赋值给成员变量radioCheckedId,正如在例1-9的OnCheckedChangeListener中看到的那样。
但是,如果用户接受默认选择,radioCheckedId将为默认值-1。简而言之,我们永远不会知道选中的是哪一个单选按钮。当然,我们知道默认选中的是哪一个按钮,可以编写稍有不同的逻辑,如果radioCheckedId的值为-1就假定小费比例为15%。但是,如果查阅API,你就会发现:可以在RadioGroup上而不是在单独的单选按钮上调用getCheckedRadioButtonId()方法。这是因为OnCheckedChangeListener提供了选中的单选按钮的ID。
显示结果
计算小费很简单。如果没有验证错误,布尔标志isError将为false。简单的小费计算参见例1-15的④~⑤。接下来,计算出的值将在⑥~⑦中设置到TextView窗口部件。
例1-15:/src/com/examples/tipcalc/Tipster.java的代码片段9

/**
 * 按照用户输入的数据计算小费
 */
private void calculate() {
    Double billAmount = Double.parseDouble(
        txtAmount.getText().toString());
    Double totalPeople = Double.parseDouble(
        txtPeople.getText().toString());
    Double percentage = null;
    boolean isError = false;
    if (billAmount < 1.0) {①
        showErrorAlert("Enter a valid Total Amount.",
            txtAmount.getId());
        isError = true;
    }
    if (totalPeople < 1.0) {②
        showErrorAlert("Enter a valid value for No. of People.",
            txtPeople.getId());
        isError = true;
    }
    /*
     * 如果用户从未修改按钮的选择,意味着默认的15%比例有效,但是出于安全还是进行验证
     */
    if (radioCheckedId == -1) {
        radioCheckedId = rdoGroupTips.getCheckedRadioButtonId();
    }
    if (radioCheckedId == R.id.radioFifteen) {
        percentage = 15.00;
    } else if (radioCheckedId == R.id.radioTwenty) {
        percentage = 20.00;
    } else if (radioCheckedId == R.id.radioOther) {
        percentage = Double.parseDouble(
            txtTipOther.getText().toString());
        if (percentage < 1.0) {③
            showErrorAlert("Enter a valid Tip percentage",
                txtTipOther.getId());
            isError = true;
        }
    }
    /*
     * 如果所有字段都填写了有效值,则继续计算小费
     */
    if (!isError) {
        Double tipAmount = ((billAmount * percentage) / 100);④
        Double totalToPay = billAmount + tipAmount;
        Double perPersonPays = totalToPay / totalPeople;⑤
        txtTipAmount.setText(tipAmount.toString());⑥
        txtTotalToPay.setText(totalToPay.toString());
        txtTipPerPerson.setText(perPersonPays.toString());⑦
    }
}

显示警告
Android提供AlertDialog类来显示弹出式警告。可以用它显示一个具有多达3个按钮和一条信息的对话框。
例1-16展示的showErrorAlert方法使用AlertDialog显示错误消息。注意,向这个方法传递了两个参数:String error Message和int fieldId。第一个参数是我们希望向用户显示的错误消息。fieldId是导致错误的字段ID。在用户关闭警告对话框之后,这个fieldId使我们能够将焦点置于该字段之上,用户由此得知该字段发生了错误。
例1-16:/src/com/examples/tipcalc/Tipster.java的代码片段10

/**
 * S在一个警告框中显示错误消息
 *
 * @param errorMessage
 * 显示的错误消息字符串
 * @param fieldId
 * 导致错误的字段ID。需要用它在对话框消失之后设置字段焦点
 */
private void showErrorAlert(String errorMessage,
    final int fieldId) {
    new AlertDialog.Builder(this).setTitle("Error")
    .setMessage(errorMessage).setNeutralButton("Close",
            new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog,
                   int which) {
                findViewById(fieldId).requestFocus();
            }
        }).show();
}

组合以上各个功能,就实现了图1-39中的效果。

image


结语
Android OS的开发与其他UI工具包的开发(包括Microsoft Windows、 X Windows、 Java Swing或Adobe Flex)没有太大的不同。当然,Android也有自己的特点,总体上是一个非常好的设计。XML布局范例相当好,可以用简单的XML构造复杂的UI。此外,事件处理模型很简单、具有丰富的特性,在代码中使用也很直观。
1.15.4 源代码下载URL
可以从 http://www.vidyut.com/sunit/android/tipster.zip下载上述例子的源代码。

1.15.5 二进制文件下载URL
可以从http://www.vidyut.com/sunit/android/tipster.zip下载上述例子的二进制文件。

相关文章
|
10天前
|
IDE Java 开发工具
深入探索安卓应用开发:从环境搭建到第一个"Hello, World!"应用
本文将引导读者完成安卓应用开发的初步入门,包括安装必要的开发工具、配置开发环境、创建第一个简单的安卓项目,以及解释其背后的一些基本概念。通过一步步的指导和解释,本文旨在为安卓开发新手提供一个清晰、易懂的起点,帮助读者顺利地迈出安卓开发的第一步。
188 65
|
10天前
|
存储 Java Android开发
探索安卓应用开发:构建你的第一个"Hello World"应用
【9月更文挑战第24天】在本文中,我们将踏上一段激动人心的旅程,深入安卓应用开发的奥秘。通过一个简单而经典的“Hello World”项目,我们将解锁安卓应用开发的基础概念和步骤。无论你是编程新手还是希望扩展技能的老手,这篇文章都将为你提供一次实操体验。从搭建开发环境到运行你的应用,每一步都清晰易懂,确保你能顺利地迈出安卓开发的第一步。让我们开始吧,探索如何将一行简单的代码转变为一个功能齐全的安卓应用!
|
3天前
|
存储 测试技术 Android开发
探索安卓应用开发:从基础到高级
【9月更文挑战第31天】在这篇文章中,我们将一起踏上安卓应用开发的旅程。无论你是初学者还是有一定经验的开发者,本文都将为你提供有价值的信息和指导。我们将从安卓应用开发的基础知识开始,逐步深入到更高级的主题。通过阅读本文,你将了解到如何构建一个安卓应用,包括用户界面设计、数据存储和网络通信等方面。此外,我们还将探讨一些高级主题,如性能优化、安全性和测试。让我们一起开始吧!
|
4天前
|
设计模式 Java Android开发
安卓应用开发中的内存泄漏检测与修复
【9月更文挑战第30天】在安卓应用开发过程中,内存泄漏是一个常见而又棘手的问题。它不仅会导致应用运行缓慢,还可能引发应用崩溃,严重影响用户体验。本文将深入探讨如何检测和修复内存泄漏,以提升应用性能和稳定性。我们将通过一个具体的代码示例,展示如何使用Android Studio的Memory Profiler工具来定位内存泄漏,并介绍几种常见的内存泄漏场景及其解决方案。无论你是初学者还是有经验的开发者,这篇文章都将为你提供实用的技巧和方法,帮助你打造更优质的安卓应用。
|
6天前
|
缓存 前端开发 Android开发
安卓应用开发中的自定义控件
【9月更文挑战第28天】在安卓应用开发中,自定义控件是提升用户界面和交互体验的关键。本文通过介绍如何从零开始构建一个自定义控件,旨在帮助开发者理解并掌握自定义控件的创建过程。内容将涵盖设计思路、实现方法以及性能优化,确保开发者能够有效地集成或扩展现有控件功能,打造独特且高效的用户界面。
|
9天前
|
存储 XML 前端开发
探索Android应用开发:从基础到进阶
【8月更文挑战第57天】在这篇文章中,我们将深入探讨Android应用开发的奥秘。无论你是新手还是有经验的开发者,本文都将为你提供有价值的见解和技巧。我们将从基本的UI设计开始,逐步介绍数据存储、网络请求等高级主题,并展示一些实用的代码示例。让我们一起踏上这段激动人心的旅程吧!
|
15天前
|
开发工具 Android开发 iOS开发
掌握安卓与iOS应用开发:关键技术与未来展望
本文深入探讨了安卓和iOS平台下的应用开发技术,重点比较了两大平台的架构、开发工具和市场策略。通过分析最新的技术趋势和开发者社区的反馈,文章为读者提供了一个全面的对比视角,旨在帮助开发者做出更明智的平台选择和开发决策。
|
10天前
|
安全 Java Android开发
掌握安卓与iOS应用开发中的关键技术
本文深入探讨了安卓和iOS平台上应用开发的关键性技术,包括平台特性、开发工具选择、性能优化技巧及跨平台开发的可行性分析。通过对比两种平台的开发环境与实践案例,旨在为开发者提供全面的视角以理解和把握移动应用开发的核心技术。无论是安卓的Java与Kotlin之争,还是iOS的Swift语言革命,本文都将一一解析其优势与应用场景,帮助开发者在技术选型上有更明智的决策。此外,文章还将触及到当前流行的跨平台框架如React Native和Flutter,评估它们在项目实施中的实用性和限制,为有意进行多平台同步开发的团队提供参考。通过对这些关键技术的梳理,本文期望能够启发开发者深化对移动平台开发的理解,并
|
12天前
|
存储 开发框架 数据可视化
深入解析Android应用开发中的四大核心组件
本文将探讨Android开发中的四大核心组件——Activity、Service、BroadcastReceiver和ContentProvider。我们将深入了解每个组件的定义、作用、使用方法及它们之间的交互方式,以帮助开发者更好地理解和应用这些组件,提升Android应用开发的能力和效率。
|
15天前
|
XML 编解码 Java
探索安卓应用开发之旅:从新手到专家
【9月更文挑战第19天】在这个数字时代,移动应用无处不在,而安卓平台因其开放性和广泛的用户基础成为了开发者的热门选择。本文将引导你了解如何从零基础开始,逐步掌握安卓开发的核心技术,包括界面设计、数据处理和网络通信等。通过实际案例和代码示例,我们将深入探讨安卓开发的各个阶段,帮助你构建起自己的应用。无论你是编程新手还是有一定经验的开发者,这篇文章都将为你提供宝贵的知识和技能,让你在安卓开发的道路上越走越远。让我们开始这段激动人心的旅程吧!
34 8

推荐镜像

更多
下一篇
无影云桌面