Android-事件处理与内部通信

程序猿的开心一刻###

以下谁是二进制思想的最早提出者?
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类中,还有更多诸如OnHoverListenerOnDragListener之类的事件监听器可供使用。所以为了开发具有更加丰富功能的应用,你需要去查阅和参考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中声明两种方法来注册事件监听器。

主要有以下步骤:

  1. 请使用Android Studio来创建一个项目。
  2. 除了MainActivity,请通过菜单再创建一个名为SecondActivity的Activity。
  3. res/layout/activity_main.xml文件中的布局里,为MainActivity添加两个主要的控件:一个文本框EditText、一个按钮Button,并设置它们的属性(详细代码请参考后文)。
  4. res/layout/activity_second.xml文件中的布局里,为SecondActivity添加两个主要的控件:一个按钮Button和一个文本标签TextView,并设置它们的属性(详细代码请参考后文)。特别注意在按钮的属性中添加一个android:onClick="show"来注册监听器。
  5. 修改MainActivity.java文件,在MainActivity类中声明刚刚添加的控件,在onCreate()方法内对它们进行实例化(使用findViewById()方法),对按钮以匿名内部类的方式添加点击事件监听器,在onClick方法中获取文本框的输入值,通过Bundle对象存入Intent,并以这个Intent对象启动SecondActivity
  6. 修改SecondActivity.java文件中,实现已注册的show方法,在其中获取MainActivity传过来的值,并显示在TextView中。
  7. 检查代码,编译并运行这个应用,在模拟器中查看运行的过程,检查其是否与设计的相一致。

下面是部分要用到的代码:

首先是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);
        }
    }
}

检查一下代码,编译并运行这个应用。等待应用安装至模拟器后,你会看到如下的画面:

演示.gif

参考文档

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,368评论 25 707
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,344评论 0 17
  • 1.什么是Activity?问的不太多,说点有深度的 四大组件之一,一般的,一个用户交互界面对应一个activit...
    JoonyLee阅读 5,724评论 2 51
  • 哎呀呀 ,马上就要面临找工作了,媛媛心里紧张呀. 作为一个即将毕业的Android程序媛,开始面临找工作了,...
    仇诺伊阅读 4,526评论 7 59
  • (一)刚刚下课,老师就出了教室。黑板上还残留着上课时的痕迹,白色的粉笔印混合着不久前因为洗过黑板还未干的水,一片糟...
    1111柠檬汁阅读 349评论 0 0