程序猿的开心一刻###
以下谁是二进制思想的最早提出者?
a,伏羲;b,姬昌;c,莱布尼茨;d,柏拉图。
请在评论区输入答案,接下来进入正题
一、简介
本文将带你学习Android中最基本的事件处理机制,了解什么是事件,怎样来监听事件。随后你将学习到什么是意图,以及通过一个实例来学习怎样用意图在Activity间传递信息。
知识点
- Android事件监听器
- Android事件处理者
- 事件监听器的注册
- 在Activity之间传递数据
二、事件处理
对于应用的交互而言,事件是一个收集用户交互信息的有效途径。这些事件包括按压按钮或者触碰屏幕。Android的框架维护了一个基于先进先出的事件队列,你可以在程序中捕获这些事件,并针对每个需求,采取合适的行动。
下面有三个关于Android事件管理的概念:
-
事件监听器
:事件监听器是View类中的一个接口,它包含了一个回调方法。当用户与UI组件进行交互时,相应已注册的监听器会被触发,Android框架会调用这些方法。 -
事件监听器注册
:事件注册就是一个事件处理者通过事件监听器来注册的过程。因此,事件监听器触发事件时调用响应的处理者(Handler
)。 -
事件处理者
:当某个事件发生时,如果我们为该事件注册了一个事件监听器,那么这个监听器就会调用事件处理者。后者为实际处理该事件的方法。
1. 事件监听器和事件处理者
以下为一些常见的事件处理者,后面补充了它对应的事件监听器和说明。
-
onClick()
:对应的事件监听器是OnClickListener()
。当用户点击、触摸或者聚焦(给它焦点)与任何一个部件时(这些部件有按钮、文本和图片等),该方法就会被调用。你可以使用名为onCLick()
的事件处理者来处理这些事件。 -
onLongClick()
:对应的事件监听器是OnLongClickListener()
。当用户长时间(一秒或者很多秒)点击、触摸或聚焦于任何一个部件时,该方法会被调用。你可以使用名为onLongClick()
的事件处理者来处理这些事件。 -
onFocusChange()
:对应的事件监听器是OnFocusChangeListener()
。有时当用户从某个视图部件上移开关注时,这个部件就会失去焦点,此时该方法会被调用。你可以使用onFocusChange()
这个事件处理者来处理这些事件。 -
onKey()
:对应的事件监听器是OnFocusChangeListener
。当用户关注某个部件并且按压(或释放)了设备上的实体按键时,该方法会被调用。你可以使用onKey()
事件处理者来处理这个事件。 -
onTouch()
:对应的事件监听器是OnTouchListener()
。当用户按键、释放按键或者在屏幕上任意移动手势时,会调用该方法。你可以使用onTouch()
事件处理者来处理这个事件。 -
onMenuItemClick
:对应的事件监听器是onMenuItemCLickListener()
。当用户选择了一个菜单项时会调用该方法。你可以使用onMenuItemCLick()
事件处理者来处理这个事件。 -
onCreateContextMenu()
:对应的事件监听器是OnCreateContextMenuItemListener()
。当上下文菜单正在建立时(持续的长按动作会触发该事件)会调用该方法。
在View
类中,还有更多诸如OnHoverListener
、OnDragListener
之类的事件监听器可供使用。所以为了开发具有更加丰富功能的应用,你需要去查阅和参考Android开发的官方文档。
2. 事件监听器注册
前文提到过,事件的注册就是一个事件处理者注册事件监听器的过程。尽管对于注册监听器来说,有许多灵活的方法,但在本文中只列出了其中最常用的3种方法。在你的实际项目中,可以任意使用其中之一。
这些方法有:
- 使用匿名内部类
- 在
Activity
类中实现Listener
的接口。 - 在
activity_main.xml
(对应的)布局文件中直接通过android:onClick
属性指定事件处理者。
下面的内容将为你逐一讲解。
(1)使用匿名内部类注册事件监听器
以按钮的点击事件为例,首先要在xml布局文件中定义按钮的id,然后在Activity类中实例化控件,使用自定义的内部类,如下所示:
public class MainActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 此处写其他代码
// button是查找后赋值的按钮对象
button.setOnClickListener(new MyOnClickListener();
}
// 自定义的匿名内部类
Private final class MyOnClickListener implements OnClickListener{
Public void onClick(View view){
// 此处写点击功能的逻辑
}
}
}
(2)实现事件监听器接口
这种方法是让当前Activity实现OnClickListener
,并实现这个接口中的方法:onClick(View view)
:
public class MainActivity extends Activity implements OnClickListener{
// 当然这儿应该还有该类中的其他内容
@Override
Public void onClick(View view){
// 此处填实现的细节
}
}
(3)在布局文件中指定事件处理者
以按钮的点击事件为例,这种方法是在xml布局文件中,在控件属性里添加android:onClick
属性,然后指明调用方法的名字,例如android:onClick="onAddClick"
。
在对应的Activity类中,也需要写一个对应的方法,方法名要与之对应,如:
public void onAddClick(View view){
//这里填具体的逻辑
}
3. 触碰模式
用户可以通过实体按键、屏幕上的按钮,以及触摸屏幕来和设备交互。触摸屏幕能让设备进入触碰模式,用户可以通过屏幕上的虚拟按钮、图片来完成交互。你可以通过调用View
类的isInTouchMode()
方法来检查设备是否处于触碰模式。
这个流程图如下所示:
4. 焦点
一个视图或部件在处于获得焦点状态时,通常会高亮显示或者显示一个闪烁的光标。这个状态意味着它已经准备好去接收用户的输入。
所以对于状态,主要的检查方法如下:
-
isFocusable()
:返回true
或者false
。 -
isFocusableInTouchMode()
:检查某个组件(或视图)在触碰模式下是否可以获得焦点(当使用实体按键但设备不处于触碰模式时,一个视图仍然是可以获得焦点的)。
在XML文件中可以对这些组件设置如下的属性,以确定是哪个组件可以获得焦点:
android:focusUp="@+id/组件的ID"
5. 触碰事件onTouchEvent()
对于触碰事件的处理方式如下,请尝试去阅读代码块:
public boolean onTouchEvent(motionEvent event){
// 通常需要重写这个onTouchEvent()方法来对指定的操作实现响应
switch(event.getAction()){
// 对event对象传入的多个动作,使用getAction()方法获得该动作的ID值(均是预定义好的常量),
// 然后进行比较,在一些判断中来执行后续的操作。
// 本例使用了switch,你可以根据实际情况选择if、while并进行条件判断。
case TOUCH_DOWN:
//TOUCH_DOWN指代了用户的手指已经按到屏幕上但还未抬起的这一动作。
Toast.makeText(this,"The Button has been clicked down.",Toast.LENTH_LONG).show();
//此处用了一个Toast提示框来显示用户执行了何种操作,下同。
break();
case TOUCH_UP:
//TOUCH_UP指代了用户的手指从屏幕上抬起的这一动作。
Toast.makeText(this,"The Button has been clicked up.",Toast.LENTH_LONG).show();
break;
case TOUCH_MOVE:
//TOUCH_MOVE指代了用户的手指在屏幕上移动但还未抬起的这一动作。
Toast.makeText(this,"The Button has been clicked move."Toast.LENTH_LONG).show();
break;
}
return super.onTouchEvent(event) ;
}
三、内部通信
当某个Activity需要启动另外一个Activity时,对于用于来说表现的是操作界面的切换,而对于应用本身来说,则需要通过意图
(Intent
)来告诉系统自己想要启动哪个Activity。
事实上,无论是启动一个Activity还是一个Service,或者一个BroadcastReceiver,在Android中均需要用Intent来封装这种调用的“意图”。
而在这些组件切换的过程中,就可以使用Intent来传递信息。你可以将数据封装成Bundle对象,然后用Intent把这个对象携带至其他地方,从而实现了系统内部的一次数据交换。
以后经常会用到startActivity(Intent intent)
、startService(Intent service)
和sendBroadcast(Intent intent)
等方法,这些方法就是使用了Intent来启动不同的组件。
现在我们主要讨论Intent操作Activity的相关知识。
意图是对于将要执行的操作的抽象描述。在startActivity()
方法中,可以用意图来启动一个Activity。意图的本身是一个Intent对象,它是一个可传递的数据结构,存放了将要执行的操作的抽象描述。
例如,你在某个Activity中需要打开邮件客户端并使用设备来发送给一封电子邮件,那么在你的Activity中就要用合适的选择器,发送一个ACTION_SEND
意图给意图解析器。指定的选择器将给予用户合适的接口,让他选择怎样发送电子邮件数据(这一步有点类似于Windows系统中的打开方式,但它们并不是同一类东西)。
实现这个功能的主要代码如下:
Intent intent_sendEmail = new Intent(Intent.ACTION_SEND, Uri.parse("mailto:"));
email.putExtra(Intent.EXTRA_EMAIL, 收件人的地址);
email.putExtra(Intent.EXTRA_SUBJECT, 邮件“主题”);
email.putExtra(Intent.EXTRA_TEXT, 邮件“正文”);
startActivity(Intent.createChooser(intent_sendEmail, "请选择一个邮件客户端以发送电子邮件…"));
再比如,假设你需要一个Activity来打开设备上的浏览器应用并访问指定的URL,你就需要在Activity中发送一个ACTION_WEB_SEARCH
意图给Android意图解析器,从而在浏览器中打开这个URL。
意图解析器解析了众多的Activity,并且选中最适合当前设定的那一个(例如本例中的浏览器Activity),然后意图解析器会将网页的URL传递给浏览器并访问它。
实现该功能的代码样例如下:
String keyword = "阿里巴巴";
Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
intent.putExtra(SearchManager.QUERY, keyword);
startActivity(intent);
上面这段代码会在Android的搜索引擎中搜索shiyanlou
相关信息。
对于在Activity中传递信息而言,则需要使用到Bundle。我们稍后在实例中为你讲解其详细的使用方法。
四、实例
下面通过一个实例,通过实现匿名类和XML中声明两种方法来注册事件监听器。
主要有以下步骤:
- 请使用Android Studio来创建一个项目。
- 除了
MainActivity
,请通过菜单再创建一个名为SecondActivity
的Activity。 - 在
res/layout/activity_main.xml
文件中的布局里,为MainActivity
添加两个主要的控件:一个文本框EditText
、一个按钮Button
,并设置它们的属性(详细代码请参考后文)。 - 在
res/layout/activity_second.xml
文件中的布局里,为SecondActivity
添加两个主要的控件:一个按钮Button
和一个文本标签TextView
,并设置它们的属性(详细代码请参考后文)。特别注意在按钮的属性中添加一个android:onClick="show"
来注册监听器。 - 修改
MainActivity.java
文件,在MainActivity
类中声明刚刚添加的控件,在onCreate()
方法内对它们进行实例化(使用findViewById()
方法),对按钮以匿名内部类的方式添加点击事件监听器,在onClick
方法中获取文本框的输入值,通过Bundle对象存入Intent,并以这个Intent对象启动SecondActivity
。 - 修改
SecondActivity.java
文件中,实现已注册的show
方法,在其中获取MainActivity
传过来的值,并显示在TextView中。 - 检查代码,编译并运行这个应用,在模拟器中查看运行的过程,检查其是否与设计的相一致。
下面是部分要用到的代码:
首先是res/layout/activity_main.xml
中的内容:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.work.eventtest.MainActivity">
<EditText
android:layout_width="match_parent"
android:layout_height="60dp"
android:id="@+id/editText01"
android:layout_marginTop="20dp"
android:textSize="20sp"
android:background="@color/colorAccent"/>
<Button
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_below="@id/editText01"
android:layout_marginTop="50dp"
android:id="@+id/button1"
android:background="@color/colorPrimaryDark"
android:text="发送文本"/>
</RelativeLayout>
接下来是MainActivity.java
中的内容,建议你先按照自己的思路实现,再来通过下面的代码验证自己的想法:
package com.example.work.eventtest;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
public class MainActivity extends AppCompatActivity {
private Button button;
private EditText editText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//实例化控件
button = (Button) findViewById(R.id.button1);
editText = (EditText)findViewById(R.id.editText01);
// 通过匿名内部类来为button注册点击事件监听器
button.setOnClickListener(new View.OnClickListener() {
// 重写onCLick()方法,实现点击事件处理者的功能
@Override
public void onClick(View v) {
// 创建一个字符串对象用于接收来自文本框的输入内容
String message = new String();
// 通过文本框的getText()方法获取输入内容,赋予message字符串对象
message = editText.getText().toString();
// 创建一个“意图”,用于从MainActivity跳转到SecondActivity
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
// 创建一个Bundle对象,名为data,用于在Activity之间传递信息
Bundle data = new Bundle();
// 向这个Bundle对象中存入内容,因为是字符串,所以用putString()方法
// 参数的形式是key-value形式,在另外一个Activity取出内容的时候就是通过这个key
// 如果是其他的内容,则根据不同的数据类型选用不同的存入方法
data.putString("mA",message);
// 最后将Bundle存入intent对象中,让后者携带到另外一个Activity中
intent.putExtras(data);
// 调用startActivity()方法,跳转到另外一个Activity
startActivity(intent);
// 如果你想在启动SecondActivity的过程中关闭现在这个Activity,
// 则可以使用finish()方法来结束当前Activity。
// MainActivity.this.finish();
}
});
}
}
接下来再创建一个SecondActivity
,下面是res/layout/activity_second.xml
中的内容。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_second"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff00ff"
tools:context="com.example.work.eventtest.SecondActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="30dp"
android:background="#00aaee"
android:textSize="20sp"
android:id="@+id/textview1"/>
<Button
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_below="@id/textview1"
android:layout_marginTop="50dp"
android:background="#aa00e0"
android:text="显示传过来的文本"
android:onClick="show"
android:id="@+id/button2"/>
</RelativeLayout>
以下是SecondActivity.java
中的内容,在这里面我们就没有注册按钮的点击事件监听器了,而是实现了一个show()
方法用于承担点击事件处理者的职责。
package com.example.work.eventtest;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
public class SecondActivity extends AppCompatActivity {
// 声明一个文本标签控件
private TextView textView;
// 声明一个字符串变量用于存放前一个Activity中传过来的信息
private String message;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
// 实例化这个文本标签
textView = (TextView)findViewById(R.id.textview1);
// 通过getIntent()方法来获得启动当前Activity的上一个Intent对象
Intent intent = getIntent();
// 从intent对象中通过getExtras()方法获得其携带的消息,
// 接下来调用getString()方法来得到这个字符串,注意该方法的参数为(字符串的key,默认值)
message = intent.getExtras().getString("mA","null");
}
public void show(View view){
// 如果获得了上个Activity传过来的消息,则显示在TextView中
if(message!=null){
textView.setText(message);
}
}
}
检查一下代码,编译并运行这个应用。等待应用安装至模拟器后,你会看到如下的画面: