Android開發入門經典例項
開發例項概述
今天帶大家做一個簡單的Android App,這個App會顯示創新工程實踐老師們的照片和資訊,不妨先看一看效果:
雖然這個App非常簡單,但是涉及到了Android開發中的一些關鍵知識,比如:
- 配置開發環境
- App中一個螢幕的抽象:
Activity
- 螢幕之間的跳轉:
Intent
- 構成螢幕展示的檢視元件:顯示圖片的
ImageView
,顯示文字的TextView
,把元件組成一個列表的檢視ListView
- 通過
Adapter
來控制模型和檢視元件之間通訊,即如何在檢視上展示特定的資料 - 通過事件來處理使用者的互動:
OnClickListener
讓我們開始吧!
建立開發環境
工欲善其事,必先利其器
在編寫程式碼之前,我們需要在我們的計算機中配置整合開發環境(IDE,Integrated Development Environment),它是用來開發和部署軟體的工具集合。
如果你之前有過程式設計的經歷,那麼一定會知道最重要的工具是編譯器——它將我們用高階語言編寫的原始碼轉換成計算機可以執行的指令來完成指定的任務。
開發環境的安裝和配置往往需要花費不少時間。對於不同的計算機以及作業系統平臺,安裝配置過程都不盡相同,會出現很多不可預測的錯誤—。新手見到這些錯誤往往會一頭霧水、不知如何解決。
好的開始等於成功的一般,開始學習前最重要的一個步驟就是完成開發環境的配置,讓我們開始吧。
配置Java開發環境
Android的開發使用Java語言,因此首先需要在開發裝置上配置好Java的開發環境。
Android Studio的安裝
Android Studio是Google官方強烈推薦的整合開發環境。在Android官方網站下載Android Studio,它包含了:
- 基於IntelliJ平臺的Android IDE
- Android SDK工具(API、驅動、原始碼、樣例等等)
- Android模擬器
由於眾所周知的原因,可能需要“翻牆”才能夠完整安裝Android Studio,這裡給出一個百度網盤的下載連結:
下載安裝檔案之後,普通軟體的安裝過程沒有任何區別,不再贅述。
如果下載的不是最新版本,啟動Android Studio後,可以通過Check for Update更新到最新的版本。
這裡先告訴大家一個小招數,如果在程式設計過程中,遇到錯誤提示,alt+Enter
(即按照alt
鍵的同時按下回車鍵)可以自動進行錯誤修復。比如,在開發過程中我們經常需要引入一些包,就能自動幫我們引入。
建立專案
-
啟動Android Studio
-
如果Android Studio中尚未開啟任何專案,在Welcome頁面選擇
Start a new Android Studio project
-
如果Android Studio中已經打開了其他專案,從
檔案
選單中選擇New Project
- Configure your new project中填寫相應資訊:
-
Application Name是我們專案的名稱,我們填寫為InnovationCourse
-
Company Domain是開發者所在的組織,一般是公司的域名,這裡我們不妨填寫為zhihuishu.com
-
Project Location是專案存在在本地的位置
-
填寫好後,點選Next
-
Select the form factors your app will run on中選擇
Phone and Tablet
並設定Mininum SDK
為API 15: Android 4.0.3 (IceCreamSandwich)
。Mininum SDK
表示所支援的Android的最低版本,版本越低支援的裝置數量就會越多。我們可以選擇不同的版本來看所支援裝置的百分比。百分比越大,表示越多人可以使用你的APP。選擇好後點擊Next -
Customize the Activity中設定Activity資訊,這裡我直接使用Android Studio提供的預設資訊即可
- 點選
Finish
,一個專案就建立成功了!
專案結構
如果你急於開始專案的開發,你可以略過這一小節。
現在我們已經建立了一個基本的Android應用,它包含了Android Studio幫助我們生成的檔案,在預設的Android
檢視中,檔案結構如下圖所示:
這裡我們看到的是Android Studio給我們生成MainActivity
的程式碼骨架,這個Activity就代表App啟動的時候,我們看到的那個螢幕。這個Java檔案定義了一個Activity,當應用執行時,MainActivity
類啟動一個Acitivty
並載入activity_main.xml
佈局檔案,將其顯示在螢幕上。Acitvity
和layout
的關聯是在MainActivity
中onCreate()
方法裡完成的:setContentView(R.layout.activity_main);
我們再雙擊layout
目錄下的activity_main.xml
,可以看到這個螢幕的預覽:
點選下方的Text,我們可以以文字的形式看到這個檔案的內容。這個檔案就定義了螢幕中應該顯示哪些元件。你如果學過HTML的話,可以類比一下,HTML描述了一個網頁長什麼樣子,而這個XML檔案則描述了App長什麼樣子。可以看到,這個佈局簡單的在螢幕上顯示了一條訊息——Hello world!
我們再來來具體看一下其它幾個重要的檔案:
-
app/src/main/AndroidManifest.xml
Android Manifest檔案是描述Android應用的基本資訊,並定義了應用中的各個元件(Activity是一種元件)。
-
)
app/src/main/res
目錄下包含了應用所需要的資原始檔:drawable<density>/
- 圖片資原始檔layout/
- 使用者介面佈局描述檔案menu/
- 應用的選單佈局values/
常量值例如字串、顏色數值等strings/
國際化資料
執行應用
在模擬器上執行應用
- 在Android Studio的選單中開啟: Tools > Android > AVD Manager
- 選擇
Create Virtual Device
建立模擬器
建立完成後,AVD Manager如下圖:
模擬器建立完成後,回到Android Studio的專案中,在工具欄裡點選Run
按鈕,接下來會彈出一個Choose Device
視窗,選擇Launch emulator
並設定好需要使用的模擬器。
接下來等待模擬器啟動,然後就可以看見剛剛建立的應用執行在模擬器視窗中了(剛啟動的機器可能需要在模擬器中解鎖螢幕)
在真機上執行應用
在真機上執行應用需要先進行一些設定:
- 將真機通過USB線纜連線至開發機器。如果在Windows上開發,可能需要選擇合適的USB驅動,可以參考OEM USB Drivers文件
- 在真機系統中開啟USB除錯選項
接下來就可以在Android Studio執行應用了,方法和在模擬器相同,只是在Choose Device
視窗中需要選擇USB連線的真機。
這裡我們以模擬器為例進行講解。
展示列表
開啟activity_main.xml
佈局檔案,在Design檢視下,我們將上面的HelloWorld標籤刪掉。
我們準備在這個螢幕中顯示老師的列表,而一個列表是一個ViewGroup,內部包含了其他的檢視。我們在顯示手機螢幕的預覽區的左側可以看到大量的檢視元件,這些就是用來構建我們App模樣的基本元素。我們我們選中ListView,將其拖入預覽區內
我們前後左右拖動一下,使其佈滿螢幕。也可以切換到Text檢視來編輯XML,XML進行了修改之後,切回Design檢視可以馬上看到效果。
最終acitivity_main.xml
中的內容為:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.zhihuishu.innovationcourse.MainActivity">
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/teacher_listView"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
</RelativeLayout>
注意,這裡面android:id="@+id/teacher_listView"
定義了這個ListView
的ID為teacher_listView
,這是需要大家手動去修改的。當然你也可以全部複製後替換掉整個XML檔案的內容。
這個ID非常重要,我們在Java程式中,將需要通過這個ID來找到這個檢視元件,進行相應的設定和操作。
檢視元件還有其他一些屬性,比如這裡其它四個屬性都是跟佈局相關的屬性,比如android:layout_alignParentLeft="true"
就表示與包含它的父檢視是左側對齊的。其它屬性我們就不一一解釋了。
簡化的老師列表
準備資料:實現模型
我們這裡先實現一個簡化的老師列表,只顯示老師的名字,圖片暫時不管。
首先我們需要準備老師的資料,我們來建立一個Teacher
類。
選中java
目錄下的com.zhihuishu.innovationcourse
包,【右鍵】->【New】->【Java Class】,在彈出的視窗中輸入類的名稱Teacher。
給Teacher
類增加一個獲取所有老師姓名的方法。這個Teacher
類,正是我們所說的MVC中M,即Model。
package com.zhihuishu.innovationcourse;
import java.util.ArrayList;
import java.util.List;
public class Teacher {
public static List<String> getAllTeachers() {
List<String> teachers = new ArrayList<String>();
teachers.add("張海霞");
teachers.add("陳江");
teachers.add("葉蔚");
return teachers;
}
}
這裡我們採用了硬編碼的形式,通常情況下資料是從資料庫中取出來的,而且是不斷變化的。不過這裡我們不涉及資料庫訪問,簡化處理了。大家知道有資料庫的存在就可以了。
設定Adapter
用來將資料傳遞給ListView
的介面卡是ArrayAdapter
。這裡我們傳入的是一個字串的陣列,因此我們建立一個ArrayAdapter<String>
類。
這部分程式碼新增到MainActivity.java
的onCreate
方法中:
public class MainActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化一個Adapter
ArrayAdapter<String> teacherAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, Teacher.getAllTeachers());
//通過ID獲取listView
ListView listView = (ListView) findViewById(R.id.teacher_listView);
//設定listView的Adapter
listView.setAdapter(teacherAdapter);
}
...
}
注意初始化Adapter的程式碼ArrayAdapter<String> teacherAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, Teacher.getAllTeachers());
三個引數的含義:
- 第一個引數是
this
,表示傳入的是當前的Activity - 第二個引數是
android.R.layout.simple_list_item_1
,這是Android系統自帶的一個列表元素(即列表中的每一行)佈局,只顯一串簡單的文字 - 第三個引數是需要顯示的所有資料構成的List,即資料來源
後面我們會傳入自己定義的元素佈局(因為我們還希望在每一行中顯示老師的圖片),資料來源也需要修改,因為此時不僅僅包含字串了,還需要包含圖片。
執行效果
我們已經可以看到一個簡單的老師列表的效果了:
優化模型
老師的資訊不僅僅包括姓名,還包括圖片和介紹。因此我們需要給Teacher
類增加一些屬性,分別為name
、imageId
和desc
。
老師的圖片我們實現準備好了,大家只需要拷貝進入drawable
資料夾就能在Java程式碼中引用了。我們之前說過drawable
資料夾就是用來存放圖片檔案的。
增加屬性之後我們還需要做三件事情:
- 增加一個建構函式,這個構造有三個引數。即我們建立一個
Teacher
類的例項時,需要告訴系統這個老師的名字、圖片和介紹。 - 給每個屬性增加
getter
和setter
,面向物件程式設計講究資訊的隱藏,因此老師們的屬性都是private
的,為了讓外部能夠訪問老師的屬性,比如獲取老師的圖片來進行展示,那麼就需要有訪問屬性的公共函式。getter
和setter
就是扮演這樣的角色。 - 修改
getAllTeachers()
方法,這個方法此時返回的是Teacher
物件構成的List,而不是字串構成的List了。在這個方法的實現中,我們就通過Teacher的建構函式生成了三個Teacher
物件。
package com.zhihuishu.innovationcourse;
import java.util.ArrayList;
import java.util.List;
public class Teacher {
private String name;
private int imageId;
private String desc;
//建構函式
public Teacher(String name, int imageId, String desc) {
this.name = name;
this.imageId = imageId;
this.desc = desc;
}
// 返回一個Teacher的列表
public static List<Teacher> getAllTeachers() {
List<Teacher> teachers = new ArrayList<Teacher>();
teachers.add(new Teacher("張海霞", R.drawable.zhx, "張海霞,北京大學教授國際大學生iCAN創新創業大賽發起人、主席北京大學資訊科學技術學院教授全球華人微納米分子系統學會祕書長IEEE NTC 北京分會主席。"));
teachers.add(new Teacher("陳江", R.drawable.cj, "陳江,北京大學資訊科學技術學院副教授,高等學校電路和訊號系統教學與教材研究會常務理事,中國通訊理論與訊號處理委員會委員。"));
teachers.add(new Teacher("葉蔚", R.drawable.yw, "葉蔚,北京大學軟體工程國家工程研究中心副研究員,創辦了技術學習服務平臺天碼營(http://tianmaying.com)與軟體開發協同工具Onboard(http://onboard.cn)。"));
return teachers;
}
// 以下都是訪問內部屬性的getter和setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getImageId() {
return imageId;
}
public void setImageId(int imageId) {
this.imageId = imageId;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
改進列表
建立新的佈局
為了在列表的一行中,同時顯示老師的圖片和名稱,我們需要建立自定義的佈局。
選中res/layout
目錄,【右鍵】->【New】->【Layout Resource File】,在彈出對話方塊中,輸入如下資訊:
File Name為佈局檔案的名稱,我們輸入teacher_item。其他的輸入框使用預設資訊即可。其中Root Element表示佈局的方式,這裡我們無需修改,使用預設的LinearLayout
,即檢視元件通過線性的方式來進行佈局。
新的佈局中,我們需要在左側顯示一張圖片,右側顯示老師姓名,因此我們往預覽區拖入一個ImageView
和TextView
,然後切換到Text檢視進行編輯,最終的佈局檔案如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginBottom="5dp"
android:layout_marginTop="5dp"
android:id="@+id/teacher_small_imageView" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="New Text"
android:id="@+id/teacher_name_textView"
android:layout_gravity="center_vertical" />
</LinearLayout>
注意我們將根元素<LinearLayout>
的android:orientation
改為了horizontal
,因為我們希望老師的圖片和老師的姓名水平線性佈局。
ImageView命名為teacher_small_imageView
,長寬都為50dp,與上下的間距為5dp。dp是一種長度的單位,也有其他型別的單位,dp能夠比較好的相容各種解析度的裝置。
TextView命名為teacher_name_textView
,長寬為根據內容自適應,與左側圖片間距為10dp,垂直居中。
建立自定義的ArrayAdapter
我們建立一個自定義的ArrayAdapter。我們建立一個命名為TeacherAdapter
的類,讓其繼承ArrayAdapter<Teacher>
,同時提供一個建構函式。
package com.zhihuishu.innovationcourse;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
import java.util.zip.Inflater;
public class TeacherAdapter extends ArrayAdapter<Teacher> {
public TeacherAdapter(Context context, int resource, List<Teacher> objects) {
super(context, resource, objects);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 獲取老師的資料
Teacher teacher = getItem(position);
// 建立佈局
View oneTeacherView = LayoutInflater.from(getContext()).inflate(R.layout.teacher_item, parent, false);
// 獲取ImageView和TextView
ImageView imageView = (ImageView) oneTeacherView.findViewById(R.id.teacher_small_imageView);
TextView textView = (TextView) oneTeacherView.findViewById(R.id.teacher_name_textView);
// 根據老師資料設定ImageView和TextView的展現
imageView.setImageResource(teacher.getImageId());
textView.setText(teacher.getName());
return oneTeacherView;
}
}
這段程式碼的主要功能就是,提供了一個getView()
方法的過載實現,我們通過過載這個方法就能夠讓listView根據我們的要求來生成每一個列表元素了。而這個方法做了四件事情:
- 獲取老師的資料,
getItem(position)
會把poistion
位置的Teacher
物件返回給你,這件事情Adapter會幫你處理好,你只管呼叫就好了。 - 建立佈局,
View oneTeacherView = LayoutInflater.from(getContext()).inflate(R.layout.teacher_item, parent, false);
這條語句大家照著寫就好了,你只需要知道這是根據Layout檔案生成一個佈局(佈局也是一個View的子類)。 - 獲取ImageView和TextView
- 最後根據老師資料設定ImageView和TextView的展現
在MainActivity.java
中的程式碼也需要做相應的修改,此時我們要建立一個TeacherAdapter
的物件,並將其設定為listView
的Adapter。
public class MainActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TeacherAdapter teacherAdapter = new TeacherAdapter(this, R.layout.teacher_item, Teacher.getAllTeachers());
ListView listView = (ListView) findViewById(R.id.teacher_listView);
listView.setAdapter(teacherAdapter);
}
...
}
執行效果
建立第二個Activity
一切進展順利,我們已經瞭解如何建立Activity中的檢視,如何通過Adapter給檢視傳遞資料。接下來我們要做一個功能,當點選列表中的每一項時,會進入第二個Activity,顯示老師的大圖片以及詳細介紹。
所以我們先來建立第二個Activity吧。
選中java
目錄下的com.zhihuishu.innovationcourse
包,【右鍵】->【New】->【Activity】->【Blank Activity】,在彈出的視窗中輸入Activity名稱TeacherDetailActivity,點選【Finish】。
類似於teacher_item.xml
佈局的編輯,我們往預覽區中拖入一個ImageView
和TextView
,然後在Text檢視中進行佈局的編輯。最終的佈局配置如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.zhihuishu.innovationcourse.TeacherDetailActivity">
<ImageView
android:layout_width="300dp"
android:layout_height="300dp"
android:id="@+id/teacher_large_imageView"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New Text"
android:id="@+id/teacher_desc_textView"
android:layout_below="@+id/teacher_large_imageView"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp" />
</RelativeLayout>
很多屬性我們之前已經遇到過了。這裡我們使用了另外一種佈局RelativeLayout
,顧名思義,就是通過檢視元素的相對位置來進行佈局。其中關鍵的一行程式碼是android:layout_below="@+id/teacher_large_imageView"
,這表示teacher_large_imageView
在teacher_large_imageView
的下方。
實現行為:事件處理
這個Activity的佈局已經建立好了,如何跳轉到這個Activity呢?這時我們需要識別出使用者的點選行為,當用戶點擊發生時才能進行跳轉。
我們回到TeacherAdapter
的getView()
方法,在最後一條return
語句前加入:
oneTeacherView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 這裡進行跳轉
}
});
這段程式碼就是在返回oneTeacherView
之前,設定一個OnClick
點選事件的監聽器,當事件發生的時候,就會執行public void onClick(View v)
內部的程式碼。
Activity的跳轉:Intent
接下來我們就要進行真正的跳轉了, Intent
終於排上用場了,程式碼如下:
oneTeacherView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 初始化一個準備跳轉到TeacherDetailActivity的Intent
Intent intent = new Intent(getContext(), TeacherDetailActivity.class);
// 準備跳轉
getContext().startActivity(intent);
}
});
增加兩行程式碼就能跳轉到第二個Activity了。但是執行起來之後,在第二個Activity中我們並沒有看到資料。所以讓我們來做最後一步吧,你馬上就要成功了。
在進行跳轉的時候,我們需要把老師的資料傳遞給TeacherDetailActivity
,並在TeacherDetailActivity
中進行適當的設定。
通過Intent傳遞引數
設定Intent的Extra資料
Intent
的putExtra
方法就是用來傳遞引數的,我們只需在初始化Intent
物件之後把老師的資料傳遞進去即可。
需要注意的一點是,為了訪問teacher
變數,需要在申明的時候加上final
修飾符。注意程式碼中哪些地方發生了變化,完整的程式碼如下:
package com.zhihuishu.innovationcourse;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
public class TeacherAdapter extends ArrayAdapter<Teacher> {
public TeacherAdapter(Context context, int resource, List<Teacher> objects) {
super(context, resource, objects);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 獲取老師的資料
final Teacher teacher = getItem(position);
// 建立佈局
View oneTeacherView = LayoutInflater.from(getContext()).inflate(R.layout.teacher_item, parent, false);
// 獲取佈局中的ImageView和TextView
ImageView imageView = (ImageView) oneTeacherView.findViewById(R.id.teacher_small_imageView);
TextView textView = (TextView) oneTeacherView.findViewById(R.id.teacher_name_textView);
// 根據老師資料設定ImageView和TextView的展現
imageView.setImageResource(teacher.getImageId());
textView.setText(teacher.getName());
oneTeacherView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 初始化一個準備跳轉到TeacherDetailActivity的Intent
Intent intent = new Intent(getContext(), TeacherDetailActivity.class);
// 往Intent中傳入Teacher相關的資料,供TeacherDetailActivity使用
intent.putExtra("teacher_image", teacher.getImageId());
intent.putExtra("teacher_desc", teacher.getDesc());
// 初始化一個準備跳轉到TeacherDetailActivity的Intent
getContext().startActivity(intent);
}
});
return oneTeacherView;
}
}
根據Intent資料展示內容
最後我們修改TeacherDetailActivity
的onCreate()
方法,加入從Intent
獲取資料並設定檢視展現的程式碼。
package com.zhihuishu.innovationcourse;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.widget.ImageView;
import android.widget.TextView;
public class TeacherDetailActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_teacher_detail);
// 從Intent獲取資料
int imageId = getIntent().getIntExtra("teacher_name", 0);
String desc = getIntent().getStringExtra("teacher_desc");
// 獲取特定的檢視
ImageView imageView = (ImageView) findViewById(R.id.teacher_large_imageView);
TextView textView = (TextView) findViewById(R.id.teacher_desc_textView);
// 根據資料設定檢視展現
imageView.setImageResource(imageId);
textView.setText(desc);
}
}
注意int imageId = getIntent().getIntExtra("teacher_image", 0);
語句中傳入的屬性名teacher_image
一定要寫對,和putExtra()
方法中的保持一致。通常我們是通過定義常量的方式來定義這樣的字串名字,這裡為了簡單處理我們用了硬編碼的方式。
好了,執行起來看看吧,跳轉沒有問題了,恭喜你,整個APP已經搞定了。