本文由Chris Blunt发表在smashingmagazine,bill学习之后,觉得与国内众多Android入门教程相比,此文堪称经典,鉴于有些朋友对Android非常感兴趣却迟迟找不到好的入门文章,在此特向大家推荐这篇博文。
http://coding.smashingmagazine.com/2010/10/25/get-started-developing-for-android-with-eclipse/
希望Chris Blunt的这篇文章能够带领更多的人进入Android的开发世界。
为了与一些对英文不太感冒的Android爱好者一起学习,同时也为了提高一下自己的语言表达、组织能力,特将全文翻译过来供大家学习参考。
译文bill已字字斟酌,但限于本人学识有限,译文中可能出现术语或者习语翻译不当甚或有错,希望读者积极指出,bill会非常高兴收到你们的反馈,在此谢过。
这个测试应用程序倒是工作的蛮不错,但你总得建立自己的真正的应用吧?基于此,我们将通过一个简单的应用设计,带领你一步一步地学会开发Android应用并使它在Android设备上运行。
如你所知,许多开发者(包括我在内)都喜欢在工作之余泡上一杯好茶(或者咖啡)。在接下来的小节中,我将带领你创建一个简单的tea counter应用,用于记录目前为止这个用户一共冲泡了几杯茶,并允许他们为自己的冲泡设置一个倒计时提醒。
你可以在GitHub.中下载到这个应用的源码。
在建立任何Android应用之前,我们总需要先设计并建立用户界面。
下面是这个应用界面的一个预览:
用户可以通过“+”,“-”按钮设置冲泡时间(以分为单位)。当他们点击“Start”按钮,冲泡倒计时便开始啦。
除非用户再次点击该按钮以取消倒计时,那么当倒计时完成时,冲泡的杯数就会自动加1。
Android的用户界面(或者说用XML文档描述的layouts)被保存于项目中的res/layouts文件夹。在之前的示例应用中那个显示“Hello World”的简单界面由Eclipse自动生成并保存于res/layouts/main.xml中。
Eclipse也提供了图形化界面设计器,它允许你对这些界面元素进行拖拽编排。尽管如此,我还是觉得在XML文档中手动编写界面,然后在图形化界面设计器中预览我编写的界面更简单易行。
现在,让我们修改main.xml文档以便使其展现出我们刚才设计的那个界面。
①在Eclipse的项目浏览器中,双击打开res/layouts/main.xml文档
②选择main.xml的XML视图标签
现在,将XML文档中的内容修改如下【bill注:最好自己手动写一遍】
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical" android:layout_width="fill_parent"
- android:layout_height="fill_parent">
- <LinearLayout android:orientation="horizontal"
- android:layout_width="fill_parent" android:layout_height="wrap_content"
- android:padding="10dip">
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:textSize="20dip"
- android:text="Brews: " />
- <TextView android:layout_width="fill_parent"
- android:layout_height="wrap_content" android:text="None"
- android:gravity="right" android:textSize="20dip" android:id="@+id/brew_count_label" />
- </LinearLayout>
- <LinearLayout android:orientation="horizontal"
- android:layout_width="fill_parent" android:layout_height="wrap_content"
- android:layout_weight="1" android:gravity="center" android:padding="10dip">
- <Button android:id="@+id/brew_time_down" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="-"
- android:textSize="40dip" />
- <TextView android:id="@+id/brew_time" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="0:00"
- android:textSize="40dip" android:padding="10dip" />
- <Button android:id="@+id/brew_time_up" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="+"
- android:textSize="40dip" />
- </LinearLayout>
- <Button android:id="@+id/brew_start" android:layout_width="fill_parent"
- android:layout_height="wrap_content" android:layout_gravity="bottom"
- android:text="Start" />
- </LinearLayout>
正如你所见,Android的XML布局文件非常冗余,但却能够帮助你掌控屏幕上几乎所有的视觉因素。
在Android布局文件中一个重要的布局元素是——布局容器(布局方式),比如这个例子中的线性布局(LinearLayout)。这种布局对用户是不可见的,只作为那些可见的,诸如按钮、文本框等视觉元素的容器。
还有其他许多种布局方式,每一种方式都提供了一种不同的布局效果。比如线性布局(LinearLayout)、绝对布局(AbsoluteLayout),还有基于网格的表格布局(TableLayout)等等,你可以在Common Layout Objects中找到更多有关布局方式的描述。
当你完成布局文件的编写之后,再次运行这个应用,你会发现界面已经不是刚才那个简单的“hello world”了,取而代之的是我们为这个应用设计的用户界面。
如果你现在点击界面中的按钮,你会发现他们会表现得和你预期的一样——高亮显示,但是,他们不会为你做任何事情。
下面让我们来编写代码,使得点击界面按钮时有适当的事情发生。
- # /src/com/example/brewclock/BrewClockActivity.java
- ...
- import android.widget.Button;
- import android.widget.TextView;
- public class BrewClockActivity extends Activity {
- /** Properties **/
- protected Button brewAddTime;
- protected Button brewDecreaseTime;
- protected Button startBrew;
- protected TextView brewCountLabel;
- protected TextView brewTimeLabel;
- ...
- }
接着,我们将改变源代码中的onCreate()的实现。这是一个回调函数,只要Android系统启动你的应用,这个方法就会被调用。在Eclipse自动生成的onCreate()实现中,将我们的应用布局设置成了R.layout.main。正是这一行代码告诉Android系统将我们的layout布局文件译码并展现给用户。
在Android中,R类是一个特殊的资源类,允许你在自己的java代码中通过它访问项目资源(布局layouts,字符串strings,菜单menus,图标icons等等)。每一个资源都被赋予了一个ID,在上面的res/layouts/main.xml布局文件中,通过“@+id”这一属性实现。我们将利用这一属性将布局文件中的按钮或者文本;连接到我们的JAVA实现代码中。
- # /src/com/example/brewclock/BrewClockActivity.java
- ...
- public class BrewClockActivity extends Activity {
- ...
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- // Connect interface elements to properties
- brewAddTime = (Button) findViewById(R.id.brew_time_up);
- brewDecreaseTime = (Button) findViewById(R.id.brew_time_down);
- startBrew = (Button) findViewById(R.id.brew_start);
- brewCountLabel = (TextView) findViewById(R.id.brew_count_label);
- brewTimeLabel = (TextView) findViewById(R.id.brew_time);
- }
- }
为了能够得知用户在何时点击了我们的按钮,我们必须实现一个监听器。你可能会觉得这很像其他拥有事件驱动的平台的listemers或者callbacks,就像Javascript/JQuery的events或者Rail的callbacks。
Android通过监听器接口向我们提供了一个类似的机制。比如OnClickListener接口定义了一个当某事件发生时会被触发的方法。为了让我们的应用程序知道用户点击了屏幕上的哪一个按钮,我们需要实现OnClickListener接口并将其绑定到某个按钮上。这样,当用户点击该按钮时,OnClickListener就会通知我们的应用。
- # /src/com/example/brewclock/BrewClockActivity.java
- ...
- // Be sure not to import
- // `android.content.dialoginterface.OnClickListener`.
- import android.view.View.OnClickListener;
- public class BrewClockActivity extends Activity
- implements OnClickListener {
- ...
- public void onCreate(Bundle savedInstanceState) {
- ...
- // Setup ClickListeners
- brewAddTime.setOnClickListener(this);
- brewDecreaseTime.setOnClickListener(this);
- startBrew.setOnClickListener(this);
- }
- ...
- public void onClick(View v) {
- // TODO: Add code to handle button taps
- }
接下来,我们要添加能够处理这些点击事件的代码。我们还向自己的Activity添加了4个新的属性——冲泡时间、冲泡倒计时、已经完成杯数以及倒计时是否正在进行的标记。
- # /src/com/example/brewclock/BrewClockActivity.java
- ...
- public class BrewClockActivity extends Activity
- implements OnClickListener {
- ...
- protected int brewTime = 3;
- protected CountDownTimer brewCountDownTimer;
- protected int brewCount = 0;
- protected boolean isBrewing = false;
- ...
- public void onClick(View v) {
- if(v == brewAddTime)
- setBrewTime(brewTime + 1);
- else if(v == brewDecreaseTime)
- setBrewTime(brewTime -1);
- else if(v == startBrew) {
- if(isBrewing)
- stopBrew();
- else
- startBrew();
- }
- }
- }
注意,我们用到了Android系统提供的CountDownTimer(倒数计时器)。它将使你能够轻松地创建一个见到的倒数计时器,并在其运行期间定期通知某些事件。你将在接下来的startBrew方法中使用到这个计时器。
下面的这些方法将实现——设置冲泡时间、开始或终止冲泡以及记录成功冲泡的杯数。我们还将在onCreate方法中添加对“冲泡时间”和“成功冲泡杯数”这两个属性的初始化操作。
在工程实践中,推荐将下面这些方法单独地列写于各个模块类中,这里只是为了简单起见才把他们全部写在BrewClockActivity这一个类中。
- # /src/com/example/brewclock/BrewClockActivity.java
- ...
- public class BrewClockActivity extends Activity
- implements OnClickListener {
- ...
- public void onCreate(Bundle savedInstanceState) {
- ...
- // Set the initial brew values
- setBrewCount(0);
- setBrewTime(3);
- }
- /**
- * Set an absolute value for the number of minutes to brew.
- * Has no effect if a brew is currently running.
- * @param minutes The number of minutes to brew.
- */
- public void setBrewTime(int minutes) {
- if(isBrewing)
- return;
- brewTime = minutes;
- if(brewTime < 1)
- brewTime = 1;
- brewTimeLabel.setText(String.valueOf(brewTime) + "m");
- }
- /**
- * Set the number of brews that have been made, and update
- * the interface.
- * @param count The new number of brews
- */
- public void setBrewCount(int count) {
- brewCount = count;
- brewCountLabel.setText(String.valueOf(brewCount));
- }
- /**
- * Start the brew timer
- */
- public void startBrew() {
- // Create a new CountDownTimer to track the brew time
- brewCountDownTimer = new CountDownTimer(brewTime * 60 * 1000, 1000) {
- @Override
- public void onTick(long millisUntilFinished) {
- brewTimeLabel.setText(String.valueOf(millisUntilFinished / 1000) + "s");
- }
- @Override
- public void onFinish() {
- isBrewing = false;
- setBrewCount(brewCount + 1);
- brewTimeLabel.setText("Brew Up!");
- startBrew.setText("Start");
- }
- };
- brewCountDownTimer.start();
- startBrew.setText("Stop");
- isBrewing = true;
- }
- /**
- * Stop the brew timer
- */
- public void stopBrew() {
- if(brewCountDownTimer != null)
- brewCountDownTimer.cancel();
- isBrewing = false;
- startBrew.setText("Start");
- }
- ...
这一大段代码中唯一牵涉到Android的一部分是通过setText方法设置显示的labels。在startBrew方法中,我们建立并启动了一个以秒为单位的倒数计时器,直到一次冲泡完成。注意,我们内嵌地实现了该倒数计时器的两个监听方法(onTick和onFinish)。onTick方法每1000毫秒(1秒)便会被调用一次,直到倒数计时为0时,onFinish方法被调用。
为了使得本教程的代码更加简洁,我故意将那些标签字符串(比如“Brew Up!”,“Start”,“Stop”等)直接硬编码在了JAVA代码里。当从大一点的工程项目来考虑, 这是非常糟糕的编程实践,因为它将会给查找或修改这些字符串带来极大的麻烦。
Android提供了一个更简洁的办法,通过R类将这些字面文本从你的JAVA硬编码中分离出来。
R类允许你在一个xml文件(res/values/strings.xml)中定义并在JAVA实现代码中引用整个应用程序所需的字符串资源。
例如:
- # /res/values/strings.xml
- <string name="brew_up_label">Brew Up!</string>
- ...
- # /res/com/example/brewclock/BrewClockActivity.java
- ...
- brewLabel.setText(R.string.brew_up_label);
- ...
现在,如果你想将应用程序中的“Brew Up!”这个文本换成别的,你需要做的仅仅是修改这个xml文件中的值,你的应用程序便会将项目中所有用到这个资源的字符串字面值更新,这真是太棒了!
我们的代码已经完工,现在是时候体验一下效果了。点击“运行”或者按快捷键“Ctrl+F11”启动模拟器。当一切启动就绪,你就会看到我们之前设计的界面了,现在,是时候泡杯咖啡了!试着设置不同的冲泡时间并点击Start按钮,观察下倒数计时器吧 ^ ^
此文为bill初学Android时的教程,感觉讲解逻辑清晰,代码完整,指导意义强,特翻译过来,希望能让更多的朋友受益。
最后,bill真心希望有热心的朋友帮我找找翻译中的弊病,这样bill才好和大家一起学习进步!再次感谢。