5.3 事件处理模型
在图形用户界面的开发中,有两个非常重要的内容:一个是界面布局,另一个是控件的事件处理。在Android中,事件处理秉承了JavaSE图形用户界面的处理方式和风格。
Android在事件处理的过程中,主要涉及三个概念:
❏ 事件源:事件发生的场所,通常就是各个控件,例如按钮Button、文本框EditText和活动等。
❏ 事件:用户在界面上的操作的描述,可以封装成为一个类的形式出现,例如键盘操作的事件类是KeyEvent,触摸屏的移动事件类是MotionEvent。与JavaSE图形界面不一样的是并非所有的事件都被封装成为一个类,例如Button单击事件就没有封装成为一个类的形式。
❏ 事件处理者:接收事件对象并对其进行处理的对象,事件处理一般是一个实现某些特定接口类创建的对象。
事件源、事件和事件处理者之间是如何运作的?例如:图5-3所示的LabelButton实例,当单击OK按钮时,将Label标签内容修改为HelloWorld,那么这里的OK按钮就是事件源,单击就是事件,处理“将Label标签修改为HelloWorld”的对象(或程序代码)被称为事件处理者。
一个类(或程序代码)能够成为事件处理者,要求有两个前提:一是要求实现特定接口,LabelButton实例OK按钮事件处理者要求实现android.view.View.OnClickListener接口;二是事件处理者必须在事件源上注册。
提示 事件处理者由于实现XXXListener接口,因此也称为事件监听器。本书以后将事件处理者统一称为事件监听器。
具体的事件处理代码有很多种模型,下面会一一介绍。
5.3.1 活动作为事件监听器
在这种事件处理模型中,事件监听器是当前活动(Activity),活动实现android.view. View.OnClickListener接口。
下面以LabelButton实例为例介绍这种事件处理模型。LabelButton中的MainActivity. java代码如下:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{ ① @Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //通过id获得OK按钮对象 Button btnOK =(Button) findViewById(R.id.button); ② //注册事件监听器 btnOK.setOnClickListener(this); ③ } /* * 实现View.OnClickListener接口方法 */ @Override public void onClick(View view){ ④ TextView text =(TextView) findViewById(R.id.textView); ⑤ text.setText("HelloWorld"); } }
当前界面的MainActivity实现View.OnClickListener接口,见代码第①行。代码第④行的onClick(View view)方法是实现View.OnClickListener接口所要求的方法,参数view是View类型,事实上该参数就是事件源Button对象。
代码第③行是通过Button的setOnClickListener(View.OnClickListener listener)方法注册事件监听器为this,即MainActivity。
代码第②行和第⑤行是通过id获得控件对象,这些id都是布局文件activity_main.xml中声明的控件id属性。
布局文件activity_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.a51work6.labelbutton.MainActivity"> <TextView android:id="@+id/textView" ① android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:layout_marginTop="59dp" android:text="Label" android:textSize="18sp"/> <Button android:id="@+id/button" ② android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/textView" android:layout_centerHorizontal="true" android:layout_marginTop="49dp" android:text="OK"/> </RelativeLayout>
在布局文件activity_main.xml中,代码第①行是声明TextView(标签)控件id为textView,代码第②行是声明Button控件的id为button。
❏ 属性android:id="@+id/Button01"是Button按钮的id,通过id可以找到此按钮对象。
❏ 属性android:layout_width="wrap_content"是设置宽度。
❏ 属性android:layout_height ="wrap_content"是设置高度。
其中,宽和高都可以是wrap_content(适合文本大小)值,也可以是fill_parent(根据屏幕大小占满)值,或是match_parent(匹配父容器大小)值,还可以是具体数字,例如200px。数字的单位可以是以下几类:
❏ px:像素屏幕上的点。
❏ dp:与密度无关的像素,是一种基于屏幕密度的抽象单位。在每英寸160点的显示器上,1dp = 1px;在大于160点的显示器上可能增大。
❏ dip:与dp相同。
❏ sp:与刻度无关的像素,是与dp类似,但是可以根据用户的字体大小首选项进行缩放等。
5.3.2 内部类事件监听器
在这种事件处理模型中,事件监听器是活动(Activity)类中声明的内部类,该内部类也要求实现android.view.View.OnClickListener接口。
下面以LabelButton实例为例介绍这种事件处理模型。LabelButton中MainActivity. java代码如下:
public class MainActivity extends AppCompatActivity{ @Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //通过id获得OK按钮对象 Button btnOK =(Button) findViewById(R.id.button); ① //注册事件监听器 btnOK.setOnClickListener(new ButtonOKOnClickListener()); ② } class ButtonOKOnClickListener implements View.OnClickListener{ ③ /* * 实现View.OnClickListener接口方法 */ @Override public void onClick(View view){ TextView text =(TextView) findViewById(R.id.textView); ④ text.setText("HelloWorld"); } } }
代码第③行是实现android.view.View.OnClickListener接口的内部类ButtonOKOn-ClickListener,因此代码第②行注册事件监听器时候,需要实例化ButtonOKOnClickListener。
注意 任何实现android.view.View.OnClickListener的接口类,都可以成为Button事件监听器,无论它是内部类还是外部类。但是需要注意:如果外部类实现接口时,外部类无法访问活动(Activity)中的视图,所以代码第①行和第④行无法使用findViewById方法,findViewById方法是在Activity或View类中定义的。
5.3.3 匿名内部类事件监听器
既然内部类很适合作为事件处理模型,那么内部类的一个特例匿名内部类是否也适合事件处理呢?事实上,在Android中事件处理时经常使用匿名内部类,包括官方的很多源代码都采用匿名内部类的方式。
下面以LabelButton实例为例介绍这种事件处理模型。LabelButton中的MainActivity. java代码如下:
public class MainActivity extends AppCompatActivity{ @Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //通过id获得OK按钮对象 Button btnOK =(Button) findViewById(R.id.button); //注册事件监听器 btnOK.setOnClickListener(new View.OnClickListener(){ ① /* * 实现View.OnClickListener接口方法 */ @Override public void onClick(View v){ TextView text =(TextView) findViewById(R.id.textView); text.setText("HelloWorld"); } }); ② } }
上述代码第①行~第②行是注册事件监听,其中new View.OnClickListener(){…}是典型的Java匿名内部类的写法。
提示 匿名内部类就是在使用接口(或者抽象类)的时候,直接给出这个接口(或者抽象类)实现。在Java中,接口(或者抽象类)是不能实例化的,实例化的是它们的实现类。匿名内部类的优点是:编译之后代码紧凑,可以在一定程度上减少字节码文件长度,提高虚拟机的加载速度,从而提高运行速度。它的缺点是:代码可读性差。
纵观三种事件处理模型,各有利弊,用户可以根据自己的喜好来选择,在实际的应用开发过程中,事件处理情况会更加复杂,同一个事件源上会有多个不同事件,因此有的时候不是单一的一种处理模型,而是多种模型的结合。