第二堂(3)应用程式与使用者的互动
最后更新于:2022-04-01 00:52:50
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();
}
}
~~~
执行这个应用程式,确认所有功能都还是可以正确的运作。