今天在做项目时遇到一个需求:界面上有个View需要能够自由拖动,松开后还要能贴边,当然需要动画过度,另外点击后需要做界面跳转。看上去每个需求都很简单,一步步实现后,跑起来运行,嗯,界面效果不错,动画也很平滑,点击效果也很.....咦?点击效果怎么不行了?忘记设置click了吗?一看代码,并没有,那为什么呢?后来经过一番研究发现是onTouch和onClick冲突了。昨天就堵这边了,今天冒着大雪来上班,终于解决了。废话不多说了,讲下思路。

想做移动效果,就要设置onTouch监听,然后在move的时候,根据坐标做移动。这些都简单,关键是onTouch的返回值,因为这个决定了系统对此次事件的派发。返回true表示自己处理这个事件,false则交由上级处理。我们的touch事件是由父控件负责派发的,父控件调用自己的dispatchTouchEvent进行派发,然后根据坐标落在哪个子view进行派发。想要能触发onClick,那么我们的down事件必须返回false,其中的原理大家可以去看源码,这里主要是给大家解决二者冲突的解决方法,直接上代码了。

@Override
    public boolean onTouch(final View v, MotionEvent event) {
        getParent().requestDisallowInterceptTouchEvent(true);
        float x = event.getX();
        float y = event.getY();
        boolean consumed = true;
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mDragging = false;
            consumed = mDragging;
            mLastMotionX = x;
            mLastMotionY = y;
            break;
        case MotionEvent.ACTION_MOVE:
            mDragging = true;
            consumed = mDragging;
            float offsetX = x - mLastMotionX;
            float offsetY = y - mLastMotionY;
            int newLeft = getLeft() + (int) offsetX;
            int newTop = getTop() + (int) offsetY;
            newLeft = Math.min(Math.max(0, newLeft), mParentWidth - v.getMeasuredWidth());
            newTop = Math.min(Math.max(0, newTop), mParentHeight - v.getMeasuredHeight());
            RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(v.getMeasuredWidth(),
                    v.getMeasuredHeight());
            lp.leftMargin = newLeft;
            lp.topMargin = newTop;
            v.setLayoutParams(lp);
            break;
        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP:
            consumed = mDragging;
            mDragging = false;
            autoAlignHorizontalSide();
            mLastMotionX = 0;
            mLastMotionY = 0;
            break;
        }
        return consumed;
    }

重要的是onTouch的返回值。其中的拖动逻辑也在里面,大家可以参考一下,当然现在Android在v4包里面新增了ViewDragHelper类,这个类功能非常强大,可以帮我们简单的实现一些拖动相关的功能,但是我的view是在一个ViewPager里面的,在使用

ViewDragHelper的时候发现,拖动会和ViewPager的拖动冲突,比较难脱动,所以我就换了自己实现拖动的方法。在onTouch第一行

getParent().requestDisallowInterceptTouchEvent(true);

这个方法就是让父容器把这个实现不要在给别的view了,最终做到了满意的效果。

如果有什么问题,请大家指正,期待与你们共同进步。