码迷,mamicode.com
首页 > 移动开发 > 详细

Android 触摸事件以及应用

时间:2015-08-07 18:40:04      阅读:283      评论:0      收藏:0      [点我收藏+]

标签:

Android中触摸事件传递过程中最重要的是dispatchTouchEvent()、onInterceptTouchEvent()和onTouchEvent()方法。这个是困扰初学者的问题之一,我开始也是。这里记录一下dispatchTouchEvent()、onInterceptTouchEvent()和onTouchEvent()的处理过程,以供记忆。

 

    dispatchTouchEvent是处理触摸事件分发,事件(多数情况)是从Activity的dispatchTouchEvent开始的。执行

super.dispatchTouchEvent(ev),事件向下分发。

    onInterceptTouchEventViewGroup提供的方法,默认返回false,返回true表示拦截。

    onTouchEvent是View中提供的方法,ViewGroup也有这个方法,view中不提供onInterceptTouchEvent。view中默认返回true,表示消费了这个事件。

 

View里,有两个回调函数 :

 

public boolean dispatchTouchEvent(MotionEvent ev);  
public boolean onTouchEvent(MotionEvent ev); 

 

ViewGroup里,有三个回调函数 :

 

public boolean dispatchTouchEvent(MotionEvent ev);  
public boolean onInterceptTouchEvent(MotionEvent ev);  
public boolean onTouchEvent(MotionEvent ev);

 

 

在Activity里,有两个回调函数 :

 

 

  1. public boolean dispatchTouchEvent(MotionEvent ev);    
  2. public boolean onTouchEvent(MotionEvent ev);    
public boolean dispatchTouchEvent(MotionEvent ev);  
public boolean onTouchEvent(MotionEvent ev);  

 

    Android中默认情况下事件传递是由最终的view的接收到,传递过程是从父布局到子布局,也就是从Activity到ViewGroup到View的过程,默认情况,ViewGroup起到的是透传作用。Android中事件传递过程(按箭头方向)如下图,图片来自[qiushuiqifei],谢谢[qiushuiqifei]整理。

技术分享

 

  

    触摸事件是一连串ACTION_DOWN,ACTION_MOVE..MOVE…MOVE、最后ACTION_UP,触摸事件还有ACTION_CANCEL事件。事件都是从ACTION_DOWN开始的,Activity的dispatchTouchEvent()首先接收到ACTION_DOWN,执行super.dispatchTouchEvent(ev),事件向下分发。

    dispatchTouchEvent()返回true,后续事件(ACTION_MOVE、ACTION_UP)会再传递,如果返回false,dispatchTouchEvent()就接收不到ACTION_UP、ACTION_MOVE。

 

 

下面的几张图参考自[eoe]

 

 

技术分享

 

                                             图1.ACTION_DOWN都没被消费

 

 

 

 

 

                                技术分享

                                                                  图2-1.ACTION_DOWN被View消费了

 

                                     技术分享

 

                                                   图2-2.后续ACTION_MOVE和UP在不被拦截的情况下都会去找VIEW

 

                              技术分享

                                                                    图3.后续的被拦截了

 

                              技术分享

                                                                       图4ACTION_DOWN一开始就被拦截

 

 

android中的Touch事件都是从ACTION_DOWN开始的:

 

单手指操作:ACTION_DOWN---ACTION_MOVE----ACTION_UP

多手指操作:ACTION_DOWN---ACTION_POINTER_DOWN---ACTION_MOVE--ACTION_POINTER_UP---ACTION_UP.

 

public class StickyNavLayout extends LinearLayout {

    private View mTop;
    private View mNav;
    private ViewPager mViewPager;

    private int mTopViewHeight;
    private ViewGroup mInnerScrollView;
    private boolean isTopHidden = false;

    private OverScroller mScroller;
    private VelocityTracker mVelocityTracker;
    private int mTouchSlop;
    private int mMaximumVelocity, mMinimumVelocity;

    private float mLastY;
    private boolean mDragging;

    private boolean isInControl = false;

    public StickyNavLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        setOrientation(LinearLayout.VERTICAL);

        mScroller = new OverScroller(context);
        // getScaledTouchSlop是一个距离,表示滑动的时候,手的移动要大于这个距离才开始移动控件。
        // 如果小于这个距离就不触发移动控件,如viewpager就是用这个距离来判断用户是否翻页
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        mMaximumVelocity = ViewConfiguration.get(context)
                .getScaledMaximumFlingVelocity();
        mMinimumVelocity = ViewConfiguration.get(context)
                .getScaledMinimumFlingVelocity();

    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mTop = findViewById(R.id.id_stickynavlayout_topview);
        mNav = findViewById(R.id.id_stickynavlayout_indicator);
        View view = findViewById(R.id.id_stickynavlayout_viewpager);
        if (!(view instanceof ViewPager)) {
            throw new RuntimeException(
                    "id_stickynavlayout_viewpager show used by ViewPager !");
        }
        mViewPager = (ViewPager) view;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        ViewGroup.LayoutParams params = mViewPager.getLayoutParams();
        params.height = getMeasuredHeight() - mNav.getMeasuredHeight();

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mTopViewHeight = mTop.getMeasuredHeight();
    }
/*
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        float y = ev.getY();

        switch (action) {
        case MotionEvent.ACTION_DOWN:
            mLastY = y;
            break;
        case MotionEvent.ACTION_MOVE:
            float dy = y - mLastY;
            getCurrentScrollView();

            if (mInnerScrollView instanceof ScrollView) {
                // 如果topView没有隐藏
                // 或sc的scrollY = 0 && topView隐藏 && 下拉,则拦截
                if (mInnerScrollView.getScrollY() == 0 && isTopHidden && dy > 0
                        && !isInControl) {
                    isInControl = true;
                    // 将触摸事件修改为取消
                    ev.setAction(MotionEvent.ACTION_CANCEL);
                    MotionEvent ev2 = MotionEvent.obtain(ev);
                    dispatchTouchEvent(ev);
                    // 再将触摸事件修改为点下
                    ev2.setAction(MotionEvent.ACTION_DOWN);
                    return dispatchTouchEvent(ev2);
                }
            } else if (mInnerScrollView instanceof ListView) {

                ListView lv = (ListView) mInnerScrollView;
                View c = lv.getChildAt(lv.getFirstVisiblePosition());

                if (!isInControl && c != null && c.getTop() == 0 && isTopHidden
                        && dy > 0) {
                    isInControl = true;
                    ev.setAction(MotionEvent.ACTION_CANCEL);
                    MotionEvent ev2 = MotionEvent.obtain(ev);
                    dispatchTouchEvent(ev);
                    ev2.setAction(MotionEvent.ACTION_DOWN);
                    return dispatchTouchEvent(ev2);
                }
            }
            break;
        }
        return super.dispatchTouchEvent(ev);
    }
 */



    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        final int action = ev.getAction();
        float y = ev.getY();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            mLastY = y;
            break;
        case MotionEvent.ACTION_MOVE:
            float dy = y - mLastY;
            getCurrentScrollView();
            if (Math.abs(dy) > mTouchSlop) {
                mDragging = true;
                if (mInnerScrollView instanceof ScrollView) {
                    // 如果topView没有隐藏
                    // 或sc的scrollY = 0 && topView隐藏 && 下拉,则拦截
                    if (!isTopHidden
                            || (mInnerScrollView.getScrollY() == 0
                                    && isTopHidden && dy > 0)) {

                        initVelocityTrackerIfNotExists();
                        mVelocityTracker.addMovement(ev);
                        mLastY = y;
                        return true;
                    }
                } else if (mInnerScrollView instanceof ListView) {

                    ListView lv = (ListView) mInnerScrollView;
                    View c = lv.getChildAt(lv.getFirstVisiblePosition());
                    // 如果topView没有隐藏
                    // 或sc的listView在顶部 && topView隐藏 && 下拉,则拦截

                    if (!isTopHidden || //
                            (c != null //
                                    && c.getTop() == 0//
                                    && isTopHidden && dy > 0)) {

                        initVelocityTrackerIfNotExists();
                        mVelocityTracker.addMovement(ev);
                        mLastY = y;
                        return true;
                    }
                }

            }
            break;
        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP:
            mDragging = false;
            recycleVelocityTracker();
            break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    /*

     */


    // 获取ViewPager里面Fragment里面的ScrollView
    private void getCurrentScrollView() {

        int currentItem = mViewPager.getCurrentItem();
        PagerAdapter a = mViewPager.getAdapter();
        if (a instanceof FragmentPagerAdapter) {
            FragmentPagerAdapter fadapter = (FragmentPagerAdapter) a;
            Fragment item = (Fragment) fadapter.instantiateItem(mViewPager,
                    currentItem);
            mInnerScrollView = (ViewGroup) (item.getView()
                    .findViewById(R.id.id_stickynavlayout_innerscrollview));
        } else if (a instanceof FragmentStatePagerAdapter) {
            FragmentStatePagerAdapter fsAdapter = (FragmentStatePagerAdapter) a;
            Fragment item = (Fragment) fsAdapter.instantiateItem(mViewPager,
                    currentItem);
            mInnerScrollView = (ViewGroup) (item.getView()
                    .findViewById(R.id.id_stickynavlayout_innerscrollview));
        }

    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        initVelocityTrackerIfNotExists();
        mVelocityTracker.addMovement(event);
        int action = event.getAction();
        float y = event.getY();

        switch (action) {
        case MotionEvent.ACTION_DOWN:
            if (!mScroller.isFinished())
                mScroller.abortAnimation();
            mLastY = y;
            return true;
        case MotionEvent.ACTION_MOVE:
            float dy = y - mLastY;

            Log.e("TAG", "dy = " + dy + " , y = " + y + " , mLastY = " + mLastY);

            if (!mDragging && Math.abs(dy) > mTouchSlop) {
                mDragging = true;
            }
            if (mDragging) {
                scrollBy(0, (int) -dy);

                // 如果topView隐藏,且上滑动时,则改变当前事件为ACTION_DOWN
                if (getScrollY() == mTopViewHeight && dy < 0) {
                    event.setAction(MotionEvent.ACTION_DOWN);
                    dispatchTouchEvent(event);
                    isInControl = false;
                }
            }

            mLastY = y;
            break;
        case MotionEvent.ACTION_CANCEL:
            mDragging = false;
            recycleVelocityTracker();
            if (!mScroller.isFinished()) {
                mScroller.abortAnimation();
            }
            break;
        case MotionEvent.ACTION_UP:
            mDragging = false;
            mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
            int velocityY = (int) mVelocityTracker.getYVelocity();
            if (Math.abs(velocityY) > mMinimumVelocity) {
                fling(-velocityY);
            }
            recycleVelocityTracker();
            break;
        }

        return super.onTouchEvent(event);
    }



    public void fling(int velocityY) {
        mScroller.fling(0, getScrollY(), 0, velocityY, 0, 0, 0, mTopViewHeight);
        invalidate();
    }

    @Override
    public void scrollTo(int x, int y) {
        if (y < 0) {
            y = 0;
        }
        if (y > mTopViewHeight) {
            y = mTopViewHeight;
        }
        if (y != getScrollY()) {
            super.scrollTo(x, y);
        }

        isTopHidden = getScrollY() == mTopViewHeight;

    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            scrollTo(0, mScroller.getCurrY());
            invalidate();
        }
    }

    private void initVelocityTrackerIfNotExists() {
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
    }

    private void recycleVelocityTracker() {
        if (mVelocityTracker != null) {
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }
    }

}

 

http://blog.csdn.net/xyz_lmn/article/details/12517911

http://blog.csdn.net/lmj623565791/article/details/43649913

 

Android 触摸事件以及应用

标签:

原文地址:http://www.cnblogs.com/xbx2015/p/4711200.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!