Android API提供應用程式使用者互動的設計架構,你可以根據使用者在應用程式的操作,設計與提供應用程式與使用者的互動功能。例如使用者點擊畫面元件、按下實體按鍵,還有在觸控螢幕上點擊或滑動,這些操作行為通常會稱為“事件”。應用程式可以依照需求加入事件的控制,當某一種事件發生的時候,也就是使用者執行某種操作,可以執行你為這些事件設計好的程式碼。
Android系統的使用者操作事件控制,都是一些已經設計好的作法,根據使用者操作事件和畫面元件的種類,通常是撰寫實作一個接口(interface)的類別,根據這個接口的規定實作需要的方法,在方法里面設計需要執行的工作。
## 7-1 畫面元件的onClick設定
想要讓應用程式提供的畫面元件,可以讓使用者點擊以后執行一個指定的工作,最簡單的作法就是在畫面元件加入“android:onClick”設定,例如最常用的按鈕元件(Button)。如果需要的話,也可以為文字符件(TextView)執行onClick設定。開啟“res/layout/activity_main.xml”檔案,參考下面的內容加入需要的設定:
~~~
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
...
<!-- 加入“android:clickable="true"”的設定,TextView元件才可以點擊 -->
<!-- 加入“android:onClick="方法名稱"”的設定 -->
<TextView
...
android:clickable="true"
android:onClick="aboutApp" />
</LinearLayout>
~~~
TextView是用來顯示文字的元件,所以要特別加入讓它可以點擊的設定,如果是按鈕元件的話就不用特別設定。如果希望使用者點擊TextView元件以后,在畫面顯示應用程式名稱的訊息框,就要加入需要的程式碼。開啟專案的“MainActivity.java”,參考下面的內容加入需要的程式碼:
~~~
package net.macdidi.myandroidtutorial;
...
// 加入訊息框的API
import android.widget.Toast;
public class MainActivity extends Activity {
...
// 方法名稱與onClick的設定一樣,參數的型態是android.view.View
public void aboutApp(View view) {
// 顯示訊息框,指定三個參數
// Context:通常指定為“this”
// String或int:設定顯示在訊息框里面的訊息或文字資源
// int:設定訊息框停留在畫面的時間
Toast.makeText(this, R.string.app_name, Toast.LENGTH_LONG).show();
}
}
~~~
執行這個應用程式,在應用程式畫面點擊最下面的TextView元件,檢查有沒有顯示訊息框。
## 7-2 選單事件控制
如果應用程式提供的功能比較多一些,為了讓畫面可以比較簡潔,通常會把功能設計為選單,選單資源的部份已經在“第二堂(1)規劃與建立應用程式需要的資源”建立好了。需要讓使用者選擇選單項目后執行一些特定的工作,最簡單的作法是為選單項目加入“onClick”的設定。開啟“res/menu/main_menu.xml”檔案,參考下面的內容加入需要的設定:
~~~
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- 為選單項目加入“android:onClick”設定 -->
<item
android:id="@+id/search_item"
android:showAsAction="always"
android:icon="@android:drawable/ic_menu_search"
android:onClick="clickMenuItem" />
<item
android:id="@+id/add_item"
android:showAsAction="always"
android:icon="@android:drawable/ic_menu_add"
android:onClick="clickMenuItem" />
<item
android:id="@+id/revert_item"
android:showAsAction="always"
android:icon="@android:drawable/ic_menu_revert"
android:onClick="clickMenuItem" />
<item
android:id="@+id/delete_item"
android:showAsAction="always"
android:icon="@android:drawable/ic_menu_delete"
android:onClick="clickMenuItem" />
<!-- 這是外層的選單項目,所以不用設定 -->
<item
android:id="@+id/share_item"
android:showAsAction="always"
android:icon="@android:drawable/ic_menu_share"
android:onClick="clickMenuItem" >
<menu>
<item
android:id="@+id/googleplus_item"
android:title="Google+"
android:onClick="clickMenuItem" />
<item
android:id="@+id/facebook_item"
android:title="Facebook"
android:onClick="clickMenuItem" />
</menu>
</item>
</menu>
~~~
這里執行的設定跟之前的說明不太一樣,所有選單項目設定的方法名稱都是“clickMenuItem”。你也可以為每一個選單項目設定不同的方法名稱,可是這樣做的話,Activity元件里面就要宣告很多方法,所以使用這樣的作法。開啟“MainActivity.java”檔案,參考下面的內容加入需要的程式碼:
~~~
package net.macdidi.myandroidtutorial;
...
import android.app.AlertDialog;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends ActionBarActivity {
...
// 加載選單資源
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.main_menu, menu);
return true;
}
// 使用者選擇所有的選單項目都會呼叫這個方法
public void clickMenuItem(MenuItem item) {
// 使用參數取得使用者選擇的選單項目元件編號
int itemId = item.getItemId();
// 判斷該執行什么工作,目前還沒有加入需要執行的工作
switch (itemId) {
case R.id.search_item:
break;
case R.id.add_item:
break;
case R.id.revert_item:
break;
case R.id.delete_item:
break;
case R.id.googleplus_item:
break;
case R.id.facebook_item:
break;
}
// 測試用的程式碼,完成測試后記得移除
AlertDialog.Builder dialog =
new AlertDialog.Builder(MainActivity.this);
dialog.setTitle("MenuItem Test")
.setMessage(item.getTitle())
.setIcon(item.getIcon())
.show();
}
}
~~~
執行這個應用程式,選擇畫面上方的選單項目,檢查有沒有顯示對話框。
## 7-3 監聽與事件介紹
“android.view”和“android.widget”套件宣告了許多“Listener”接口,這些接口通常會叫作“監聽接口”。每一個監聽接口可以控制使用者在應用程式中執行的一種操作,這些接口的名稱都很規則,都是使用“On種類Listener”的格式命名。例如下列宣告在“android.view.View”類別中的基本監聽接口:
* View.OnClickListener:執行點擊事件。
* View.OnLongClickListener:執行長按事件。
* View.OnKeyListener:執行實體按鍵操作事件。
* View.OnTouchListener:執行觸控螢幕操作事件。
采用這種方式為某個畫面元件加入事件控制,因為需要在程式碼使用畫面元件,所以一定要為元件取一個名稱,設定元件名稱使用“android:id="@+id/名稱"”的格式:
~~~
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
...
<!-- 加入“android:id="@+id/名稱"”的設定 -->
<TextView
android:id="@+id/show_app_name"
... />
</LinearLayout>
~~~
為需要執行事件控制的元件設定好名稱(id)以后,讓使用者在點擊這個元件以后,使用對話框顯示比較詳細的應用程式資訊,所以先在文字資源檔(res/values/strings.xml)加入需要的資源:
~~~
<resources>
...
<string name="about">這是Android Tutorial應用程式</string>
</resources>
~~~
開啟專案的“MainActivity.java”,參考下面的內容加入需要的程式碼:
~~~
package net.macdidi.myandroidtutorial;
...
public class MainActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// 讀取在畫面配置檔已經設定好名稱的元件
TextView show_app_name = (TextView) findViewById(R.id.show_app_name);
// 建立點擊監聽物件
View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View view) {
AlertDialog.Builder d =
new AlertDialog.Builder(MainActivity.this);
d.setTitle(R.string.app_name)
.setMessage(R.string.about)
.show();
}
};
// 注冊點擊監聽物件
show_app_name.setOnClickListener(listener);
}
...
}
~~~
執行這個應用程式,在應用程式畫面點擊最下面的TextView元件,檢查有沒有顯示對話框。因為你在這個TextView元件執行OnClickListener事件的注冊,它的“android:onClick”設定就被覆蓋了,所以點擊以后只會顯示對話框。
一般的應用程式也很常使用長按事件,開啟專案的“MainActivity.java”,參考下面的內容,把原來的點擊事件改為長按事件:
~~~
package net.macdidi.myandroidtutorial;
?
...
?
public class MainActivity extends ActionBarActivity {
?
????@Override
????protected void onCreate(Bundle savedInstanceState) {
????????...
????????// 讀取在畫面配置檔已經設定好名稱的元件
????????TextView show_app_name = (TextView) findViewById(R.id.show_app_name);
?
????????// 建立長按監聽物件
????????View.OnLongClickListener listener = new View.OnLongClickListener() {
?
????????????@Override
????????????public boolean onLongClick(View view) {
????????????????AlertDialog.Builder dialog =
????????????????????new AlertDialog.Builder(MainActivity.this);
????????????????dialog.setTitle(R.string.app_name)
??????????????????????.setMessage(R.string.about)
??????????????????????.show();
????????????????return false;
????????????}
?
????????};
?
????????// 注冊長按監聽物件
????????show_app_name.setOnLongClickListener(listener);
????}
????...
}
~~~
執行這個應用程式,在應用程式畫面點擊最下面的TextView元件,會顯示原來設定的訊息框,長按TextView元件會顯示對話框。經由這兩個練習,就可以了解Android事件的設計方式,在大部份的情況下,監聽接口都會以“On”開頭,宣告與建立好監聽物件以后,呼叫元件的“set監聽接口”方法執行注冊的工作。
## 7-4 ListView元件的事件控制
ListView元件在應用程式中的應用非常多,從應用程式的功能表、瀏覽大量的資料或是讓使用者執行資料的選擇,應用程式需要類似列表資料的需求,都可以使用它來完成。它可以簡單的列出一些文字的項目在畫面上,讓使用者瀏覽與選擇。也可以自己設計需要的項目畫面,加入圖示、CheckBox或其它需要的畫面元件,它呈現的畫面與可以提供的操作功能都非常靈活。
這個記事本的主畫面使用ListView元件顯示所有的記事資料,選擇一個項目以后可以顯示詳細的內容與執行后續的工作,所以需要為ListView設定選擇項目的事件控制。開啟專案的“MainActivity.java”,參考下面的內容加入需要的程式碼:
~~~
package net.macdidi.myandroidtutorial;
...
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class MainActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 增加“final”關鍵字,讓巢狀類別中的程式碼使用
final String[] data = {
"關于Android Tutorial的事情",
"一只非常可愛的小狗狗!",
"一首非常好聽的音樂!"};
int layoutId = android.R.layout.simple_list_item_1;
ArrayAdapter<String> adapter =
new ArrayAdapter<String>(this, layoutId, data);
ListView item_list = (ListView)findViewById(R.id.item_list);
item_list.setAdapter(adapter);
// 建立選單項目點擊監聽物件
AdapterView.OnItemClickListener itemListener = new AdapterView.OnItemClickListener() {
// 第一個參數是使用者操作的ListView物件
// 第二個參數是使用者選擇的項目
// 第三個參數是使用者選擇的項目編號,第一個是0
// 第四個參數在這里沒有用途
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Toast.makeText(MainActivity.this,
data[position], Toast.LENGTH_LONG).show();
}
};
// 注冊選單項目點擊監聽物件
item_list.setOnItemClickListener(itemListener);
...
}
...
}
~~~
執行這個應用程式,在應用程式畫面點擊ListView的選單項目,看看有沒有顯示選單項目的內容訊息框。ListView元件也提供項目長按事件,你可以依照應用程式的需求,使用點擊與長按事件提供使用者的操作。開啟專案的“MainActivity.java”,參考下面的內容加入需要的程式碼:
~~~
package net.macdidi.myandroidtutorial;
...
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class MainActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 增加“final”關鍵字,讓巢狀類別中的程式碼使用
final String[] data = {
"關于Android Tutorial的事情",
"一只非常可愛的小狗狗!",
"一首非常好聽的音樂!"};
int layoutId = android.R.layout.simple_list_item_1;
ArrayAdapter<String> adapter =
new ArrayAdapter<String>(this, layoutId, data);
ListView item_list = (ListView)findViewById(R.id.item_list);
item_list.setAdapter(adapter);
...
// 建立選單項目長按監聽物件
AdapterView.OnItemLongClickListener itemLongListener = new AdapterView.OnItemLongClickListener() {
// 第一個參數是使用者操作的ListView物件
// 第二個參數是使用者選擇的項目
// 第三個參數是使用者選擇的項目編號,第一個是0
// 第四個參數在這里沒有用途
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view,
int position, long id) {
Toast.makeText(MainActivity.this,
"Long: " + data[position], Toast.LENGTH_LONG).show();
return false;
}
};
// 注冊選單項目長按監聽物件
item_list.setOnItemLongClickListener(itemLongListener);
...
}
...
}
~~~
執行這個應用程式,在應用程式畫面長按ListView的選單項目,看看有沒有顯示選單項目的內容訊息框。
## 7-5 重新規劃Activity元件的程式碼
如果Activity元件需要的畫面元件比較多一些,使用者操作的功能也比較復雜,你應該可以想像得到,這個Activity元件類別的onCreate方法,會有一大堆呼叫findViewById方法取得畫面元件物件的敘述,還有宣告與建立監聽物件與執行注冊的敘述。這些需要的敘述通通寫在onCreate方法中,以程式設計的概念來說,一個方法的宣告有上百行的程式敘述,應該不是一種很好的寫法,對開發人員來說,以后的維護與修改都會是一件不容易的工作。
為了讓所有Activity元件的程式碼,都可以使用一種比較固定而且容易的設計方式來完成需要的工作,建議你可以在開發每一個Activity元件類別的時候,使用像這個樣版的模式來開發Activity元件:
~~~
public class SampleActivity extends Activity {
// 宣告所有需要的畫面元件物件字段變量
private ...;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(...);
// 呼叫自己額外宣告的方法,執行所有取得畫面元件物件的工作
processViews();
// 呼叫自己額外宣告的方法,執行所有注冊的工作
processControllers();
}
private void processViews() {
// 在這個方法中,取得畫面元件物件后指定給字段變量
... = (...) findViewById(R.id.xxx);
}
private void processControllers() {
// 在這個方法中,宣告或建立需要的監聽物件
// 并執行所有需要的注冊工作
...
}
...
}
~~~
熟悉這樣的寫法以后,原來需要執行的工作會在不同的方法中執行。你會先加入畫面元件字段變量的宣告,然后在processViews方法中取得與設定畫面元件物件,如果需要執行注冊監聽物件的工作,在processControllers方法中加入需要的程式碼。這樣把程式碼依照工作簡單的分開在不同方法中執行,對以后的維護與修改都會有一個比較固定的作法,而且比較不容易出錯。所以就算是一個很簡單的Activity元件,都建議你使用這樣的寫法。
開啟專案的“MainActivity.java”,不改變原來撰寫好的功能,把它改為下面的內容:
~~~
package net.macdidi.myandroidtutorial;
import android.app.AlertDialog;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends ActionBarActivity {
private ListView item_list;
private TextView show_app_name;
private static final String[] data = {
"關于Android Tutorial的事情",
"一只非常可愛的小狗狗!",
"一首非常好聽的音樂!"};
private ArrayAdapter<String> adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
processViews();
processControllers();
int layoutId = android.R.layout.simple_list_item_1;
adapter = new ArrayAdapter<String>(this, layoutId, data);
item_list.setAdapter(adapter);
}
private void processViews() {
item_list = (ListView)findViewById(R.id.item_list);
show_app_name = (TextView) findViewById(R.id.show_app_name);
}
private void processControllers() {
// 建立選單項目點擊監聽物件
AdapterView.OnItemClickListener itemListener = new AdapterView.OnItemClickListener() {
// 第一個參數是使用者操作的ListView物件
// 第二個參數是使用者選擇的項目
// 第三個參數是使用者選擇的項目編號,第一個是0
// 第四個參數在這里沒有用途
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Toast.makeText(MainActivity.this,
data[position], Toast.LENGTH_LONG).show();
}
};
// 注冊選單項目點擊監聽物件
item_list.setOnItemClickListener(itemListener);
// 建立選單項目長按監聽物件
AdapterView.OnItemLongClickListener itemLongListener = new AdapterView.OnItemLongClickListener() {
// 第一個參數是使用者操作的ListView物件
// 第二個參數是使用者選擇的項目
// 第三個參數是使用者選擇的項目編號,第一個是0
// 第四個參數在這里沒有用途
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view,
int position, long id) {
Toast.makeText(MainActivity.this,
"Long: " + data[position], Toast.LENGTH_LONG).show();
return false;
}
};
// 注冊選單項目長按監聽物件
item_list.setOnItemLongClickListener(itemLongListener);
// 建立長按監聽物件
View.OnLongClickListener listener = new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
AlertDialog.Builder dialog =
new AlertDialog.Builder(MainActivity.this);
dialog.setTitle(R.string.app_name)
.setMessage(R.string.about)
.show();
return false;
}
};
// 注冊長按監聽物件
show_app_name.setOnLongClickListener(listener);
}
// 加載選單資源
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.menu_main, menu);
return true;
}
// 使用者選擇所有的選單項目都會呼叫這個方法
public void clickMenuItem(MenuItem item) {
// 使用參數取得使用者選擇的選單項目元件編號
int itemId = item.getItemId();
// 判斷該執行什么工作,目前還沒有加入需要執行的工作
switch (itemId) {
case R.id.search_item:
break;
case R.id.add_item:
break;
case R.id.revert_item:
break;
case R.id.delete_item:
break;
case R.id.googleplus_item:
break;
case R.id.facebook_item:
break;
}
// 測試用的程式碼,完成測試后記得移除
AlertDialog.Builder dialog =
new AlertDialog.Builder(MainActivity.this);
dialog.setTitle("MenuItem Test")
.setMessage(item.getTitle())
.setIcon(item.getIcon())
.show();
}
// 方法名稱與onClick的設定一樣,參數的型態是android.view.View
public void aboutApp(View view) {
// 顯示訊息框
// Context:通常指定為“this”;如果在巢狀類別中使用,要加上這個Activity元件類別的名稱,例如“元件類別名稱.this”
// String或int:設定顯示在訊息框里面的訊息或文字資源
// int:設定訊息框停留在畫面的時間,使用宣告在Toast類別中的變量,可以設定為“LENGTH_LONG”和“LENGTH_SHORT”
Toast.makeText(this, R.string.app_name, Toast.LENGTH_LONG).show();
}
}
~~~
執行這個應用程式,確認所有功能都還是可以正確的運作。
- 第一堂
- 第一堂(1)西游記里的那只猴子
- 第一堂(2)準備 Android Studio 開發環境
- 第一堂(3)開始設計 Android 應用程式
- 第一堂(4)開發 Android 應用程式的準備工作
- 第二堂
- 第二堂(1)規劃與建立應用程式需要的資源
- 第二堂(2)設計應用程式使用者界面
- 第二堂(3)應用程式與使用者的互動
- 第二堂(4)建立與使用 Activity 元件
- 第三堂
- 第三堂(1)為ListView元件建立自定畫面
- 第三堂(2)儲存與讀取應用程式資訊
- 第三堂(3)Android 內建的 SQLite 數據庫
- 第四堂
- 第四堂(1)使用照相機與麥克風
- 第四堂(2)設計地圖應用程式 - Google Maps Android API v2
- 第四堂(3)讀取裝置目前的位置 - Google Services Location
- 第五堂
- 第五堂(1)建立廣播接收元件 - BroadcastReceiver
- 第五堂(2)系統通知服務 - Notification
- 第五堂(3)設計小工具元件 - AppWidget
- 第六堂
- 第六堂(1)Material Design - Theme與Transition
- 第六堂(2)Material Design - RecylerView
- 第六堂(3)Material Design - Shared Element與自定動畫效果