之前又不止一次的碰到类似的需求,之前为了图方便,都是选择的重写listView的onMeasure方法
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
但是这种方法跟提前计算设置好listView的高度一样,我们的ListView的复用机制就失效了。如果listView多几个,再上拉加载几次 很容易就OOM了。
也可以采用只使用一个ListView 每次切换tab的时候给listView的adapter刷新数据。这样倒是能解决服用的问题,但是这样getView()中的方法太过复杂,而且切换时候的用户体验并不好。话不多说,直接上正餐!
我们都知道ViewGroup 中跟触摸有关的回调函数有三个dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent,其中dispatchTouchEvent是用于分发触摸事件的,onInterceptTouchEvent是用于拦截touch事件的,onTouchEvent是用于处理touch事件的。而View中没有onInterceptTouchEvent,而要解决我们的问题,则需要用到onInterceptTouchEvent,onTouchEvent这两个回调函数。
用户手指触碰屏幕的时候首先会出发onInterceptTouchEvent 的ACTION_DOWN ,这时候我们只需把touch事件放行并记录相关数据即可。此处有一个小tips,如果子控件对于ACTION_DOWN事件也没有进行消费的话,系统会认为touch事件无效。后面的ACTION_MOVE就不会有了。listView的onTouchEvent对于ACTION_DOWN事件是有进行消费的。而其他的一些控件诸如TextView则没有进行消费,这时候就需要我们手动进行消费了。
重头戏来了!
ACTION_MOVE 事件的处理才是我们这篇文章的精髓!
首先,记录下这次move的相关数据
float y = ev.getY(), x = ev.getX();
float diff, oppositeDiff, absDiff;
diff = y - mLastMotionY;
oppositeDiff = x - mLastMotionX;
absDiff = Math.abs(diff);
然后根据滑动的方向,listView的位置来判断是放行还是拦截
首先我们需要区分是左右滑动还是上下滑动,这里简单比较上下的偏移和左右的便宜的大小。如果是上下滑动,再判断是否为点击事件,判断点击事件还是滑动事件的临界值,可以这样获取
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop()
如果是滑动事件,再根据listView的滑动位置(在顶部/底部)和手指滑动的方向来决定是否放行。
1: listView在底部,手指向下滑动,放行,把滑动事件交给listView的onTouchEvent。
2:listView在顶部,手指想上滑动,同上。
3:由于我们有headerView的存在,所以需要把headerView考虑进去,具体代码在下面贴出来了。
if (absDiff >= 0 && absDiff >= Math.abs(oppositeDiff)) {
if (isBottom) {
//listView在底部了
if (absDiff >= mTouchSlop) {
if (diff >= 1f) {
//往上滑
mIsBeingDragged = false;
}else{
mIsBeingDragged = true;
}
}else{
mIsBeingDragged = false;
}
}else{
if (absDiff >= mTouchSlop) {
if (isTop) {
if(diff <= -1f && getScrollY() < headViewHeight){
//listView 在顶部 这时候往上滑
mIsBeingDragged = true;
}else if(diff >= 1f && getScrollY() <= headViewHeight){
//listView在顶部 这时候往下滑
mIsBeingDragged = true;
}else{
mIsBeingDragged = false;
}
}
}else if(absDiff <= mTouchSlop){
//移动的位置在-8 ~ 8之间视为点击事件
mIsBeingDragged = false;
}
}
}
ACTION_UP 的时候只需吧mIsBeingDragged置为false即可。
接下来处理move事件 重写onTouchEvent方法,这里就不做多解释了,相信都能看得懂,直接贴代码了
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE: {
mLastMotionY = event.getY();
mLastMotionX = event.getX();
if (mIsBeingDragged) {
pullEvent();
return true;
}
break;
}
default:
break;
}
return false;
}
private void pullEvent() {
int scrollY = (int) (mInitialMotionY - mLastMotionY + currScrollY);
scrollY = scrollY > 0 ? (scrollY < headViewHeight ? scrollY : headViewHeight) : 0;
scrollTo(0, scrollY);
}
关键代码都贴出来了,如代码有问题,欢迎各位同学指正
完整代码链接:http://download.csdn.net/download/qq_24265937/10246213