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中的内容:



    
    

接下来是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中的内容。


    

    

以下是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);
        }
    }
}

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

Android-事件处理与内部通信_第1张图片
演示.gif

参考文档

  • Android官方文档 - Intents and Intent Filters

你可能感兴趣的:(Android-事件处理与内部通信)