- 浏览: 707333 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
utyujin:
作者你好,首先自我注释一下我是一名UI设计师,最近在写关于屏幕 ...
android中的dp,px深度解析 -
eagledame:
<data android:scheme="p ...
Android利用系统广播---监听应用程序安装和卸载[转] -
圣经未来:
虽然帖子距今已有三年,但是我还是来评论一下。对于这段话: 这句 ...
android中的dp,px深度解析 -
passerby_whu:
如果Activity中已经有了很多id了。怎么样保证你指定的i ...
Android Layout 之 RelativeLayout,代码实现相对布局 -
passerby_whu:
u013023750 写道楼主你好 ...
android中的dp,px深度解析
Android ListView onTouchEvent源码简单分析,在看代码之前先来看下代码结构图
一、onTouchEvent源码
二、ACTION_DOWN具体操作源码分析,主要是CheckForTap
三、ACTION_MOVE具体操作源码分析,主要是startScrollIfNeeded和trackMotionScroll
四、ACTION_UP具体操作源码分析,主要是PerformClick, mPendingCheckForLongPress, mFlingRunnable
说明:本文为转载并整理,某些地方讲的并不完善,比如没有提到对onInterceptTouchEvent(MotionEvent ev)方法的处理,在滚动过程中child view是如何布局的,ListView中滚动条加载机制,ListView是如何实现高效缓存的,及如何自定义ListView可显示多列并每个child View高度自适应,等等。先做个记录,后续有空继续研究。
一、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高度自适应,等等。先做个记录,后续有空继续研究。
发表评论
-
深入理解Android的密度独立性
2013-06-17 10:24 1872前言 安卓是一个只 ... -
滑动操作的维度研究
2013-06-13 10:44 1181提示:这篇文章 ... -
移动开发高价值资料参考[持续更新中]
2013-06-13 09:56 1541做人要厚道转载请注明出处:http://zhangkun71 ... -
手机产品设计之用户引导
2013-06-10 20:21 1418在手机产品的设计过程中,由于手机界面 ... -
Android 设计的几处硬伤
2013-06-08 11:31 1579[核心提示] 一些 Andro ... -
Android开发大牛们的博客[持续更新中]
2013-05-31 08:40 27做人要厚道转载请注明出处:http://zhangk ... -
LocalActivityManager的内部机制详解
2013-05-27 15:56 5205LocalActivityManager的内部 ... -
App调试内存泄露之Cursor深入研究
2013-05-24 15:20 0最近在工作中处 ... -
Android设计模式系列(6)--SDK源码之享元模式
2013-05-24 13:12 929享元模式,给我的感 ... -
Android设计模式系列(5)--SDK源码之备忘录模式
2013-05-24 11:15 1084定义(源于GoF《设计模式》):在不破坏封闭的前提下,捕获 ... -
Android设计模式系列(4)--SDK源码之模板方法模式
2013-05-24 10:39 889模板方法,和单例模 ... -
Android设计模式系列(3)--SDK源码之单例模式
2013-05-24 09:43 1009单例模式,可以说是GOF的23种设计模式中最简单的一个。这 ... -
Android设计模式系列(2)--SDK源码之观察者模式
2013-05-23 17:41 1315观察者模式,是一种非常常见的设计模式,在很多系统中随处可见 ... -
Android设计模式系列(1)--SDK源码之组合模式
2013-05-23 17:12 881Android中对组合模式的应用,可谓是泛滥成粥,随处可见, ... -
Android设计模式系列(0)--开篇
2013-05-23 17:11 1211有时候,最难的是坚持;有时候缺少的是信念。 看了很多 ... -
Android Design与Holo Theme详解
2013-05-22 14:06 1554在 国内,有个很有意思的现状。一方面,几个国内最大的公司/企 ... -
GitHub上最火的Android开源项目(完结篇)
2013-05-22 09:22 7707摘要:截至目前,在GitHub“最受欢迎的开源项目”系列文 ... -
二十六个月Android学习工作总结
2013-05-21 16:52 2401本文转自http://www.cnblog ... -
Android中的Layout_weight终极研究
2013-05-17 14:08 3992以前在做UI布局时,也经常用Layout_weight属性 ... -
谷歌I/O大会给开发者带来福音:推出最新Android Studio开发工具
2013-05-17 08:59 1418摘要:在Android Studio发布之后,无论国内外, ...
相关推荐
本篇文章将深入探讨如何在ListView中实现这两种特性,以及相关的源码分析。 一、下拉刷新 下拉刷新功能通常在用户滚动列表到顶部时触发,更新数据并显示刷新状态。在Android中,我们通常使用SwipeRefreshLayout来...
本压缩包"Android ListView反弹效果源码.zip"中包含了实现这一效果的源代码,对于理解Android自定义视图和动画原理非常有帮助。 首先,我们要了解Android ListView的基础知识。ListView是Android提供的一个可以展示...
在这个名为"AndroidListView反弹效果源码.zip"的压缩包中,包含了实现这一效果的源代码。这种效果的实现主要涉及到以下几个关键点: 1. **OverScroller**:这是Android系统提供的一个类,用于处理过度滚动动画。它...
本资源“Android ListView反弹效果源码.zip”就是提供了实现这种效果的源代码。 首先,我们要理解反弹效果的实现原理。通常,这种效果是通过自定义ListView的滚动监听器,结合物理学中的动量守恒定律来模拟的。在...
HorizontalListView在Android...通过分析和学习这个源码示例,你可以掌握如何自定义Android视图组件,处理触摸事件,以及如何创建高效的适配器。这对于提升你的Android开发技能和理解Android系统的底层机制大有裨益。
本篇文章将深入探讨ListView的工作原理、使用方式以及源码分析。 首先,ListView的基本用法涉及到Adapter的使用。Adapter是连接数据源和视图的关键,它负责将数据转化为ListView中的每一项View。通常,我们需要创建...
6. **源码分析**:压缩包中的"源码说明.txt"很可能是对实现反弹效果的具体步骤和代码逻辑的解释。而"1_121119105308_1.jpg"可能是一个示例截图,展示了反弹效果的实际表现。阅读源码可以帮助我们深入理解实现机制,...
5. **源码分析**:标签提到“源码”,可能意味着博主会深入解析Android SDK中与手势处理相关的源代码,帮助读者理解系统是如何处理和分发触摸事件的。 6. **工具使用**:另一个标签是“工具”,可能意味着博主会...
总的来说,通过分析和理解这个源码资源,开发者可以学习到如何在Android应用中实现类似360手机卫士后台通知的滑动删除效果,提升应用的交互性和用户体验。这不仅涉及到Android的基础知识,还涵盖了手势识别、自定义...
水平ListView,作为一种特殊的布局方式,通常用于展示一系列数据项,且这些数据项在水平方向上进行滚动。...通过学习和分析`HorizontalListView.java`,开发者可以进一步提升在Android开发中的专业技能。
Gallery是Android提供的一个水平滚动视图,它继承自AbsSpinner类,实现了ListView的水平版。在API 16(Android 4.1)之后,Gallery被弃用,取而代之的是HorizontalScrollView和RecyclerView等更灵活的组件。然而,...
- 源码分析:BounceListView可能包含以下关键部分: - `onMeasure()`:计算ListView的高度,确保能完全展示所有项目。 - `onLayout()`:布局子视图,处理回弹效果。 - `onTouchEvent()`:处理触摸事件,判断是否...
通过分析这个示例,我们可以更深入地理解如何在实际项目中处理ViewPager和ListView的共存问题。实践是最好的老师,建议对这个示例进行研究和调试,以便掌握这些技巧。 总之,虽然ViewPager和ListView的共存带来了...
通过分析和学习这个源码,开发者可以更好地理解和掌握Android中嵌套滑动的实现方式。 这个源码示例提供了一个实际的解决方案,可以帮助开发者解决在Android应用中遇到的类似问题。通过研究和实践,开发者不仅可以...
在源码分析方面,"MyListViewDemo"可能包含了自定义的ListView类,重写了滑动事件的处理方法,如onTouchEvent()。在这个方法中,会计算滑动的距离和速度,根据这些信息来决定是否触发回弹效果。此外,它可能还包含了...
对于横向滚动的需求,我们不能直接修改ListView的源码,而是需要创建一个新的自定义ViewGroup,这个自定义组件需要重写onMeasure()和onLayout()方法来实现水平布局和滚动。 标签"横向滚动"表明我们要处理的是水平...
这个毕业设计Demo应该包含了以上所有步骤的实现代码,通过阅读和分析源码,可以深入理解Android中如何实现ListView的拖动功能。同时,这对于理解Android UI组件的工作原理,以及如何通过自定义行为增强用户体验,是...
`Android高级应用源码-ViewPager中嵌套ListView实现了下拉刷新和上拉更多,解决了冲突的问题。.zip`这个压缩包提供的就是一个解决此类问题的示例代码。 首先,我们来看`ViewPager`。`ViewPager`是Android SDK中的一...
5. **源码分析**: 这个压缩包可能包含了自定义的下拉刷新控件类,它们可能重写了`onTouchEvent()`方法来检测滑动行为,同时实现了回调机制,当用户松开手指时触发数据加载。源码可能还包含了XML布局文件,定义了...
这个“安卓Android源码——上下拖动的listview.zip”文件提供了一个特殊功能的ListView实现,即用户可以手动拖动列表项来改变它们的顺序。这个功能通常被称为Drag & Sort ListView,它扩展了标准ListView的功能,...