前戏很长
这算是刚到实习公司接触到的第一个任务。公司某一产品中某个界面的listView快速滑动会有卡顿的现象发生,我的任务就是解决它。
我一开始的想法比较简单,可能是listview的优化没有做到位,例如convertView的复用、viewHolder的使用等等基础的优化措施,然并卵。好长时间后终于找到了问题发生的相关代码...经过在可疑语句上(onTouchEvent方法中的几个case、onScrollStateChanged方法中的SCROLLSTATEXXX状态下)打印log,发现如下几个问题和卡顿现象有关系:
于是再看代码,发现原因在于接受数据、解析数据、更新listView的Adapter内容的代码,没有和listView的滑动状态相关联,即滑动过程中可能就接收到了新数据并刷新了界面,造成卡顿。
主要是滑动和数据接收更新界面之间的冲突,二者通过一个boolean变量关联一下即可。即只有在scrollState为IDLE时,才允许进行数据接收和界面刷新(调用notifyAdapterDataChanged方法),其他状态(FLING,TOUCH_SCROLL)都不允许。 而在服务器活跃的时间段之外,原本的代码中也是在滑动结束后才主动发起数据请求的,所以这方面不用考虑。
但是组长提出,可能会发生这样的问题,在滑动过程中数据不接收,会发生数据丢失的问题,这个应用本身对数据的实时性很高,所以这个也需要解决。
测试了一下发现,卡顿的原因主要在于界面的刷新而不是数据接收,于是将控制范围缩小到了:只有在不滑动的状态下才允许更新界面,而数据接收在滑动过程中也可执行。保留最新接收到的数据即可。在滑动结束后及时判断有无最新的数据缓存。
组长的老板感觉还是卡顿...这次问清楚了,竟然觉得界面上滑块的移动有一顿一顿很难受...我和另一个前辈对此真是相当无语了。最后没办法了,组长说那就把这个fling的功能给禁止了吧...
百度搜索关键词“android listView fling”,出现的第一条就是下面这个,【Andorid X 项目笔记】禁用ListView的Fling功能(1),可是也是然并卵... ``` /** 手势识别类 */ private class TouchGesture extends SimpleOnGestureListener {
/** 快速滚动 */
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return true;
}
}
private OnTouchListener mOnListViewTouchListener = new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (mTouchGesture.onTouchEvent(event))
return true;
return false;
}
};
```
代码中mTouchGesture
根本没有onTouchEvent(event)
这个方法啊啊啊啊啊啊...一开始我是很开心的....
然后就是各种百度,各种google,各种“静态流”(stackOverFlow的中文备案名称,我也是醉了),都没有个能用的解决方案。 其中可以使用但是不能达到要求的一段代码我觉得不错,留下链接-Android
listview垂直滑动指定距离,利用反射的原理,从AbsListView中找到一个相对来说比较通用的方法boolean
trackMotionScroll(int deltaY, int incrementalDeltaY)
来达到控制滑动距离的目的。以后可能用得上。
整整困扰了我三天,既然不能从正面刚,那从侧面试试看吧。于是开始查询事件分发机制、fling的实现方式、fling的触发条件、scroll的相关内容...其中比较有用的是
dispatchTouchEvent
涉及事件分发,onInterceptTouchEvent
涉及事件拦截和onTouchEvent
涉及事件处理。遇到几次和这个相关的问题,每次都得查看相关的知识点...总是记不住。链接-Android
触摸事件分发ViewGroup&View多看几遍就看懂了。VelocityTracker
这个类是用来测量滑动速度的。链接-手势事件:滑动动速度跟踪类VelocityTracker介绍AbsListView
中,滑动达到了一定的速度,就会触发fling,而在AbsListView
的源码中是由一个runnable
的自定义类来完成的,太复杂所以没怎么看,但是有空了是一定要仔细研究的。那么从上面这几点可以总结出一个结论,就是fling是由touch事件来控制的,而touch事件是由view&viewGroup来进行分发、拦截、相应的。今天早上起床的一个念头就是解决问题的方法:
拦截掉touch事件来从源头上打破触发fling的事件链,从而达到禁止fling的目的
说干就干,到了公司,找到问题发生的listView,找到容纳该listView的viewGroup(是一个LinearLayout的子类),将那三个touch函数写下了,打印log分析,从哪里拦截,拦截哪个事件比较好。最终决定在dispatchTouchEvent
中拦截,在ACTION_UP
中判断当前滑动在y(纵轴)方向上的速度,当速度达到某一个阀值后,return
false
,也就是将可以出发fling的事件链中最后一个事件给消灭掉,不传给子view。试了一下,果然好使!
对公司项目代码还是不熟,大部分事件都花在定位问题代码上面了。再加上解决问题的积累还是不够,不能从根本上考虑,处处碰壁之后才想到从源头上来解决。其实我感觉解决问题的根本还是看一个人的知识积累和知识整合的能力,“熟读唐诗三百首”就是这个道理。
onTouch
方法和onTouchEvent
方法还是不同的,前者是OnTouchListener
接口中的方法,而后者是view中的方法,而且前者比后者的优先级要高。版权声明:本文为博主原创文章,未经博主允许不得转载。
android问题及其解决-优化listView卡顿和如何禁用ListView的fling
原文地址:http://blog.csdn.net/u012123160/article/details/47720257