Framework事件机制—Android事件处理的三种方法

1.1.背景

Android的事件处理的三种方法:

1、基于监听的事件处理机制

setOnClickListener,setOnLongClickListener、setOnTouchListener

注意:如果onTouchEvent方法return true,则单击事件和长摁事件不再执行;若onLongClick方法返回true,则单击事件不再处理。

2、基于回调的事件处理机制

需要定义继承组件的类,重写回调方法Touch方法执行时,先被Activity捕获,DispatchTouchEvent方法处理。return false,交给上层的onTouchEvent方法处理;return super.dispatchTouchEvent(ev),则传递给最外层的View。

View用Dispatch方法处理,return false,由上层的onTouchEvent方法处理。如果返回super.dispatchTouchEvent(ev),则本层的onInterceptTouchEvent拦截,如果拦截true,则拦截,false不拦截,传递给子View的DispatchTouchEvent处理。

常用的回调方法:onKeyDown,onKeyLongPress,onKeyUp,onTouchEvent,onTrackballEvent(轨迹球事件)监听和回调同时存在时,先调用监听。

1.2.Android基于监听

基于监听的时间处理机制模型

流程模型图

监听三要素:

Event source 事件源 Event 事件 Event Listener 事件监听器 下面我们来看一下点击事件和触摸事件的监听三要素具体是那部分:

  • 点击时间( 由于点击事件比较简单,系统已经帮我们处理了,并没有找到具体事件是哪个 )
  • 触摸事件

归纳:

事件监听机制是一种委派式的事件处理机制,事件源(组件)事件处理委托给事件监听器 当事件源发生指定事件时,就通知指定事件监听器,执行相应的操作

常⽤监听接⼝

View.OnClickListener 单击事件监听器必须实现的接⼝ View.OnCreateContextMenuListener 创建上下⽂菜单事件 View.OnFocusChangeListener 焦点改变事件 View.OnKeyListener 按键事件监听器 View.OnLongClickListener 长按事件监听器 View.OnTouchListener 触摸屏事件监听器

  • 基于监听的事件处理机制

⾸先,事件监听机制中由事件源,事件,事件监听器三类对象组成。

事件监听器处理流程:

  1. 为事件源(例如:button)设置⼀个监听器,⽤于监听⽤户的操作(点击操作等)
  2. ⽤户做出的操作触发事件源的监听器
  3. ⾃动⽣成对应的事件对象
  4. 将事件源对象作为参数传给事件监听器
  5. 事件监听器对事件对象进⾏判断,执⾏对应的事件处理器(处理⽅法)在此以OnClickListener单击事件为例使用intent来实现页面的跳转

内部类形式实现监听

<TextView
//id值
  android:
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_marginTop="10dp"
  android:text="内部类"
  android:gravity="center"
  android:textSize="20dp"
  android:textColor="#fff"/>

public class MainActivity extends AppCompatActivity{

//定义一个TextView对象
 private TextView textView2;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
//获得事件源
  textView = findViewById(R.id.test2);
//确定事件为点击事件,绑定监听器到事件源
  textView.setOnClickListener(new myListener());
 }
 //内部类实现页面跳转
 private class myListener implements View.OnClickListener {
  @Override
  public void onClick(View v) {
 //采用显示Intent启动第二个页面
startActivity(new Intent(MainActivity.this,internalActivity.class));
  }
 }
}

匿名内部类实现

<TextView
  android:
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_marginTop="10dp"
  android:text="匿名内部类"
  android:gravity="center"
  android:textSize="20dp"
  android:textColor="#fff"/>

public class MainActivity extends AppCompatActivity {

 private TextView textView1;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
//获得事件源
  textView1 = findViewById(R.id.test3);
  //匿名内部类实现跳转 (实现监听器,绑定监听器到事件源要同步进行)
  textView1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
 startActivity(new Intent(MainActivity.this,anonymousActivity.class));
}
  });
 }
}

类本身实现监听器

<TextView
  android:
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_marginTop="10dp"
  android:text="类本身打开浏览器"
  android:gravity="center"
  android:textSize="20dp"
  android:textColor="@color/colorWhite"/>

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

 private TextView textView2;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  //获得事件源
  textView2 = findViewById(R.id.test4);
  //绑定监听器到事件源
  textView2.setOnClickListener(this);
 }
//类本身实现 浏览器跳转
 @Override
 public void onClick(View v) {
  switch (v.getId()){
case R.id.test4:
//采用隐式intent
 Intent intent = new Intent();
 intent.setAction(Intent.ACTION_VIEW);
 intent.setData(Uri.parse("http://www.baidu.com"));
 startActivity(intent);
 break;
  }
 }
}

本节给大家介绍了Android中的事件处理机制,例子中的是onClickListener点击事件,当然除了这个以外还有其他的事件,比如onItemClickListener,凡是需要通过setXxxListener这些,基本上都是基于事件监听的!

1.3.Android基于回调

回调事件处理原理

监听事件处理是事件源与事件监听器分开的而基于回调的事件处理UI组件不但是事件源,而且还是事件监听器,通过组件的相关回调方法处理对应的事件。

回调事件应用步骤

Ⅰ. 自定义View类,继承自需要的View UI类。ex :自定义 MyButton按钮类 extends 基础Button类

Ⅱ. 复写回调函数。ex:public boolean onTouchEvent(MotionEvent event)

每一个事件回调方法都会返回一个boolean值,①.如果返回true:表示该事件已被处理,不再继续向外扩散,②.如果返回false:表示事件继续向外扩散

而说到基于回调就离不开监听机制

回调机制与监听机制的区别:

如果说事件监听机制是⼀种委托式的事件处理,那么回调机制则恰好与之相反:对于基于回调机制的事件处理模型来说,事件源与事件监听器是统⼀的,或者说事件监听器完全消失了。

当⽤户在GUI组件上激发某个事件时,组件⾃⼰特定的⽅法将会负责处理该事件。

监听机制的事件源与事件监听是分开的。我们需要自己设置一个监听器,回调机制的事件源与事件监听是绑定在一起的。

  • boolean类型

几乎所有基于回调的事件处理方法都有一个boolean类型的返回值,该返回值用于表示该处理方法是否能完全处理该事件。 如果处理事件的回调方法返回true,表明该处理方法已经完全处理改事件,该事件不会传播出去。 如果处理事件的回调方法返回false,表明该处理方法并未完全处理该事件,该事件会传播出去。 对于基于回调的时间传播而言,某组件上所发生的事件不仅会激发该组件上的回调方法,也会触发该组件所在Activity的回调方法——只要事件能传播到该Activity。

实例:

MyButton 子类

public class MyButton extends AppCompatButton {

 public MyButton(Context context , AttributeSet set)
 {

  super(context , set);
 }
 @Override
 public boolean onKeyDown(int keyCode, KeyEvent event)
 {

  super.onKeyDown(keyCode , event);
  Log.v("-MyButton-", "the onKeyDown in MyButton");
  // 返回false,表明并未完全处理该事件,该事件依然向外扩散
  return true;
 }
}
  • MainActivity
public class MainActivity extends AppCompatActivity {


 @Override
 public void onCreate(Bundle savedInstanceState)
 {

  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  Button bn = (Button) findViewById(R.id.bn);
  Log.v("-Listener-", "the onKeyDown in Listener");
  // 为bn绑定事件监听器
  bn.setOnKeyListener(new OnKeyListener() {

@Override
public boolean onKey(View source
  , int keyCode, KeyEvent event) {

 // 只处理按下键的事件
 if (event.getAction() == KeyEvent.ACTION_DOWN) {

  Log.v("-Listener-", "the onKeyDown in Listener");
 }
 // 返回false,表明该事件会向外传播
 return false; 
}
  });
 }
 // 重写onKeyDown方法,该方法可监听它所包含的所有组件的按键被按下事件
 @Override
 public boolean onKeyDown(int keyCode, KeyEvent event)
 {

  super.onKeyDown(keyCode , event);
  Log.v("-Activity-" , "the onKeyDown in Activity");
  //返回false,表明并未完全处理该事件,该事件依然向外扩散
  return true;
 }
}

这里是在模拟器里进行的测试,这里按下键盘(而不是点击),会看到 logcat 中的输出,如下:

V/-Listener-: the onKeyDown in Listener
V/-MyButton-: the onKeyDown in MyButton
V/-Activity-: the onKeyDown in Activity
  • Override组件类的事件处理函数实现事件的处理。
举例:

View类实现了KeyEvent.Callback接口中的一系列回调函数,因此,基于回调的事件处理机制通过自定义View来实现,自定义View时重写这些事件处理方法即可。

public interface Callback { 
  // 几乎所有基于回调的事件处理函数都会返回一个boolean类型值,该返回值用于 
  // 标识该处理函数是否能完全处理该事件 ` `// 返回true,表明该函数已完全处理该事件,该事件不会传播出去 
  // 返回false,表明该函数未完全处理该事件,该事件会传播出去 
booleanonKeyDown( int keyCode, KeyEvent event); 
boolean onKeyLongPress( int keyCode, KeyEvent event); 
boolean onKeyUp( int keyCode, KeyEvent event); 
booleanonKeyMultiple( int keyCode, int count, KeyEvent event); 
}
  public interface Callback {
// 几乎所有基于回调的事件处理函数都会返回一个boolean类型值,该返回值用于
// 标识该处理函数是否能完全处理该事件
// 返回true,表明该函数已完全处理该事件,该事件不会传播出去
// 返回false,表明该函数未完全处理该事件,该事件会传播出去
 boolean onKeyDown(int keyCode, KeyEvent event);
boolean onKeyLongPress( int keyCode, KeyEvent event);
 boolean onKeyUp( int keyCode, KeyEvent event);   boolean onKeyMultiple( int keyCode,  int count, KeyEvent event); 
}
  public interface Callback { 
 
  // 几乎所有基于回调的事件处理函数都会返回一个boolean类型值,该返回值用于 
 
  // 标识该处理函数是否能完全处理该事件 
 
// 返回true,表明该函数已完全处理该事件,该事件不会传播出去 
 
// 返回false,表明该函数未完全处理该事件,该事件会传播出去 
 
boolean onKeyDown(int keyCode, KeyEvent event); 
 
boolean onKeyLongPress(int keyCode, KeyEvent event); 
 
boolean onKeyUp(int keyCode, KeyEvent event); 
 
boolean onKeyMultiple(int keyCode, int count, KeyEvent event); 
 
} 
 
public interface Callback {
  // 几乎所有基于回调的事件处理函数都会返回一个boolean类型值,该返回值用于
  // 标识该处理函数是否能完全处理该事件
// 返回true,表明该函数已完全处理该事件,该事件不会传播出去
// 返回false,表明该函数未完全处理该事件,该事件会传播出去
 boolean onKeyDown(int keyCode, KeyEvent event);
 boolean onKeyLongPress(int keyCode, KeyEvent event);
 boolean onKeyUp(int keyCode, KeyEvent event);
 boolean onKeyMultiple(int keyCode, int count, KeyEvent event);
}

比对

  • 基于监听器的事件模型符合单一职责原则,事件源和事件监听器分开实现。

  • Android的事件处理机制保证基于监听器的事件处理会优先于基于回调的事件处理被触发。

  • 某些特定情况下,基于回调的事件处理机制会更好的提高程序的内聚性。

1.4.Handler消息处理

什么是Handler

Handler是一个消息分发对象。

Handler是Android系统提供的一套用来更新UI的机制,也是一套消息处理机制,可以通过Handler发消息,也可以通过Handler处理消息。

Handler的工作原理

在下面介绍Handler机制前,首先得了解以下几个概念:

1.Message 消息,理解为线程间通讯的数据单元。例如后台线程在处理数据完毕后需要更新UI,则可发送一条包含更新信息的Message给UI线程。 Message Queue 消息队列,用来存放通过Handler发布的消息,按照先进先出执行。
2.Handler Handler是Message的主要处理者,负责将Message添加到消息队列以及对消息队列中的Message进行处理。
3.Looper 循环器,扮演Message Queue和Handler之间桥梁的角色,循环取出Message Queue里面的Message,并交付给相应的Handler进行处理。 线程 UI thread 通常就是main thread,而Android启动程序时会替它建立一个Message Queue。每一个线程里可含有一个Looper对象以及一个MessageQueue数据结构。在你的应用程序里,可以定义Handler的子类别来接收Looper所送出的消息。

Handler的运行流程

在子线程执行完耗时操作,当Handler发送消息时,将会调用 MessageQueue.enqueueMessage,向消息队列中添加消息。 当通过 Looper.loop开启循环后,会不断地从消息池中读取消息,即调用 MessageQueue.next, 然后调用目标Handler(即发送该消息的Handler)的 dispatchMessage方法传递消息, 然后返回到Handler所在线程,目标Handler收到消息,调用 handleMessage方法,接收消息,处理消息。

3.1.5.源码分析

在子线程创建Handler

class LooperThread extends Thread {

 public Handler mHandler;
 public void run() {

  Looper.prepare();
  mHandler = new Handler() {

public void handleMessage(Message msg) {

 // process incoming messages here
}
  }
  ;
  Looper.loop();
 }
}

从上面可以看出,在子线程中创建Handler之前,要调用 Looper.prepare()方法,Handler创建后,还要调用 Looper.loop()方法。而前面我们在主线程创建Handler却不要这两个步骤,因为系统帮我们做了。

主线程的Looper

在ActivityThread的main方法,会调用 Looper.prepareMainLooper()来初始化Looper,并调用 Looper.loop()方法来开启循环。

public final class ActivityThread extends ClientTransactionHandler {

 // ... 
 public static void main(String[] args) {

  // ... 
  Looper.prepareMainLooper();
  // ... 
  Looper.loop();
 }
}

1.5.Looper

从上可知,要使用Handler,必须先创建一个Looper。

初始化looper:

public final class Looper {

 public static void prepare() {

  prepare(true);
 }
 private static void prepare(Boolean quitAllowed) {

  if (sThreadLocal.get() != null) {

throw new RuntimeException("Only one Looper may be created per thread");
  }
  sThreadLocal.set(new Looper(quitAllowed));
 }
 public static void prepareMainLooper() {

  prepare(false);
  synchronized (Looper.class) {

if (sMainLooper != null) {

 throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
  }
 }
 private Looper(Boolean quitAllowed) {

  mQueue = new MessageQueue(quitAllowed);
  mThread = Thread.currentThread();
 }
 // ...
}

从上可以看出,不能重复创建Looper,每个线程只能创建一个。创建Looper,并保存在 ThreadLocal。其中ThreadLocal是线程本地存储区(Thread Local Storage,简称TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域。

开启Looper

public final class Looper {

 // ... 
 public static void loop() {

  // 获取TLS存储的Looper对象 
  final Looper me = myLooper();
  if (me == null) {

throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
  }
  final MessageQueue queue = me.mQueue;
  // 进入loop主循环方法 
  for (;;) {

Message msg = queue.next();
// 可能会阻塞,因为next()方法可能会无线循环 
if (msg == null) {

 // No message indicates that the message queue is quitting. 
 return;
}
// This must be in a local variable, in case a UI event sets the logger 
final Printer logging = me.mLogging;
if (logging != null) {

 logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
// ... 
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
try {

 // 获取msg的目标Handler,然后分发Message 
 msg.target.dispatchMessage(msg);
 dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
}
finally {

 if (traceTag != 0) {

  Trace.traceEnd(traceTag);
 }
}
// ... 
msg.recycleUnchecked();
  }
 }
}

1.6.Handler

创建Handler

public class Handler {

 // ... 
 public Handler() {

  this(null, false);
 }
 public Handler(Callback callback, Boolean async) {

  // ... 
  // 必须先执行Looper.prepare(),才能获取Looper对象,否则为null 
  mLooper = Looper.myLooper();
  if (mLooper == null) {

throw new RuntimeException( 
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
  }
  mQueue = mLooper.mQueue;
  // 消息队列,来自Looper对象 
  mCallback = callback;
  // 回调方法 
  mAsynchronous = async;
  // 设置消息是否为异步处理方式
 }
}

发送消息:

子线程通过Handler的post()方法或send()方法发送消息,最终都是调用 sendMessageAtTime()方法。

post方法:

public final Boolean post(Runnable r){

 return sendMessageDelayed(getPostMessage(r), 0);
}
public final Boolean postAtTime(Runnable r, long uptimeMillis){

 return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final Boolean postAtTime(Runnable r, Object token, long uptimeMillis){

 return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
public final Boolean postDelayed(Runnable r, long delayMillis){

 return sendMessageDelayed(getPostMessage(r), delayMillis);
}
private static Message getPostMessage(Runnable r) {

 Message m = Message.obtain();
 m.callback = r;
 return m;
}

send方法

public final Boolean sendMessage(Message msg){

 return sendMessageDelayed(msg, 0);
}
public final Boolean sendEmptyMessage(int what){

 return sendEmptyMessageDelayed(what, 0);
}
public final Boolean sendEmptyMessageDelayed(int what, long delayMillis) {

 Message msg = Message.obtain();
 msg.what = what;
 return sendMessageDelayed(msg, delayMillis);
}
public final Boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {

 Message msg = Message.obtain();
 msg.what = what;
 return sendMessageAtTime(msg, uptimeMillis);
}
public final Boolean sendMessageDelayed(Message msg, long delayMillis){

 if (delayMillis < 0) {

  delayMillis = 0;
 }
 return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
sendMessageAtTime()
public Boolean sendMessageAtTime(Message msg, long uptimeMillis) {

 MessageQueue queue = mQueue;
 if (queue == null) {

  RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
  Log.w("Looper", e.getMessage(), e);
  return false;
 }
 return enqueueMessage(queue, msg, uptimeMillis);
}
private Boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {

 msg.target = this;
 if (mAsynchronous) {

  msg.setAsynchronous(true);
 }
 return queue.enqueueMessage(msg, uptimeMillis);
}

1.7.分发消息

loop()方法中,获取到下一条消息后,执行 msg.target.dispatchMessage(msg),来分发消息到目标Handler。

public class Handler {

 // ... 
 public void dispatchMessage(Message msg) {

  if (msg.callback != null) {

// 当Message存在回调方法,调用该回调方法 
handleCallback(msg);
  } else {

if (mCallback != null) {

 // 当Handler存在Callback成员变量时,回调其handleMessage()方法 
 if (mCallback.handleMessage(msg)) {

  return;
 }
}
// Handler自身的回调方法 
handleMessage(msg);
  }
 }
 private static void handleCallback(Message message) {

  message.callback.run();
 }
}

1.8.Handler的简单使用

在子线程中,进行耗时操作,执行完操作后,发送消息,通知主线程更新UI。

public class Activity extends android.app.Activity {

 private Handler mHandler = new Handler(){

  @Override  
 public void handleMessage(Message msg) {

super.handleMessage(msg);
// 更新UI
  }
 }
 ;
 @Override 
 public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {

  super.onCreate(savedInstanceState, persistentState);
  setContentView(R.layout.activity_main);
  new Thread(new Runnable() {

@Override
 public void run() {

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

推荐阅读更多精彩内容

  • 与界面编程紧密相关的就是事件处理机制,当用户在程序界面上执行各种操作时,应用程序必须为用户动作提供响应动作,这种响...
    GB_speak阅读 2,308评论 0 3
  • 以下文章为转载,对理解JavaScript中的事件处理机制很有帮助,浅显易懂,特分享于此。 什么是事件? 事件(E...
    jxyjxy阅读 3,023评论 1 10
  • 一.基于监听的事件处理 在事件监听的处理模型中,主要涉及如下三类对象: Event Source(事件源):事件发...
    哇楼主阅读 966评论 0 0
  • 前言 你的时间有限,不要浪费于重复别人的生活。不要让别人的观点淹没了你内心的声音。 Android事件处理概述 A...
    olaH阅读 1,912评论 0 2
  • 1. 基于监听的事件处理 对象内容EvenSource事件源产生事件的控件即事件发生的源头Event事件封装操作的...
    知向谁边阅读 1,342评论 0 0