Android 事件分发机制

来源:互联网 发布:化工设计软件 编辑:程序博客网 时间:2024/06/01 19:45
</pre>Android 事件分发机制重点及相关我们只在view 中执行onTouchEvent 实现业务逻辑,然而我们要在ViewGroup 执行dispatchTouchEvent方法负责事件的分发。所以上面的概括可以更简单一些:ViewGroup  负责事件分发的相关事件 :  dispatchTouchEventView  执行onTouchEvent事件  相关事件: onTouchEvent  <p>图片:</p><p><img src="http://img.blog.csdn.net/20160615134832625?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" /></p>上述例子中的消息下发顺序是这样的:①-②-⑤-⑥-⑦-③-④。dispatchTouchEvent方法只负责事件的分发,它拥有boolean类型的返回值,当返回为true时,顺序下发会中断。分发的目的是为了找到真正要处理本次完整触摸事件的View,这个View会在onTouchuEvent结果返回true。所以返回true 说明已经找到了真正要处理本次完整触摸事件的View。====华丽的分割线===注意:以上说的只是经常用到情况:当然也有特别的情况,其实ViewGroup的相关事件有三个:onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent。View的相关事件只有两个:dispatchTouchEvent、onTouchEvent。具体的就不写了,其实ViewGroup 继承自View 它其实也是通过调用View的dispatchTouchEvent方法来执行onTouchEvent事件====华丽的分割线===触摸事件由Action_Down、Action_Move、Aciton_UP组成,其中一次完整的触摸事件中,Down和Up都只有一个,Move有若干个,可以为0个代码<pre style="font-family:宋体; font-size:9pt; background-color:rgb(255,255,255)"><pre style="font-family:宋体; font-size:9pt; background-color:rgb(255,255,255)"><pre name="code" class="java">/** * Handles touch events by activating automatic scrolling, adjusting scroll * velocity, or stopping. * <p> * If {@link #isExclusive()} is false, always returns false so that * the host view may handle touch events. Otherwise, returns true when * automatic scrolling is active and false otherwise. */@Overridepublic boolean onTouch(View v, MotionEvent event) {    if (!mEnabled) {        return false;    }    final int action = MotionEventCompat.getActionMasked(event);    switch (action) {        case MotionEvent.ACTION_DOWN:            mNeedsCancel = true;            mAlreadyDelayed = false;            // $FALL-THROUGH$        case MotionEvent.ACTION_MOVE:            final float xTargetVelocity = computeTargetVelocity(                    HORIZONTAL, event.getX(), v.getWidth(), mTarget.getWidth());            final float yTargetVelocity = computeTargetVelocity(                    VERTICAL, event.getY(), v.getHeight(), mTarget.getHeight());            mScroller.setTargetVelocity(xTargetVelocity, yTargetVelocity);            // If the auto scroller was not previously active, but it should            // be, then update the state and start animations.            if (!mAnimating && shouldAnimate()) {                startAnimating();            }            break;        case MotionEvent.ACTION_UP:        case MotionEvent.ACTION_CANCEL:            requestStop();            break;    }    return mExclusive && mAnimating;}


简单介绍一下onInterceptTouchEvent

我们先看一段代码

View mTarget=null;//保存捕获Touch事件处理的View    public boolean dispatchTouchEvent(MotionEvent ev) {        //....其他处理,在此不管                if(ev.getAction()==KeyEvent.ACTION_DOWN){            //每次Down事件,都置为Null            if(!onInterceptTouchEvent()){            mTarget=null;            View[] views=getChildView();            for(int i=0;i<views.length;i++){                if(views[i].dispatchTouchEvent(ev))                    mTarget=views[i];                    return true;            }          }        }        //当子View没有捕获down事件时,ViewGroup自身处理。这里处理的Touch事件包含Down、Up和Move        if(mTarget==null){            return super.dispatchTouchEvent(ev);        }        //...其他处理,在此不管        if(onInterceptTouchEvent()){         //...其他处理,在此不管             }//这一步在Action_Down中是不会执行到的,只有Move和UP才会执行到。        return mTarget.dispatchTouchEvent(ev);    }

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">从上述代码可以得知</span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">: 事件分发机制找到真正要处理本次完整触摸事件的View 为: View mTarget; 只有在ev.getAction()==KeyEvent.ACTION_DOWN 时,mTarget被赋值</span>

假如我们在某个ViewGroup的onInterceptTouchEvent中,将Action为Down的Touch事件返回true,那便表示将该ViewGroup的所有下发操作拦截掉,这种情况下,mTarget会一直为null,因为mTarget是在Down事件中赋值的。由于mTarge为null,该ViewGroup的onTouchEvent事件被执行。这种情况下可以把这个ViewGroup直接当成View来对待。


好了,一般情况下关于事件分发机制的代码都是scrollview 的,就不贴这个链接了,贴个窗帘的吧。贴我写的view ,就是上文中提到了执行view 返回true 的那个,还记得吗?

要全部源码的留言

package view;import android.content.Context;import android.graphics.Color;import android.util.AttributeSet;import android.util.Log;import android.view.LayoutInflater;import android.view.MotionEvent;import android.view.View;import android.view.View.OnTouchListener;import android.view.animation.BounceInterpolator;import android.view.animation.Interpolator;import android.widget.ImageView;import android.widget.RelativeLayout;import android.widget.Scroller;import com.yiran.push.curtain.R;import tools.BaseTools;/** * 窗帘拉开自定义布局 * @author Ra * blog : http://blog.csdn.net/vipzjyno1/ */public class CurtainView extends RelativeLayout implements OnTouchListener{private static String TAG = "CurtainView";private Context mContext;/** Scroller 拖动类 */private Scroller mScroller;/** 屏幕高度  */private int mScreenHeigh = 0;/** 屏幕宽度  */private int mScreenWidth = 0;/** 点击时候Y的坐标*/private int downY = 0;/** 拖动时候Y的坐标*/private int moveY = 0;/** 拖动时候Y的方向距离*/private int scrollY = 0;/** 松开时候Y的坐标*/private int upY = 0;/** 广告幕布的高度*/private int curtainHeigh = 0;/** 是否 打开*/private boolean isOpen = false;/** 是否在动画 */private boolean isMove = false;/** 绳子的图片*/private ImageView img_curtain_rope;/** 广告的图片*/private ImageView img_curtain_ad;/** 上升动画时间 */private int upDuration = 1000;/** 下落动画时间 */private int downDuration = 500;public CurtainView(Context context) {super(context);init(context);}public CurtainView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init(context);}public CurtainView(Context context, AttributeSet attrs) {super(context, attrs);init(context);}/** 初始化 */private void init(Context context) {this.mContext = context;//Interpolator 设置为有反弹效果的  (Bounce:反弹)Interpolator interpolator = new BounceInterpolator();mScroller = new Scroller(context, interpolator);mScreenHeigh = BaseTools.getWindowHeigh(context);mScreenWidth = BaseTools.getWindowWidth(context);// 背景设置成透明this.setBackgroundColor(Color.argb(0, 0, 0, 0));final View view = LayoutInflater.from(mContext).inflate(R.layout.curtain, null);img_curtain_ad = (ImageView)view.findViewById(R.id.img_curtain_ad);img_curtain_rope = (ImageView)view.findViewById(R.id.img_curtain_rope);addView(view);//默认(初始化时)将整个view 上移,上移的高度为图片(curtain)的高度img_curtain_ad.post(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubcurtainHeigh  = img_curtain_ad.getHeight();Log.d(TAG, "curtainHeigh= " + curtainHeigh);//注意scrollBy和scrollTo的区别CurtainView.this.scrollTo(0, curtainHeigh);}});img_curtain_rope.setOnTouchListener(this);}/** * 拖动动画 * @param startY * @param dy  垂直距离, 滚动的y距离 * @param duration 时间 */public void startMoveAnim(int startY, int dy, int duration) {isMove = true;mScroller.startScroll(0, startY, 0, dy, duration);invalidate();//通知UI线程的更新}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {// TODO Auto-generated method stubsuper.onLayout(changed, l, t, r, b);}@Overridepublic void computeScroll() {//判断是否还在滚动,还在滚动为trueif (mScroller.computeScrollOffset()) {scrollTo(mScroller.getCurrX(), mScroller.getCurrY());//更新界面postInvalidate();isMove = true;} else {isMove = false;}super.computeScroll();}@Overridepublic boolean onTouch(View v, MotionEvent event) {// TODO Auto-generated method stubif (!isMove) {int offViewY = 0;//屏幕顶部和该布局顶部的距离switch (event.getAction()) {//Down方式通过dispatchTouchEvent分发,分发的目的是为了找到真正需要处理完整Touch请求的View。// 当某个View或者ViewGroup的onTouchEvent事件返回true时// ,便表示它是真正要处理这次请求的View,case MotionEvent.ACTION_DOWN:downY = (int) event.getRawY();offViewY = downY - (int)event.getX();return true;//之后的Aciton_UP和Action_MOVE将由它处理。case MotionEvent.ACTION_MOVE:moveY = (int) event.getRawY();scrollY = moveY - downY;if (scrollY < 0) {// 向上滑动if(isOpen){if(Math.abs(scrollY) <= img_curtain_ad.getBottom() - offViewY){scrollTo(0, -scrollY);}}} else {// 向下滑动if(!isOpen){if (scrollY <= curtainHeigh) {scrollTo(0, curtainHeigh - scrollY);}}}break;case MotionEvent.ACTION_UP:upY = (int) event.getRawY();if(Math.abs(upY - downY) < 10){onRopeClick();break;}startMoveAnim(this.getScrollY(),(curtainHeigh - this.getScrollY()), upDuration);isOpen = false;/**if (downY > upY) { // 向上滑动if(isOpen){if (Math.abs(scrollY) > curtainHeigh / 2) {// 向上滑动超过半个屏幕高的时候 开启向上消失动画startMoveAnim(this.getScrollY(),(curtainHeigh - this.getScrollY()), upDuration);isOpen = false;} else {startMoveAnim(this.getScrollY(), -this.getScrollY(),upDuration);isOpen = true;}}} else {// 向下滑动if (scrollY > curtainHeigh / 2) {// 向上滑动超过半个屏幕高的时候 开启向上消失动画startMoveAnim(this.getScrollY(), -this.getScrollY(),upDuration);isOpen = true;} else {startMoveAnim(this.getScrollY(),(curtainHeigh - this.getScrollY()), upDuration);isOpen = false;}}**/break;default:break;}}return false;}/** * 点击绳索开关,会展开关闭 * 在onToch中使用这个中的方法来当点击事件,避免了点击时候响应onTouch的衔接不完美的影响 */public void onRopeClick(){if(isOpen){CurtainView.this.startMoveAnim(0, curtainHeigh, upDuration);}else{CurtainView.this.startMoveAnim(curtainHeigh,-curtainHeigh, downDuration);}isOpen = !isOpen;}}

最后个大家一个连接,窗帘效果的实现!如果大家想从最基础方面学习代码,建议先看看scrollView!

http://download.csdn.net/detail/yangyi1018/9550137




0 0
原创粉丝点击