定义:
使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系,将这些对象形成一链条,并沿着这条链传递该请求,直到有对象处理它为止。
使用场景:
1、多个对象能够处理同一请求,具体处理则在运行时动态确定。
2、在请求矗立着不明确的时候向多个对象提交同一个请求。
3、需要动态制定一组对象处理请求。
大致示意图:
GIF11.gif可以看出,责任链模式的一个重点是:上一个处理对象必须含有下一个处理对象的引用,形成一个单向链表,直到事件被消费处理掉。
简答代码示意:
分为事件处理类和事件处理请求类,将事件处理类形成一个单向链表,根据事件处理和事件处理请求条件,来消费处理该事件。
抽象事件处理类:
/**
* 抽象事件处理类
* Created by Administrator on 2017/10/10.
*/
public abstract class HandlerTask {
public HandlerTask nextHandlerTask;
/**
* 对事件请求的处理
* @param abstractRequest
*/
public void handleRequest(AbstractRequest abstractRequest){
if(getHandleLevel()==abstractRequest.getRequestLevel()){
//如果事件处理等级和请求等级一样就进行处理
handle(abstractRequest);
}else{
//事件处理继承往下传递
if(nextHandlerTask!=null){
nextHandlerTask.handleRequest(abstractRequest);
}else{
System.out.println("----> 所有的处理对象都不能处理它");
}
}
}
/**
* 每个处理者的对象的具体处理方式
* @param abstractRequest 抽象的请求对象
*/
public abstract void handle(AbstractRequest abstractRequest);
/**
* 获取每个事件的处理等级
* @return 返回每个事件对应的处理等级
*/
public abstract int getHandleLevel();
}
具体的事件处理类都 extends 事件抽象处理类,根据需要给予事件不同的处理结果。
具体事件处理类:
/**
* 具体的事件处理类
* Created by Administrator on 2017/10/10.
*/
public class HandlerTask1 extends HandlerTask{
@Override
public void handle(AbstractRequest abstractRequest) {
System.out.println("----HandlerTask1 处理请求: "+abstractRequest.getRequestLevel());
}
@Override
public int getHandleLevel() {
return 1;
}
}
/**
* Created by Administrator on 2017/10/10.
*/
public class HandlerTask2 extends HandlerTask{
@Override
public void handle(AbstractRequest abstractRequest) {
System.out.println("----HandlerTask2 处理请求: "+abstractRequest.getRequestLevel());
}
@Override
public int getHandleLevel() {
return 2;
}
}
/**
* Created by Administrator on 2017/10/10.
*/
public class HandlerTask3 extends HandlerTask{
@Override
public void handle(AbstractRequest abstractRequest) {
System.out.println("----HandlerTask3 处理请求: "+abstractRequest.getRequestLevel());
}
@Override
public int getHandleLevel() {
return 3;
}
}
/**
* Created by Administrator on 2017/10/10.
*/
public class HandlerTask4 extends HandlerTask{
@Override
public void handle(AbstractRequest abstractRequest) {
System.out.println("----HandlerTask4 处理请求: "+abstractRequest.getRequestLevel());
}
@Override
public int getHandleLevel() {
return 4;
}
}
根据项目的实际需求建立相应的具体事件处理类。
事件处理对应的就是事件请求处理,根据事件处理类,建立对应的事件处理请求类,一一对应。
抽象事件处理请求类:
/**
* 抽象请求类
* Created by Administrator on 2017/10/10.
*/
public abstract class AbstractRequest {
private Object object;
public AbstractRequest(Object mObject){
this.object=mObject;
}
/**
* 获取具体的对象
* @return 返回具体的对象
*/
public Object getContent() {
return object;
}
/**
* 获取请求的级别
* @return 返回请求的级别
*/
public abstract int getRequestLevel();
}
具体事件处理请求类:
/**
* 具体的事件处理请求类
* Created by Administrator on 2017/10/10.
*/
public class RequestHandler1 extends AbstractRequest{
public RequestHandler1(Object mObject) {
super(mObject);
}
@Override
public int getRequestLevel() {
return 11;
}
}
/**
* 具体的事件处理请求类
* Created by Administrator on 2017/10/10.
*/
public class RequestHandler2 extends AbstractRequest{
public RequestHandler2(Object mObject) {
super(mObject);
}
@Override
public int getRequestLevel() {
return 12;
}
}
/**
* 具体的事件处理请求类
* Created by Administrator on 2017/10/10.
*/
public class RequestHandler3 extends AbstractRequest{
public RequestHandler3(Object mObject) {
super(mObject);
}
@Override
public int getRequestLevel() {
return 3;
}
}
/**
* 具体的事件处理请求类
* Created by Administrator on 2017/10/10.
*/
public class RequestHandler4 extends AbstractRequest{
public RequestHandler4(Object mObject) {
super(mObject);
}
@Override
public int getRequestLevel() {
return 13;
}
}
将建立好的事件处理类形成单向链表,根据对应的请求结果决定是否处理该对应的事件。
//确定链式关系
HandlerTask1 handlerTask1=new HandlerTask1();
HandlerTask2 handlerTask2=new HandlerTask2();
HandlerTask3 handlerTask3=new HandlerTask3();
HandlerTask4 handlerTask4=new HandlerTask4();
//上一个对象持有下一个的引用,形成链式关系
handlerTask1.nextHandlerTask=handlerTask2;
handlerTask2.nextHandlerTask=handlerTask3;
handlerTask3.nextHandlerTask=handlerTask4;
RequestHandler3 requestHandler3=new RequestHandler3("请求3");
handlerTask3.handleRequest(requestHandler3);
这样子就实现了一个简单的责任链模式,在实际项目开发中,责任链模式的使用还是比较多的,比较常见的就是Android的事件分发机制。
责任链模式——Android事件分发机制
在布局中点击一个按钮,activity、window、decorView三者之间首先得到事件响应的是activity,接着是window,再接着是decorView;
大致是一个这样子的传递:
Activity---->Window---->decorView
在点击按钮的时候,就会去调用activity中的dispatchTouchEvent方法;
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
接着就走到getWindow().superDispatchTouchEvent(ev),调用的是Window类中的
public abstract boolean superDispatchTouchEvent(MotionEvent event);
发现这是一个抽象方法,就要去找Window的实现类PhoneWindow
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
PhoneWindow类中superDispatchTouchEvent方法返回的是mDecor.superDispatchTouchEvent(event);调用的是DecorView中的superDispatchTouchEvent方法;
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
DecorView extends FrameLayout,而它返回的super.dispatchTouchEvent(event);,点进去就会到ViewGroup的dispatchTouchEvent方法;点击一个按钮,正常情况下,就是这样一步一步将事件传递下去,直到被消费处理掉。
示意图(正常情况下):
上面这个只是一个正常的事件流程,其实Android的事件是很复杂的,其中就包含事件拦截、事件分发、事件处理。
在点击按钮的时候,首先会调用activity中的dispatchTouchEvent,接着就会调用代码中的getWindow().superDispatchTouchEvent(ev);dispatchTouchEvent的返回值受到getWindow().superDispatchTouchEvent(ev);返回值的影响。
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
//如果getWindow().superDispatchTouchEvent(ev)返回的true,那么dispatchTouchEvent就直接返回true,否则就返回onTouchEvent(ev)
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
根据上面正常流程的分析会知道,事件的传递会一步一步走到ViewGroup里面的dispatchTouchEvent方法,关于PhoneWindow和DecorView中的调用可以看上面那张简单流程图;
ViewGroup中dispatchTouchEvent方法所有源码:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
// If the event targets the accessibility focused view and this is it, start
// normal event dispatch. Maybe a descendant is what will handle the click.
if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
ev.setTargetAccessibilityFocus(false);
}
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
cancelAndClearTouchTargets(ev);
resetTouchState();
}
// Check for interception.在这里会检测事件是否被拦截掉了
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
//这里会调用onInterceptTouchEvent方法,
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
// If intercepted, start normal event dispatch. Also if there is already
// a view that is handling the gesture, do normal event dispatch.
if (intercepted || mFirstTouchTarget != null) {
ev.setTargetAccessibilityFocus(false);
}
// Check for cancelation.
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
// Update list of touch targets for pointer down, if needed.
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {
//事件是否被拦截以及事件是否被取消,如果事件被拦截就不会走这里
//这里面就会进行子view事件的分发和处理
// If the event is targeting accessiiblity focus we give it to the
// view that has accessibility focus and if it does not handle it
// we clear the flag and dispatch the event to all children as usual.
// We are looking up the accessibility focused host to avoid keeping
// state since these events are very rare.
View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
? findChildWithAccessibilityFocus() : null;
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
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
// Clean up earlier touch targets for this pointer id in case they
// have become out of sync.
removePointersFromTouchTargets(idBitsToAssign);
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// Find a child that can receive the event.
// Scan children from front to back.
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
//获取子view的数量
final View[] children = mChildren;
//对子view进行遍历
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
// If there is a view that has accessibility focus we want it
// to get the event first and if not handled we will perform a
// normal dispatch. We may do a double iteration but this is
// safer given the timeframe.
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}
//判断点击的区域是否是子view的区域范围,如果不是就结束掉这次循环,继续下一次循环
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
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;
}
resetCancelNextUpFlag(child);
//将子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();
//addTouchTarget方法里面也采用了责任链设计模式
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
// The accessibility focus didn't handle the event, so clear
// the flag and do a normal dispatch to all children.
ev.setTargetAccessibilityFocus(false);
}
if (preorderedList != null) preorderedList.clear();
}
if (newTouchTarget == null && mFirstTouchTarget != null) {
// Did not find a child to receive the event.
// Assign the pointer to the least recently added target.
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
// Dispatch to touch targets.
//mFirstTouchTarget的首次赋值是在事件未被拦截里面,如果将事件拦截了,mFirstTouchTarget对象就不会进行复制,从而mFirstTouchTarget值就为null
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
//如果mFirstTouchTarget为null,就会调用dispatchTransformedTouchEvent方法,第三个参数传入的是一个ChildView这里没有,就直接传入一个null
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
// Dispatch to touch targets, excluding the new touch target if we already
// dispatched to it. Cancel touch targets if necessary.
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}
// Update list of touch targets for pointer up or cancel, if needed.
if (canceled
|| actionMasked == MotionEvent.ACTION_UP
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
resetTouchState();
} else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
final int actionIndex = ev.getActionIndex();
final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
removePointersFromTouchTargets(idBitsToRemove);
}
}
if (!handled && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
}
return handled;
}
事件传递到ViewGroup中的dispatchTouchEvent,就会先去检测该事件是否被拦截掉并调用ViewGroup中的onInterceptTouchEvent方法,onInterceptTouchEvent方法默认返回false,如果返回true代表的是将该事件拦截掉。
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
&& ev.getAction() == MotionEvent.ACTION_DOWN
&& ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
&& isOnScrollbarThumb(ev.getX(), ev.getY())) {
return true;
}
return false;
}
如果onInterceptTouchEvent返回true(拦截该事件),代码在走到下面的时候就不会走子view事件处理的代码块,而是走下面这个代码块;
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
//如果mFirstTouchTarget为null,就会调用dispatchTransformedTouchEvent方法,第三个参数传入的是一个ChildView这里没有,就直接传入一个null
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
}
就会去调用dispatchTransformedTouchEvent方法对事件进行处理;
dispatchTransformedTouchEvent方法全部源码:
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
// Canceling motions is a special case. We don't need to perform any transformations
// or filtering. The important part is the action, not the contents.
final int oldAction = event.getAction();
//事件是否被取消
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
// Calculate the number of pointers to deliver.
final int oldPointerIdBits = event.getPointerIdBits();
final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
// If for some reason we ended up in an inconsistent state where it looks like we
// might produce a motion event with no pointers in it, then drop the event.
if (newPointerIdBits == 0) {
return false;
}
// If the number of pointers is the same and we don't need to perform any fancy
// irreversible transformations, then we can reuse the motion event for this
// dispatch as long as we are careful to revert any changes we make.
// Otherwise we need to make a copy.
final MotionEvent transformedEvent;
if (newPointerIdBits == oldPointerIdBits) {
if (child == null || child.hasIdentityMatrix()) {
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
event.offsetLocation(offsetX, offsetY);
handled = child.dispatchTouchEvent(event);
event.offsetLocation(-offsetX, -offsetY);
}
return handled;
}
transformedEvent = MotionEvent.obtain(event);
} else {
transformedEvent = event.split(newPointerIdBits);
}
// Perform any necessary transformations and dispatch.
if (child == null) {
//如果子view为null,就会调用父类中的dispatchTouchEvent,其实就是调用View中的dispatchTouchEvent
handled = super.dispatchTouchEvent(transformedEvent);
} else {
//子view不为空的时候,在点击的时候就会根据mScrollX、mScrollY、view的child.mLeft、view的child.mTop计算子view的x和y,并通过offsetLocation()方法进行设置
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);
}
// Done.
transformedEvent.recycle();
return handled;
}
这个时候事件是被拦截掉的,就是没有子view,没有子view就会调用dispatchTransformedTouchEvent方法中的super.dispatchTouchEvent(transformedEvent);方法,其实调用的就是View中的dispatchTouchEvent方法,
View中dispatchTouchEvent方法所有源码:
public boolean dispatchTouchEvent(MotionEvent event) {
// If the event should be handled by accessibility focus first.
if (event.isTargetAccessibilityFocus()) {
// We don't have focus or no virtual descendant has it, do not handle the event.
if (!isAccessibilityFocusedViewOrHost()) {
return false;
}
// We have focus and got the event, then use normal event dispatch.
event.setTargetAccessibilityFocus(false);
}
boolean result = false;
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
final int actionMasked = event.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Defensive cleanup for new gesture
stopNestedScroll();
}
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
//这里如果设置了setOnTouchListener事件就会去调用onTouch方法
result = true;
}
//如果没有设置setOnTouchListener事件就会去调用onTouchEvent方法
if (!result && onTouchEvent(event)) {
result = true;
}
}
if (!result && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
// Clean up after nested scrolls if this is the end of a gesture;
// also cancel it if we tried an ACTION_DOWN but we didn't want the rest
// of the gesture.
if (actionMasked == MotionEvent.ACTION_UP ||
actionMasked == MotionEvent.ACTION_CANCEL ||
(actionMasked == MotionEvent.ACTION_DOWN && !result)) {
stopNestedScroll();
}
return result;
}
在父类(View)的dispatchTouchEvent方法中就会去调用onTouchEvent方法,也就是ViewGroup中重写onInterceptTouchEvent方法返回true为什么会拦截事件,并且该ViewGroup会消费了Event(调用onTouchEvent)。
下面是一个简单的流程示意图:
如果ViewGroup中并没有重写onInterceptTouchEvent或者onInterceptTouchEvent返回值为false(不拦截事件),就会走ViewGroup中dispatchTouchEvent()方法中的if(!canceled && !intercepted)代码块,具体的可以看上面的ViewGrop中dispatchTouchEvent方法的源码;在代码块里会根据子view的数量遍历,调用dispatchTransformedTouchEvent()方法去处理每个子view的事件情况,并会计算该子view点击的x和y。
责任链模式在Android事件分发中运用很多,且Android事件分发也比较复杂,上面这些仅是本人在学过程中的一个简单记录,如果问题,欢迎交流。
网友评论