Sunit Katkar1.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中的效果。
结语 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下载上述例子的二进制文件。
相关资源:七夕情人节表白HTML源码(两款)