`
亚当爱上java
  • 浏览: 707333 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Android ListView onTouchEvent源码分析

阅读更多
Android ListView  onTouchEvent源码简单分析,在看代码之前先来看下代码结构图


一、onTouchEvent源码
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (!isEnabled()) {
            // A disabled view that is clickable still consumes the touch
            // events, it just doesn't respond to them.
            return isClickable() || isLongClickable();
        }
       
        // AbsListView 绘制与控制手指快速滚动的辅助类
        if (mFastScroller != null) {
            boolean intercepted = mFastScroller.onTouchEvent(ev);
            if (intercepted) {
                return true;
            }
        }

        final int action = ev.getAction();

        View v;
        int deltaY;

         // 获取触摸滚动时的速率
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(ev);

        // ListView触屏事件主要从ACTION操作划分
        switch (action & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN: {
             ......
             break;
        }
        case MotionEvent.ACTION_MOVE: {
            ......
            break;
        }

        case MotionEvent.ACTION_UP: {
            switch (mTouchMode) {
            case TOUCH_MODE_DOWN:
            case TOUCH_MODE_TAP:
            case TOUCH_MODE_DONE_WAITING:
                ......
                mTouchMode = TOUCH_MODE_REST;
                break;
            case TOUCH_MODE_SCROLL:
             ......
                break;
            }

            setPressed(false);

            // Need to redraw since we probably aren't drawing the selector anymore
            invalidate();

            final Handler handler = getHandler();
            if (handler != null) {
                handler.removeCallbacks(mPendingCheckForLongPress);
            }

            if (mVelocityTracker != null) {
                mVelocityTracker.recycle();
                mVelocityTracker = null;
            }
           
            mActivePointerId = INVALID_POINTER;

            if (PROFILE_SCROLLING) {
                if (mScrollProfilingStarted) {
                    Debug.stopMethodTracing();
                    mScrollProfilingStarted = false;
                }
            }
            break;
        }

        case MotionEvent.ACTION_CANCEL: {
            mTouchMode = TOUCH_MODE_REST;
            ......
            break;
        }
       
        case MotionEvent.ACTION_POINTER_UP: {
            ......
            break;
        }
       
        }

        return true;
    }


二、ACTION_DOWN具体操作源码分析,主要是CheckForTap
        case MotionEvent.ACTION_DOWN: {
            mActivePointerId = ev.getPointerId(0);
            final int x = (int) ev.getX();
            final int y = (int) ev.getY();
           
             // 手指按下时x,y坐标,获取当前选中的item
            int motionPosition = pointToPosition(x, y);
            // 如果ListView 数据未发生变化
            if (!mDataChanged) {
                if ((mTouchMode != TOUCH_MODE_FLING) && (motionPosition >= 0)
                        && (getAdapter().isEnabled(motionPosition))) {
                    // User clicked on an actual view (and was not stopping a fling). It might be a
                    // click or a scroll. Assume it is a click until proven otherwise
                    mTouchMode = TOUCH_MODE_DOWN;
                   
                    // TAP机制,主要是用于去除手指点击抖动
                    // 使Item处于按下状态
                    if (mPendingCheckForTap == null) {
                        mPendingCheckForTap = new CheckForTap();
                    }
                    // 添加到消息队列并延时ViewConfiguration.getTapTimeout()执行此runnable
                    postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                } else {
                    if (ev.getEdgeFlags() != 0 && motionPosition < 0) {
                        // If we couldn't find a view to click on, but the down event was touching
                        // the edge, we will bail out and try again. This allows the edge correcting
                        // code in ViewRoot to try to find a nearby view to select
                        return false;
                    }

                      // 之前处于Fling模式
                    if (mTouchMode == TOUCH_MODE_FLING) {
                        // Stopped a fling. It is a scroll.
                        createScrollingCache();
                        // 更改为scroll
                        mTouchMode = TOUCH_MODE_SCROLL;
                        mMotionCorrection = 0;
                        motionPosition = findMotionRow(y);
                        reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
                    }
                }
            }

             // 对于ACTION_MOVE,ACTION_UP会使用的触屏位置信息进行记录
            if (motionPosition >= 0) {
                // Remember where the motion event started
                v = getChildAt(motionPosition - mFirstPosition);
                mMotionViewOriginalTop = v.getTop();
            }
            mMotionX = x;
            mMotionY = y;
            mMotionPosition = motionPosition;
            mLastY = Integer.MIN_VALUE;
            break;
        }


三、ACTION_MOVE具体操作源码分析,主要是startScrollIfNeeded和trackMotionScroll
        case MotionEvent.ACTION_MOVE: {
            final int pointerIndex = ev.findPointerIndex(mActivePointerId);
            final int y = (int) ev.getY(pointerIndex);
            // 获取y轴当前与前一次的偏移值
            deltaY = y - mMotionY;
            switch (mTouchMode) {
            case TOUCH_MODE_DOWN:
            case TOUCH_MODE_TAP:
            case TOUCH_MODE_DONE_WAITING:
                // 必须移动一段距离后才会执行滚动
                startScrollIfNeeded(deltaY);
                break;
            case TOUCH_MODE_SCROLL:
                if (PROFILE_SCROLLING) {
                    if (!mScrollProfilingStarted) {
                        Debug.startMethodTracing("AbsListViewScroll");
                        mScrollProfilingStarted = true;
                    }
                }

                 // 手指移动
                if (y != mLastY) {
                    deltaY -= mMotionCorrection;
                    int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;
                   
                    // No need to do all this work if we're not going to move anyway
                    boolean atEdge = false;
                    if (incrementalDeltaY != 0) {
                      // 滚动的重要方法,滚动的具体处理就是这里
                        atEdge = trackMotionScroll(deltaY, incrementalDeltaY);
                    }

                    // ListView滚动到边界后不不能再进行移动
                    if (atEdge && getChildCount() > 0) {
                        // Treat this like we're starting a new scroll from the current
                        // position. This will let the user start scrolling back into
                        // content immediately rather than needing to scroll back to the
                        // point where they hit the limit first.
                        int motionPosition = findMotionRow(y);
                        if (motionPosition >= 0) {
                            final View motionView = getChildAt(motionPosition - mFirstPosition);
                            mMotionViewOriginalTop = motionView.getTop();
                        }
                        mMotionY = y;
                        mMotionPosition = motionPosition;
                        invalidate();
                    }
                    // 记录当前Y值,用于下次计算偏移量
                    mLastY = y;
                }
                break;
            }

            break;
        }


四、ACTION_UP具体操作源码分析,主要是PerformClick, mPendingCheckForLongPress, mFlingRunnable
        case MotionEvent.ACTION_UP: {
            switch (mTouchMode) {
            case TOUCH_MODE_DOWN:
            case TOUCH_MODE_TAP:
            case TOUCH_MODE_DONE_WAITING:
                final int motionPosition = mMotionPosition;
                final View child = getChildAt(motionPosition - mFirstPosition);
                if (child != null && !child.hasFocusable()) {
                    // 清理Item按下状态
                    if (mTouchMode != TOUCH_MODE_DOWN) {
                        child.setPressed(false);
                    }

                      // 执行Item Click
                    if (mPerformClick == null) {
                        mPerformClick = new PerformClick();
                    }

                    final AbsListView.PerformClick performClick = mPerformClick;
                    performClick.mChild = child;
                    performClick.mClickMotionPosition = motionPosition;
                    performClick.rememberWindowAttachCount();

                    mResurrectToPosition = motionPosition;

                    if (mTouchMode == TOUCH_MODE_DOWN || mTouchMode == TOUCH_MODE_TAP) {
                        final Handler handler = getHandler();
                        if (handler != null) {
                          // 清理tap或者long press长按
                            handler.removeCallbacks(mTouchMode == TOUCH_MODE_DOWN ?
                                    mPendingCheckForTap : mPendingCheckForLongPress);
                        }
                        mLayoutMode = LAYOUT_NORMAL;
                        if (!mDataChanged && mAdapter.isEnabled(motionPosition)) {
                            mTouchMode = TOUCH_MODE_TAP;
                            setSelectedPositionInt(mMotionPosition);
                            layoutChildren();
                            child.setPressed(true);
                            positionSelector(child);
                            setPressed(true);
                            if (mSelector != null) {
                                Drawable d = mSelector.getCurrent();
                                if (d != null && d instanceof TransitionDrawable) {
                                    ((TransitionDrawable) d).resetTransition();
                                }
                            }
                            postDelayed(new Runnable() {
                                public void run() {
                                    child.setPressed(false);
                                    setPressed(false);
                                    if (!mDataChanged) {
                                        post(performClick);
                                    }
                                    mTouchMode = TOUCH_MODE_REST;
                                }
                            }, ViewConfiguration.getPressedStateDuration());
                        } else {
                            mTouchMode = TOUCH_MODE_REST;
                        }
                        return true;
                    } else if (!mDataChanged && mAdapter.isEnabled(motionPosition)) {
                        post(performClick);
                    }
                }
                mTouchMode = TOUCH_MODE_REST;
                break;
            case TOUCH_MODE_SCROLL:
                final int childCount = getChildCount();
                if (childCount > 0) {
                    if (mFirstPosition == 0 && getChildAt(0).getTop() >= mListPadding.top &&
                            mFirstPosition + childCount < mItemCount &&
                            getChildAt(childCount - 1).getBottom() <=
                                    getHeight() - mListPadding.bottom) {
                        mTouchMode = TOUCH_MODE_REST;
                        reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
                    } else {
                        // 是否执行ListView Scroll Fling
                        final VelocityTracker velocityTracker = mVelocityTracker;
                        velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                        // 获取当前触屏滚动速率
                        final int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
   
                        if (Math.abs(initialVelocity) > mMinimumVelocity) {
                            if (mFlingRunnable == null) {
                                mFlingRunnable = new FlingRunnable();
                            }
                            reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
                           
                            // 执行ListView 快速滚动(Scroll Fling)
                            mFlingRunnable.start(-initialVelocity);
                        } else {
                            mTouchMode = TOUCH_MODE_REST;
                            reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
                        }
                    }
                } else {
                    mTouchMode = TOUCH_MODE_REST;
                    reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
                }
                break;
            }

            setPressed(false);

            // Need to redraw since we probably aren't drawing the selector anymore
            invalidate();

            final Handler handler = getHandler();
            if (handler != null) {
                handler.removeCallbacks(mPendingCheckForLongPress);
            }

            if (mVelocityTracker != null) {
                mVelocityTracker.recycle();
                mVelocityTracker = null;
            }
           
            mActivePointerId = INVALID_POINTER;

            if (PROFILE_SCROLLING) {
                if (mScrollProfilingStarted) {
                    Debug.stopMethodTracing();
                    mScrollProfilingStarted = false;
                }
            }
            break;
        }


说明:本文为转载并整理,某些地方讲的并不完善,比如没有提到对onInterceptTouchEvent(MotionEvent ev)方法的处理,在滚动过程中child view是如何布局的,ListView中滚动条加载机制,ListView是如何实现高效缓存的,及如何自定义ListView可显示多列并每个child View高度自适应,等等。先做个记录,后续有空继续研究。
  • 大小: 14.9 KB
分享到:
评论

相关推荐

    Android ListView 下拉刷新、上拉加载

    本篇文章将深入探讨如何在ListView中实现这两种特性,以及相关的源码分析。 一、下拉刷新 下拉刷新功能通常在用户滚动列表到顶部时触发,更新数据并显示刷新状态。在Android中,我们通常使用SwipeRefreshLayout来...

    Android ListView反弹效果源码.zip

    本压缩包"Android ListView反弹效果源码.zip"中包含了实现这一效果的源代码,对于理解Android自定义视图和动画原理非常有帮助。 首先,我们要了解Android ListView的基础知识。ListView是Android提供的一个可以展示...

    AndroidListView反弹效果源码.zip

    在这个名为"AndroidListView反弹效果源码.zip"的压缩包中,包含了实现这一效果的源代码。这种效果的实现主要涉及到以下几个关键点: 1. **OverScroller**:这是Android系统提供的一个类,用于处理过度滚动动画。它...

    Android ListView反弹效果源码.zip源码资源下载

    本资源“Android ListView反弹效果源码.zip”就是提供了实现这种效果的源代码。 首先,我们要理解反弹效果的实现原理。通常,这种效果是通过自定义ListView的滚动监听器,结合物理学中的动量守恒定律来模拟的。在...

    Android应用源码之HorizontalListViewDemo 横向滑动的ListView.rar

    HorizontalListView在Android...通过分析和学习这个源码示例,你可以掌握如何自定义Android视图组件,处理触摸事件,以及如何创建高效的适配器。这对于提升你的Android开发技能和理解Android系统的底层机制大有裨益。

    ListVIew详解源码学习

    本篇文章将深入探讨ListView的工作原理、使用方式以及源码分析。 首先,ListView的基本用法涉及到Adapter的使用。Adapter是连接数据源和视图的关键,它负责将数据转化为ListView中的每一项View。通常,我们需要创建...

    安卓Android源码——ListView反弹效果源码.zip

    6. **源码分析**:压缩包中的"源码说明.txt"很可能是对实现反弹效果的具体步骤和代码逻辑的解释。而"1_121119105308_1.jpg"可能是一个示例截图,展示了反弹效果的实际表现。阅读源码可以帮助我们深入理解实现机制,...

    Android手势研究(textview及listview对比验证)(一)

    5. **源码分析**:标签提到“源码”,可能意味着博主会深入解析Android SDK中与手势处理相关的源代码,帮助读者理解系统是如何处理和分发触摸事件的。 6. **工具使用**:另一个标签是“工具”,可能意味着博主会...

    Android listview 滑动删除(具体效果360手机卫士后台通知).zip源码资源下载

    总的来说,通过分析和理解这个源码资源,开发者可以学习到如何在Android应用中实现类似360手机卫士后台通知的滑动删除效果,提升应用的交互性和用户体验。这不仅涉及到Android的基础知识,还涵盖了手势识别、自定义...

    水平listview空间源码

    水平ListView,作为一种特殊的布局方式,通常用于展示一系列数据项,且这些数据项在水平方向上进行滚动。...通过学习和分析`HorizontalListView.java`,开发者可以进一步提升在Android开发中的专业技能。

    Android应用源码之Gallery2.zip

    Gallery是Android提供的一个水平滚动视图,它继承自AbsSpinner类,实现了ListView的水平版。在API 16(Android 4.1)之后,Gallery被弃用,取而代之的是HorizontalScrollView和RecyclerView等更灵活的组件。然而,...

    Android:弹性ListView

    - 源码分析:BounceListView可能包含以下关键部分: - `onMeasure()`:计算ListView的高度,确保能完全展示所有项目。 - `onLayout()`:布局子视图,处理回弹效果。 - `onTouchEvent()`:处理触摸事件,判断是否...

    android ViewPager 和 listview的共存问题

    通过分析这个示例,我们可以更深入地理解如何在实际项目中处理ViewPager和ListView的共存问题。实践是最好的老师,建议对这个示例进行研究和调试,以便掌握这些技巧。 总之,虽然ViewPager和ListView的共存带来了...

    Android高级应用源码-横竖都能滑动的 listview 嵌套在一起的.rar

    通过分析和学习这个源码,开发者可以更好地理解和掌握Android中嵌套滑动的实现方式。 这个源码示例提供了一个实际的解决方案,可以帮助开发者解决在Android应用中遇到的类似问题。通过研究和实践,开发者不仅可以...

    ListView回弹效果

    在源码分析方面,"MyListViewDemo"可能包含了自定义的ListView类,重写了滑动事件的处理方法,如onTouchEvent()。在这个方法中,会计算滑动的距离和速度,根据这些信息来决定是否触发回弹效果。此外,它可能还包含了...

    android横向滚动listview

    对于横向滚动的需求,我们不能直接修改ListView的源码,而是需要创建一个新的自定义ViewGroup,这个自定义组件需要重写onMeasure()和onLayout()方法来实现水平布局和滚动。 标签"横向滚动"表明我们要处理的是水平...

    Android应用源码ListView 中的item随意拖动-IT计算机-毕业设计.zip

    这个毕业设计Demo应该包含了以上所有步骤的实现代码,通过阅读和分析源码,可以深入理解Android中如何实现ListView的拖动功能。同时,这对于理解Android UI组件的工作原理,以及如何通过自定义行为增强用户体验,是...

    Android高级应用源码-ViewPager中嵌套ListView实现了下拉刷新和上拉更多,解决了冲突的问题。.zip

    `Android高级应用源码-ViewPager中嵌套ListView实现了下拉刷新和上拉更多,解决了冲突的问题。.zip`这个压缩包提供的就是一个解决此类问题的示例代码。 首先,我们来看`ViewPager`。`ViewPager`是Android SDK中的一...

    安卓Android源码——下拉刷新控件(ListView好ScrollView版).rar

    5. **源码分析**: 这个压缩包可能包含了自定义的下拉刷新控件类,它们可能重写了`onTouchEvent()`方法来检测滑动行为,同时实现了回调机制,当用户松开手指时触发数据加载。源码可能还包含了XML布局文件,定义了...

    安卓Android源码——上下拖动的listview.zip

    这个“安卓Android源码——上下拖动的listview.zip”文件提供了一个特殊功能的ListView实现,即用户可以手动拖动列表项来改变它们的顺序。这个功能通常被称为Drag & Sort ListView,它扩展了标准ListView的功能,...

Global site tag (gtag.js) - Google Analytics