Android中的事件分发之自我见

23 篇文章 0 订阅

好久没有写博客了,呵呵,我做事貌似总是这么有一段没一段的,前几天我看好多人在理解的事件分发和处理的时候都把QQ侧滑拿出来分析分析,同样今天我也是把QQ侧滑拿出来分析一下,但因为我是已经知道了答案,所以呢,我就用倒序的方式来聊一聊Android中事件处理。

1. View中的事件分发顺序
    注意:如果返回值为true,就表示,已经消费了该事件,该事件便不会继续往下传递


dispatchTouchEvent(MotionEvent event);
                    |
                    |
    mOnTouchListener.onTouch(this, event);
                    |
                    |
           onTouchEvent(event);
                    |
                    |
           onClick(this);

看看源码:
image
imgae
image
image

2.ViewGroup中的事件分发顺序

注意:ViewGroup是View的组合,View的事件处理中没有对多个View情况,但是ViewGroup中有,所以就多了一个方法,叫事件拦截,具体看下面得流程图

dispatchTouchEvent(MotionEvent ev);
                |
                |
onInterceptTouchEvent(ev);
                |
                |
dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign);

如果父View不拦截事件,父View就要把事件分发给子view,子view要判断
他还有没有子view,如果他有则继续分发,没有则重复View的事件分发的顺序

看看源码:
image
image
image

那么事件拦截又是一个什么鬼?

刚刚已经把View和ViewGroup的事件分发顺序,这是按照正常的执行情况下,View对于触摸事件的处理,那么所谓的事件拦截又从何说起呢?

这里简单的分为两种情况

1.子view请求父View拦截事件

比如,之前自己实现侧滑菜单和ViewPager联用时,滑动到左侧时,应该弹出侧滑菜单,这个时候Viewpager应该请求父View拦截事件,不让自己处理。

看源码是如何实现请求父View拦截的
image
image
image

2.父View不让事件传递下去
这个就比较简单了,你可以在事件分发顺序中的那几个方法返回true就行

说了原理 那现在就把QQ侧滑的效果实现一下

步骤
1.继承HorizontalScrollView,因为HorizontalScrollView已经帮我们把View摆放好了,所以用它更方便

获取屏幕的宽度,用于判断两个View滑到了哪一个区域该左滑还是右滑

WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics metrics = new DisplayMetrics();
        manager.getDefaultDisplay().getMetrics(metrics);
        mScreenWidth = metrics.widthPixels;
2.重写onMeasure()方法

获取布局中的两个布局,然后分别指定他们的宽度(菜单选项的宽度等于屏幕宽度-固定值,内容宽度等于屏幕的实际宽度)

    if (!mIsOnce) {
            LinearLayout childAt = (LinearLayout) getChildAt(0);
            mMenu = childAt.getChildAt(0);
            mMain = childAt.getChildAt(1);
            mMenuWidth = mScreenWidth - 200;
            mMenu.getLayoutParams().width = mMenuWidth;
            mMain.getLayoutParams().width = mScreenWidth;
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
3.重写onLayout()方法

将其移动到指定位置,使用this.scrollTo()方法
为了避免每次都要测量,这里是刚进入app的时候摆放,所以在这里用一个变量记着,下次onMeasure的时候就不用测量了

 if (changed) {
            this.scrollTo(mMenuWidth, 0);
            mIsOnce = true;
        }
        super.onLayout(changed, l, t, r, b);
4.重写onTouchEvent()方法

记录down时的坐标,在move时再获取一次坐标,两个值相减是否大于某一固定值,移动到相对位置
因为onTouchEvent方法已经消费了事件,所以这里得返回true,这里也就是事件拦截;

@Override
    public boolean onTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                downX = ev.getX();
                break;
            case MotionEvent.ACTION_UP: {
                float dx = ev.getX() - downX;
                if (dx < mScreenWidth / 3) {
                    this.smoothScrollTo(mMenuWidth, 0);
                } else {
                    this.smoothScrollTo(0, 0);
                }
                return true;
            }
        }
        return super.onTouchEvent(ev);
    }
5.在onScrollChanged()方法中做一些在移动中所需要做的效果
//滑动的时候进行动画处理
    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        float factor = (float) l / mMenuWidth;
        mMenu.setTranslationX(mMenuWidth * factor * 0.6f);
        float leftScale = 1 - 0.4f * factor;
        mMenu.setScaleX(leftScale);
        mMenu.setScaleY(leftScale);
        float rightScale = 0.8f + 0.2f * factor;
        mMain.setScaleX(rightScale);
        mMain.setScaleY(rightScale);
        mMenu.setAlpha(1 - factor);
    }

效果我就不展示了,我只是想把事件分发自己彻底弄明白而已。。。希望你我共勉

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值