【前言】

本文由Chris Blunt发表在smashingmagazinebill学习之后,觉得与国内众多Android入门教程相比,此文堪称经典,鉴于有些朋友对Android非常感兴趣却迟迟找不到好的入门文章,在此特向大家推荐这篇博文。 

http://coding.smashingmagazine.com/2010/10/25/get-started-developing-for-android-with-eclipse/

希望Chris Blunt的这篇文章能够带领更多的人进入Android的开发世界。

为了与一些对英文不太感冒的Android爱好者一起学习,同时也为了提高一下自己的语言表达、组织能力,特将文翻译过来供大家学习参考。

译文bill已字字斟酌,但限于本人学识有限,译文中可能出现术语或者习语翻译不当甚或有错,希望读者积极指出,bill会非常高兴收到你们的反馈,在此谢过。

 

文-下】

建立你的第一个Android应用

这个测试应用程序倒是工作的蛮不错,但你总得建立自己的真正的应用吧?基于此,我们将通过一个简单的应用设计,带领你一步一步地学会开发Android应用并使它在Android设备上运行。

如你所知,许多开发者(包括我在内)都喜欢在工作之余泡上一杯好茶(或者咖啡)。在接下来的小节中,我将带领你创建一个简单的tea counter应用,用于记录目前为止这个用户一共冲泡了几杯茶,并允许他们为自己的冲泡设置一个倒计时提醒。

你可以在GitHub.中下载到这个应用的源码。

设计用户界面

在建立任何Android应用之前,我们总需要先设计并建立用户界面。

下面是这个应用界面的一个预览:

【Android经典入门教程-下(bill译)】_第1张图片

    用户可以通过“+”,“-”按钮设置冲泡时间(以分为单位)。当他们点击“Start”按钮,冲泡倒计时便开始啦。

除非用户再次点击该按钮以取消倒计时,那么当倒计时完成时,冲泡的杯数就会自动加1

创建用户界面

Android的用户界面(或者说用XML文档描述的layouts)被保存于项目中的res/layouts文件夹。在之前的示例应用中那个显示“Hello World”的简单界面由Eclipse自动生成并保存于res/layouts/main.xml中。

Eclipse也提供了图形化界面设计器,它允许你对这些界面元素进行拖拽编排。尽管如此,我还是觉得在XML文档中手动编写界面,然后在图形化界面设计器中预览我编写的界面更简单易行。

现在,让我们修改main.xml文档以便使其展现出我们刚才设计的那个界面。

①在Eclipse的项目浏览器中,双击打开res/layouts/main.xml文档

②选择main.xmlXML视图标签

现在,将XML文档中的内容修改如下【bill注:最好自己手动写一遍】

 

   
   
   
   
  1. xml version="1.0" encoding="utf-8"?> 
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  3.     android:orientation="vertical" android:layout_width="fill_parent" 
  4.     android:layout_height="fill_parent"> 
  5.     <LinearLayout android:orientation="horizontal" 
  6.         android:layout_width="fill_parent" android:layout_height="wrap_content" 
  7.         android:padding="10dip"> 
  8.         <TextView android:layout_width="wrap_content" 
  9.             android:layout_height="wrap_content" android:textSize="20dip" 
  10.             android:text="Brews: " /> 
  11.         <TextView android:layout_width="fill_parent" 
  12.             android:layout_height="wrap_content" android:text="None" 
  13.             android:gravity="right" android:textSize="20dip" android:id="@+id/brew_count_label" /> 
  14.     LinearLayout> 
  15.     <LinearLayout android:orientation="horizontal" 
  16.         android:layout_width="fill_parent" android:layout_height="wrap_content" 
  17.         android:layout_weight="1" android:gravity="center" android:padding="10dip"> 
  18.         <Button android:id="@+id/brew_time_down" android:layout_width="wrap_content" 
  19.             android:layout_height="wrap_content" android:text="-" 
  20.             android:textSize="40dip" /> 
  21.         <TextView android:id="@+id/brew_time" android:layout_width="wrap_content" 
  22.             android:layout_height="wrap_content" android:text="0:00" 
  23.             android:textSize="40dip" android:padding="10dip" /> 
  24.         <Button android:id="@+id/brew_time_up" android:layout_width="wrap_content" 
  25.             android:layout_height="wrap_content" android:text="+" 
  26.             android:textSize="40dip" /> 
  27.     LinearLayout> 
  28.     <Button android:id="@+id/brew_start" android:layout_width="fill_parent" 
  29.         android:layout_height="wrap_content" android:layout_gravity="bottom" 
  30.         android:text="Start" /> 
  31. LinearLayout> 

正如你所见,AndroidXML布局文件非常冗余,但却能够帮助你掌控屏幕上几乎所有的视觉因素。

Android布局文件中一个重要的布局元素是——布局容器(布局方式),比如这个例子中的线性布局(LinearLayout)。这种布局对用户是不可见的,只作为那些可见的,诸如按钮、文本框等视觉元素的容器。

还有其他许多种布局方式,每一种方式都提供了一种不同的布局效果。比如线性布局(LinearLayout)、绝对布局(AbsoluteLayout),还有基于网格的表格布局(TableLayout)等等,你可以在Common Layout Objects中找到更多有关布局方式的描述。

编写代码链接你的布局元素

当你完成布局文件的编写之后,再次运行这个应用,你会发现界面已经不是刚才那个简单的“hello world”了,取而代之的是我们为这个应用设计的用户界面。

如果你现在点击界面中的按钮,你会发现他们会表现得和你预期的一样——高亮显示,但是,他们不会为你做任何事情。

下面让我们来编写代码,使得点击界面按钮时有适当的事情发生。

 

   
   
   
   
  1. # /src/com/example/brewclock/BrewClockActivity.java 
  2.  ... 
  3.  import android.widget.Button; 
  4.  import android.widget.TextView; 
  5.  
  6.  public class BrewClockActivity extends Activity { 
  7.    /** Properties **/ 
  8.    protected Button brewAddTime; 
  9.    protected Button brewDecreaseTime; 
  10.    protected Button startBrew; 
  11.    protected TextView brewCountLabel; 
  12.    protected TextView brewTimeLabel; 
  13.  
  14.    ... 
  15.   } 

接着,我们将改变源代码中的onCreate()的实现。这是一个回调函数,只要Android系统启动你的应用,这个方法就会被调用。在Eclipse自动生成的onCreate()实现中,将我们的应用布局设置成了R.layout.main。正是这一行代码告诉Android系统将我们的layout布局文件译码并展现给用户。

资源对象 The Resource Object

Android中,R类是一个特殊的资源类,允许你在自己的java代码中通过它访问项目资源(布局layouts,字符串strings,菜单menus,图标icons等等)。每一个资源都被赋予了一个ID,在上面的res/layouts/main.xml布局文件中,通过“@+id”这一属性实现。我们将利用这一属性将布局文件中的按钮或者文本;连接到我们的JAVA实现代码中。

 

   
   
   
   
  1. # /src/com/example/brewclock/BrewClockActivity.java 
  2.   ... 
  3.   public class BrewClockActivity extends Activity { 
  4.     ... 
  5.     public void onCreate(Bundle savedInstanceState) { 
  6.       super.onCreate(savedInstanceState); 
  7.       setContentView(R.layout.main); 
  8.  
  9.       // Connect interface elements to properties 
  10.       brewAddTime = (Button) findViewById(R.id.brew_time_up); 
  11.       brewDecreaseTime = (Button) findViewById(R.id.brew_time_down); 
  12.       startBrew = (Button) findViewById(R.id.brew_start); 
  13.       brewCountLabel = (TextView) findViewById(R.id.brew_count_label); 
  14.       brewTimeLabel = (TextView) findViewById(R.id.brew_time); 
  15.     } 

监听事件 Listening For Events

为了能够得知用户在何时点击了我们的按钮,我们必须实现一个监听器。你可能会觉得这很像其他拥有事件驱动的平台的listemers或者callbacks,就像Javascript/JQueryevents或者Railcallbacks

Android通过监听器接口向我们提供了一个类似的机制。比如OnClickListener接口定义了一个当某事件发生时会被触发的方法。为了让我们的应用程序知道用户点击了屏幕上的哪一个按钮,我们需要实现OnClickListener接口并将其绑定到某个按钮上。这样,当用户点击该按钮时,OnClickListener就会通知我们的应用。

 

   
   
   
   
  1. # /src/com/example/brewclock/BrewClockActivity.java 
  2.  ... 
  3.  // Be sure not to import 
  4.  // `android.content.dialoginterface.OnClickListener`. 
  5.  import android.view.View.OnClickListener;  
  6.  
  7.  public class BrewClockActivity extends Activity 
  8.    implements OnClickListener { 
  9.    ... 
  10.    public void onCreate(Bundle savedInstanceState) { 
  11.      ... 
  12.      // Setup ClickListeners 
  13.      brewAddTime.setOnClickListener(this); 
  14.      brewDecreaseTime.setOnClickListener(this); 
  15.      startBrew.setOnClickListener(this); 
  16.    } 
  17.    ... 
  18.    public void onClick(View v) { 
  19.      // TODO: Add code to handle button taps 
  20.    } 

接下来,我们要添加能够处理这些点击事件的代码。我们还向自己的Activity添加了4个新的属性——冲泡时间、冲泡倒计时、已经完成杯数以及倒计时是否正在进行的标记。

 

   
   
   
   
  1. # /src/com/example/brewclock/BrewClockActivity.java 
  2.  ... 
  3.  public class BrewClockActivity extends Activity 
  4.    implements OnClickListener { 
  5.    ... 
  6.    protected int brewTime = 3
  7.    protected CountDownTimer brewCountDownTimer; 
  8.    protected int brewCount = 0
  9.    protected boolean isBrewing = false
  10.    ... 
  11.    public void onClick(View v) { 
  12.      if(v == brewAddTime) 
  13.        setBrewTime(brewTime + 1); 
  14.      else if(v == brewDecreaseTime) 
  15.        setBrewTime(brewTime -1); 
  16.      else if(v == startBrew) { 
  17.        if(isBrewing) 
  18.          stopBrew(); 
  19.        else 
  20.          startBrew(); 
  21.      } 
  22.    } 
  23.  } 

注意,我们用到了Android系统提供的CountDownTimer(倒数计时器)。它将使你能够轻松地创建一个见到的倒数计时器,并在其运行期间定期通知某些事件。你将在接下来的startBrew方法中使用到这个计时器。

下面的这些方法将实现——设置冲泡时间、开始或终止冲泡以及记录成功冲泡的杯数。我们还将在onCreate方法中添加对“冲泡时间”和“成功冲泡杯数”这两个属性的初始化操作。

在工程实践中,推荐将下面这些方法单独地列写于各个模块类中,这里只是为了简单起见才把他们全部写在BrewClockActivity这一个类中

 

   
   
   
   
  1. # /src/com/example/brewclock/BrewClockActivity.java 
  2.  ... 
  3.  public class BrewClockActivity extends Activity 
  4.    implements OnClickListener { 
  5.    ... 
  6.    public void onCreate(Bundle savedInstanceState) { 
  7.      ... 
  8.      // Set the initial brew values 
  9.      setBrewCount(0); 
  10.      setBrewTime(3); 
  11.    } 
  12.  
  13.    /** 
  14.     * Set an absolute value for the number of minutes to brew. 
  15.     * Has no effect if a brew is currently running. 
  16.     * @param minutes The number of minutes to brew. 
  17.     */ 
  18.    public void setBrewTime(int minutes) { 
  19.      if(isBrewing) 
  20.        return
  21.  
  22.      brewTime = minutes; 
  23.  
  24.      if(brewTime < 1
  25.        brewTime = 1
  26.  
  27.      brewTimeLabel.setText(String.valueOf(brewTime) + "m"); 
  28.    } 
  29.  
  30.    /** 
  31.     * Set the number of brews that have been made, and update 
  32.     * the interface. 
  33.     * @param count The new number of brews 
  34.     */ 
  35.    public void setBrewCount(int count) { 
  36.      brewCount = count; 
  37.      brewCountLabel.setText(String.valueOf(brewCount)); 
  38.    } 
  39.  
  40.    /** 
  41.     * Start the brew timer 
  42.     */ 
  43.    public void startBrew() { 
  44.      // Create a new CountDownTimer to track the brew time 
  45.      brewCountDownTimer = new CountDownTimer(brewTime * 60 * 10001000) { 
  46.        @Override 
  47.        public void onTick(long millisUntilFinished) { 
  48.          brewTimeLabel.setText(String.valueOf(millisUntilFinished / 1000) + "s"); 
  49.        } 
  50.  
  51.        @Override 
  52.        public void onFinish() { 
  53.          isBrewing = false
  54.          setBrewCount(brewCount + 1); 
  55.  
  56.          brewTimeLabel.setText("Brew Up!"); 
  57.          startBrew.setText("Start"); 
  58.        } 
  59.      }; 
  60.  
  61.      brewCountDownTimer.start(); 
  62.      startBrew.setText("Stop"); 
  63.      isBrewing = true
  64.    } 
  65.  
  66.    /** 
  67.     * Stop the brew timer 
  68.     */ 
  69.    public void stopBrew() {  
  70.      if(brewCountDownTimer != null
  71.        brewCountDownTimer.cancel(); 
  72.  
  73.      isBrewing = false
  74.      startBrew.setText("Start"); 
  75.    } 
  76.    ... 

这一大段代码中唯一牵涉到Android的一部分是通过setText方法设置显示的labels。在startBrew方法中,我们建立并启动了一个以秒为单位的倒数计时器,直到一次冲泡完成。注意,我们内嵌地实现了该倒数计时器的两个监听方法(onTick和onFinish)。onTick方法每1000毫秒(1秒)便会被调用一次,直到倒数计时为0时,onFinish方法被调用。

避免硬编码 Avoiding Hard-Coded Text in your Code

为了使得本教程的代码更加简洁,我故意将那些标签字符串(比如“Brew Up”,“Start”,“Stop”等)直接硬编码在了JAVA代码里。当从大一点的工程项目来考虑, 这是非常糟糕的编程实践,因为它将会给查找或修改这些字符串带来极大的麻烦。

Android提供了一个更简洁的办法,通过R类将这些字面文本从你的JAVA硬编码中分离出来。

R类允许你在一个xml文件(res/values/strings.xml)中定义并在JAVA实现代码中引用整个应用程序所需的字符串资源。

例如:

 

   
   
   
   
  1. # /res/values/strings.xml 
  2.  <string name="brew_up_label">Brew Up!string> 
  3.  ... 
  4.  
  5.  # /res/com/example/brewclock/BrewClockActivity.java 
  6.  ... 
  7.  brewLabel.setText(R.string.brew_up_label); 
  8.  ... 

现在,如果你想将应用程序中的“Brew Up”这个文本换成别的,你需要做的仅仅是修改这个xml文件中的值,你的应用程序便会将项目中所有用到这个资源的字符串字面值更新,这真是太棒了!

试试你的咖啡冲泡器

我们的代码已经完工,现在是时候体验一下效果了。点击“运行”或者按快捷键“Ctrl+F11”启动模拟器。当一切启动就绪,你就会看到我们之前设计的界面了,现在,是时候泡杯咖啡了!试着设置不同的冲泡时间并点击Start按钮,观察下倒数计时器吧 ^ ^

【Android经典入门教程-下(bill译)】_第2张图片

 

【后记】

此文为bill初学Android时的教程,感觉讲解逻辑清晰,代码完整,指导意义强,特翻译过来,希望能让更多的朋友受益。

最后,bill真心希望有热心的朋友帮我找找翻译中的弊病,这样bill才好和大家一起学习进步!再次感谢。