当前位置:首页 » 安卓系统 » android事件传递机制

android事件传递机制

发布时间: 2022-06-09 01:25:38

⑴ android事件分发机制是什么设计模式

android事件分发机制 就是一个触摸事件发生了,从一个窗口传递到一个视图,再传递到另外一个视图,最后被消费的过程,在android中还是比较复杂的传递流程如下:

(1) 事件从Activity.dispatchTouchEvent()开始传递,只要没有被停止或拦截,从最上层的View(ViewGroup)开始一直往下(子View)传递。子View可以通过onTouchEvent()对事件进行处理。

(2) 事件由父View(ViewGroup)传递给子View,ViewGroup可以通过onInterceptTouchEvent()对事件做拦截,停止其往下传递。

(3) 如果事件从上往下传递过程中一直没有被停止,且最底层子View没有消费事件,事件会反向往上传递,这时父View(ViewGroup)可以进行消费,如果还是没有被消费的话,最后会到Activity的onTouchEvent()函数。

(4) 如果View没有对ACTION_DOWN进行消费,之后的其他事件不会传递过来。

(5) OnTouchListener优先于onTouchEvent()对事件进行消费。

上面的消费即表示相应函数返回值为true。

⑵ android onTouchEvent和setOnTouchListener中onTouch的区别

android中onTouchEvent和setOnTouchListener中onTouch的区别可以有些人并不了解,其实要说明白这个问题先要说下android的事件传递机制。首先看下以下的代码1,这段代码反应了View处理事件的过程:
代码1:
public boolean dispatchTouchEvent(MotionEvent event) {
if (mOnTouchListener != null && mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
在代码1中有一点首先需要明确:
1、如果dispatchTouchEvent返回值为true则本次事件被系统消耗掉(就是被控件处理了), 然后一个新的事件会被传入(如down事件返回true,则后续的move、up等事件也将被系统传入进行处理,否则move、up等事件不会响应);
2、如果dispatchTouchEvent返回值为false,则不会有新的事件被传入。
好,明白了这一点再看那个代码1中if语句后面的条件mOnTouchListener != null && mOnTouchListener.onTouch(this, event),mOnTouchListener 对象其实不就是你写的监听器对象吗?
比如下面代码2的这个匿名对象。
代码2:
xxxView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
好,我们再看下代码2中onTouch事件中的返回值:
1、如果代码2 return true则代表代码1中将不会执行 return onTouchEvent(event);语句,这也就是说这次系统事件会被消耗掉,将会再次执行dispatchTouchEvent这个方法。
2、如果代码2 return false则代表代码1会执行onTouchEvent(event);这个方法,本次事件是否会被消耗掉将取决于onTouchEvent的返回值。

总结:
1、如果setOnTouchListener中的onTouch方法返回值是true则onTouchEvent方法将不会被执行;
2、只有当setOnTouchListener中的onTouch方法返回值是false时onTouchEvent方法才被执行。
3、以上说的情况适用于View对象而不是ViewGroup对象,ViewGroup对象下次再分析。

1.onTouch方法:
onTouch方法是View的 OnTouchListener借口中定义的方法。
当一个View绑定了OnTouchLister后,当有touch事件触发时,就会调用onTouch方法。
(当把手放到View上后,onTouch方法被一遍一遍地被调用)

2.onTouchEvent方法:
onTouchEvent方法是override 的Activity的方法。
重新了Activity的onTouchEvent方法后,当屏幕有touch事件时,此方法就会别调用。
(当把手放到Activity上时,onTouchEvent方法就会一遍一遍地被调用)

3.touch事件的传递:
在一个Activity里面放一个TextView的实例tv,并且这个tv的属性设定为 fill_parent
在这种情况下,当手放到屏幕上的时候,首先会是tv响应touch事件,执行onTouch方法。

如果onTouch返回值为true,
表示这个touch事件被onTouch方法处理完毕,不会把touch事件再传递给Activity,
也就是说onTouchEvent方法不会被调用。
(当把手放到屏幕上后,onTouch方法被一遍一遍地被调用)

如果onTouch的返回值是false,
表示这个touch事件没有被tv完全处理,onTouch返回以后,touch事件被传递给Activity,
onTouchEvent方法被调用。
(当把手放到屏幕上后,onTouch方法调用一次后,onTouchEvent方法就会一遍一遍地被调用)

⑶ android怎样传递一个控件

public boolean dispatchTouchEvent(MotionEvent ev){}

用于事件的分发,Android中所有的事件都必须经过这个方法的分发,然后决定是自身消费当前事件还是继续往下分发给子控件处理。返回true表示不继续分发,事件没有被消费。

public boolean onInterceptTouchEvent(MotionEvent arg0){}

用于事件的处理,返回true表示消费处理当前事件,返回false则不处理,交给子控件进行继续分发。

public boolean onTouchEvent(MotionEvent arg0){}

负责事件的拦截,返回true的时候表示拦截当前事件,不继续往下分发,交给自身的onTouchEvent进行处理。返回false则不拦截,继续往下传。

举例说明三个方法之间的传递关系,加入界面如下图:



例:Android
Viewpage禁止滑动屏幕(如果是其他view可以自定义控件,然后重写这几个方法)

Android事件机制是从父View传向子View的,可以去检测你当前子View是不是在有可滑动控件等,决定事件是否拦截,但是这个麻烦,而且并不能解决所有的问题(必须检测触摸点是否在这个控件上面),其实有比较简单的方法,在你嵌套的控件中注入ViewPager实例,然后在onTouchEvent,onInterceptTouchEvent,dispatchTouchEvent里面告诉父View,也就是ViewPager不要拦截该控件上的触摸事件。

详细

⑷ Android onTouchEvent和setOnTouchListener中onTouch的区别

droid中onTouchEvent和setOnTouchListener中onTouch的区别可以有些人并不了解,其实要说明白这个问题先要说下android的事件传递机制。

⑸ android怎么完成事件调用事件

(1)首先由Activity分发,分发给根View,也就是DecorView(DecorView为整个Window界面的最顶层View)

(2)然后由根View分发到子的View

如下图所示:

⑹ android中touch事件的传递机制是怎样的

不多说,给你一个链接
http://www.cnblogs.com/virtual-young/p/4118890.html

⑺ android怎么实现后台对按键事件的监控

遥控器按键事件这个不是在Android源码的bootable下面ircon.c配置的么。比如:
{ .scancode = 0x0b, .mask = 0xaa0087ee,
.keycode = KEY_UP, .spec = IRCON_KEYCODE_NORMAL | IRCON_KEYCODE_LONGPRESS | IRCON_KEYCODE_MOUSEMD },
遥控器按键编号是“ 0x0b”,给他的响应是“KEY_UP”,就是方向键的下,这个是在源码里边配置的。你必须要有遥控器的书名数,知道遥控器每个按键的编号,然后对应给他相应的响应才可以。
但是你要操控手机,你的手机必须要能接受遥控器信号,这个也是要硬件支持的,一般的手机貌似都没有这个。
这个遥控的响应操作是驱动层做的,和应用层没什么关系,主要是驱动和硬件的支持。

⑻ 请简述什么是android事件处理,并分析两种android事件处理机制的实现过程和区别

UI编程通常都会伴随事件处理,Android也不例外,它提供了两种方式的事件处理:基于回调的事件处理和基于监听器的事件处理。

对于基于监听器的事件处理而言,主要就是为Android界面组件绑定特定的事件监听器;对于基于回调的事件处理而言,主要做法是重写Android组件特定的回调函数,Android大部分界面组件都提供了事件响应的回调函数,我们主要重写它们就行。


一 基于监听器的事件处理

相比于基于回调的事件处理,这是更具“面向对象”性质的事件处理方式。在监听器模型中,主要涉及三类对象:

1)事件源Event Source:产生事件的来源,通常是各种组件,如按钮,窗口等。

2)事件Event:事件封装了界面组件上发生的特定事件的具体信息,如果监听器需要获取界面组件上所发生事件的相关信息,一般通过事件Event对象来传递。

3)事件监听器Event Listener:负责监听事件源发生的事件,并对不同的事件做相应的处理。


基于监听器的事件处理机制是一种委派式Delegation的事件处理方式,事件源将整个事件委托给事件监听器,由监听器对事件进行响应处理。这种处理方式将事件源和事件监听器分离,有利于提供程序的可维护性。

举例:

View类中的OnLongClickListener监听器定义如下:(不需要传递事件)


[java] view plainprint?

public interface OnLongClickListener {

boolean onLongClick(View v);

}

public interface OnLongClickListener {
boolean onLongClick(View v);
}


View类中的OnLongClickListener监听器定义如下:(需要传递事件MotionEvent)

[java] view plainprint?

public interface OnTouchListener {

boolean onTouch(View v, MotionEvent event);

}

public interface OnTouchListener {
boolean onTouch(View v, MotionEvent event);
}

二 基于回调的事件处理

相比基于监听器的事件处理模型,基于回调的事件处理模型要简单些,该模型中,事件源和事件监听器是合一的,也就是说没有独立的事件监听器存在。当用户在GUI组件上触发某事件时,由该组件自身特定的函数负责处理该事件。通常通过重写Override组件类的事件处理函数实现事件的处理。

举例:

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

[java] view plainprint?

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的事件处理机制保证基于监听器的事件处理会优先于基于回调的事件处理被触发;

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


四 基于自定义监听器的事件处理流程

在实际项目开发中,我们经常需要自定义监听器来实现自定义业务流程的处理,而且一般都不是基于GUI界面作为事件源的。这里以常见的app自动更新为例进行说明,在自动更新过程中,会存在两个状态:下载中和下载完成,而我们的程序需要在这两个状态做不同的事情,“下载中”需要在UI界面上实时显示软件包下载的进度,“下载完成”后,取消进度条的显示。这里进行一个模拟,重点在说明自定义监听器的事件处理流程。

4.1)定义事件监听器如下:

⑼ 谁可以解释下,android事件分发为什么要设计成从根view到子view,而不是从子vie

Android事件传递流程在网上可以找到很多资料,FrameWork层输入事件和消费事件,可以参考:
Touch事件派发过程详解
这篇blog阐述了底层是如何处理屏幕输,并往上传递的。Touch事件传递到Activity的DecorView时,往下走就是ViewGroup和子View之间的事件传递,可以参考郭神的这两篇博客
Android事件分发机制完全解析,带你从源码的角度彻底理解(上)
Android事件分发机制完全解析,带你从源码的角度彻底理解(下)
郭神的两篇博客清楚明白地说明了View之间事件传递的大方向,但是具体的一些晦暗的细节阐述较少,本文主要是总结这两篇博客的同时,侧重于两点:
事件分发过程中一些细节到底如何实现的?
子view到底如何和父View抢事件,父View又是如何拦截事件不发送给子View,以及如果我们需要处理这种混乱的关系才能让两者和谐相处?。
MotionEvent抽象
要明白View的事件传递,很有必要先说一下Touch事件是如何在Android系统中抽象的,这主要使用的就是MotionEvent。这个类经历了几次重大的修改,一次是在2.x版本支持多点触摸,一次是4.x将大部分代码甩给native层处理。
一次简单的事件
我们先举个栗子来说明一次完整的事件,用户触屏 滑动 到手机离开屏幕,这认为是一次完整动作序列(movement traces)。一个动作序列中包含很多动作Action,比如在用户按下时,会封装一个MotionEvent,分发给视图树,我们可以通过motionevent.getAction拿到这个动作是ACTION_DOWN。同样,在手指抬起时,我们可以接收到Action类型是Action_UP的MotionEvent。对于滑动(MOVE)这个操作,Android为了从效率出发,会将多个MOVE动作打包到一个MotionEvent中。通过getX getY可以获取当前的坐标,如果要访问打包的缓存数据,可以通过getHistorical**()函数来获取。
加入多点触摸
对于单点的操作来看,MotionEvent显得比较简单,但是考虑引入多点触摸呢?我们定义一个接触点为(Pointer)。每一个触摸点Pointer都会有一个当次动作序列的唯一Id和Index.MotionEvent中多个手指的操作API大部分都是通过pointerindex来进行的,如:获取不同Pointer的触碰位置,getX(int pointerIndex);获取PointerId等等。大部分情况下,pointerid == pointeridex。
我们从onTouch接受到一个MotionEvent,怎么拿到多个触碰点的信息?为了解开笔者刚开始学习这部分知识时的困惑,我们首先树立起一种概念:一个MotionEvent只允许有一个Action(动作),而且这个Action会包含触发这次Action的触碰点信息,对于MOVE操作来说,一定是当前所有触碰点都在动。只有ACTION_POINTER_DOWN这类事件事件会在Action里面指定是哪一个POINTER按下。
在MotionEvent的底层实现中,是通过一个16位来存储Action和Pointer信息(PointerIndex)。低8位表示Action,理论上可以表示255种动作类型;高8位表示触发这个Action的PointerIndex,理论上Android最多可以支持255点同时触摸,但是在上层代码使用的时候,默认多点最多存在32个,不然事件在分发的时候会有问题。
ACTION_DOWN OR ACTION_POINTER_DOWN:
这两个按下操作的区别是ACTION_DOWN是一个系列动作的开始,而ACTION_POINTER_DOWN是在一个系列动作中间有另外一个触碰点触碰到屏幕。
这部分详细的描述,请参考:
android触控,先了解MotionEvent
到这里,铺垫终于结束了,我们开始直奔主题。
View的事件传递
Android的Touch事件传递到Activity顶层的DecorView(一个FrameLayout)之后,会通过ViewGroup一层层往视图树的上面传递,最终将事件传递给实际接收的View。下面给出一些重要的方法。如果你对这个流程比较熟悉的话,可以跳过这里,直接进入第二部分。
dispatchTouchEvent
事件传递到一个ViewGroup上面时,会调用dispatchTouchEvent。代码有删减
public boolean dispatchTouchEvent(MotionEvent ev) {

boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;

// Attention 1 :在按下时候清除一些状态
if (actionMasked == MotionEvent.ACTION_DOWN) {
cancelAndClearTouchTargets(ev);
//注意这个方法
resetTouchState();
}

// Attention 2:检查是否需要拦截
final boolean intercepted;
//如果刚刚按下 或者 已经有子View来处理
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// 不是一个动作序列的开始 同时也没有子View来处理,直接拦截
intercepted = true;
}

//事件没有取消 同时没有被当前ViewGroup拦截,去找是否有子View接盘
if (!canceled && !intercepted) {
//如果这是一系列动作的开始 或者有一个新的Pointer按下 我们需要去找能够处理这个Pointer的子View
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int actionIndex = ev.getActionIndex(); // always 0 for down

//上面说的触碰点32的限制就是这里导致
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;

final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);

//对当前ViewGroup的所有子View进行排序,在上层的放在开始
final ArrayList<View> preorderedList = buildOrderedChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = customOrder
? getChildDrawingOrder(childrenCount, i) : i;
final View child = (preorderedList == null)
? children[childIndex] : preorderedList.get(childIndex);

// canViewReceivePointerEvents visible的View都可以接受事件
// isTransformedTouchPointInView 计算是否落在点击区域上
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}

//能够处理这个Pointer的View是否已经处理之前的Pointer,那么把
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
// Child is already receiving touch within its bounds.
// Give it the new pointer in addition to the ones it is handling.
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
} }
//Attention 3 : 直接发给子View
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// childIndex points into presorted list, find original index
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
= true;
break;
}

}
}

}
}

// 前面已经找到了接收事件的子View,如果为NULL,表示没有子View来接手,当前ViewGroup需要来处理
if (mFirstTouchTarget == null) {
// ViewGroup处理
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {

if() {
//ignore some code
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
}

}
return handled;
}

上面代码中的Attention在后面部分将会涉及,重点注意。
这里需要指出一点的是,一系列动作中的不同Pointer可以分配给不同的View去响应。ViewGroup会维护一个PointerId和处理View的列表TouchTarget,一个TouchTarget代表一个可以处理Pointer的子View,当然一个View可以处理多个Pointer,比如两根手指都在某一个子View区域。TouchTarget内部使用一个int来存储它能处理的PointerId,一个int32位,这也就是上层为啥最多只能允许同时最多32点触碰。
看一下Attention 3 处的代码,我们经常说view的dispatchTouchEvent如果返回false,那么它就不能系列动作后面的动作,这是为啥呢?因为Attention 3处如果返回false,那么它不会被记录到TouchTarget中,ViewGroup认为你没有能力处理这个事件。
这里可以看到,ViewGroup真正处理事件是在dispatchTransformedTouchEvent里面,跟进去看看:
dispatchTransformedTouchEvent
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {

//没有子类处理,那么交给viewgroup处理
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}

handled = child.dispatchTouchEvent(transformedEvent);
}
return handled;
}

可以看到这里不管怎么样,都会调用View的dispatchTouchEvent,这是真正处理这一次点击事件的地方。
dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent event) {
if (onFilterTouchEventForSecurity(event)) {
//先走View的onTouch事件,如果onTouch返回True
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}

if (!result && onTouchEvent(event)) {
result = true;
}
}
return result;
}

我们给View设置的onTouch事件处在一个较高的优先级,如果onTouch执行返回true,那么就不会去走view的onTouchEvent,而我们一些点击事件都是在onTouchEvent中处理的,这也是为什么onTouch中返回true,view的点击相关事件不会被处理。

⑽ 安卓 View层 和 Activity层 消息传递机制和焦点问题

其实是activity派送出来的事件,不是无人知晓。而是这种问题不容易弄出一个关键字来搜索。

一个activity的touch事件先由activity的dispatchtouchevent来处理,负责派送。

dispatchtouchevent把ACTION_DOWN事件首先交给view,然后view返回false再传递回activity的onTouchEvent中处理。

具体的部分代码如下:

if(ev.getAction()==MotionEvent.ACTION_DOWN){
onUserInteraction();
}


如果用户需要这个ACTION_DOWN,应该在自身的onTouchEvent中对 ACTION_DOWN采取return true的响应,表示这个ACTION_DOWN归我了。只有在dispatchtouchevent为ACTION_DOWN事件找到一个归属,也即是target之后,后面的ACTION_MOVE和ACTION_UP才会对这个target传送。


如果你这个页面就仅仅有一个这样需要捕捉事件的view,你是可以直接在dispatchtouchevent中直接传送给这个view然后return true的。

热点内容
app什么情况下找不到服务器 发布:2025-05-12 15:46:25 浏览:714
php跳过if 发布:2025-05-12 15:34:29 浏览:467
不定时算法 发布:2025-05-12 15:30:16 浏览:131
c语言延时1ms程序 发布:2025-05-12 15:01:30 浏览:166
动物园灵长类动物配置什么植物 发布:2025-05-12 14:49:59 浏览:736
wifi密码设置什么好 发布:2025-05-12 14:49:17 浏览:148
三位数乘两位数速算法 发布:2025-05-12 13:05:48 浏览:397
暴风影音缓存在哪里 发布:2025-05-12 12:42:03 浏览:542
access数据库exe 发布:2025-05-12 12:39:04 浏览:630
五开的配置是什么 发布:2025-05-12 12:36:37 浏览:365