上一篇《Android自定义组件系列【6】——进阶实践(3)》中补充了关于Android中事件分发的过程知识,这一篇我们接着来分析任老师的《可下拉的PinnedHeaderExpandableListView的实现》。

一、StickyLayout中的OnGiveUpTouchEventListener接口的作用是什么?

    public interface OnGiveUpTouchEventListener {
public boolean giveUpTouchEvent(MotionEvent event);
}

在StickyLayout中还提供了设置监听的方法如下:

    public void setOnGiveUpTouchEventListener(OnGiveUpTouchEventListener l) {
mGiveUpTouchEventListener = l;
}

这种方式其实是一种钩子方法,在OnGiveUpTouchEventListener中定义了一个抽象方法(未具体实现)giveUpTouchEvent.,然后通过MainActivity继承OnGiveUpTouchEventListener接口来实现具体逻辑。

    @Override
public boolean giveUpTouchEvent(MotionEvent event) {
if (expandableListView.getFirstVisiblePosition() == 0) {
View view = expandableListView.getChildAt(0);
if (view != null && view.getTop() >= 0) {
return true;
}
}
return false;
}

这个方法中的逻辑:取到ExpandableListView中的第一个可见项,如果是它的子View中的第一个则说明现在首先应该滑动上面的Header部分(让其展开)。

这里返回的true和false有什么不同呢?向下看

    @Override
public boolean onInterceptTouchEvent(MotionEvent event) {
int intercepted = 0;
int x = (int) event.getX();
int y = (int) event.getY(); switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
mLastXIntercept = x;
mLastYIntercept = y;
mLastX = x;
mLastY = y;
intercepted = 0;
break;
}
case MotionEvent.ACTION_MOVE: {
int deltaX = x - mLastXIntercept;
int deltaY = y - mLastYIntercept;
if (mStatus == STATUS_EXPANDED && deltaY <= -mTouchSlop) {
intercepted = 1;
} else if (mGiveUpTouchEventListener != null) {
if (mGiveUpTouchEventListener.giveUpTouchEvent(event) && deltaY >= mTouchSlop) {
intercepted = 1;
}
}
break;
}
case MotionEvent.ACTION_UP: {
intercepted = 0;
mLastXIntercept = mLastYIntercept = 0;
break;
}
default:
break;
} Log.d(TAG, "intercepted=" + intercepted);
return intercepted != 0;
}

在StickyLayout类中的事件拦截方法的ACTION_MOVE中有这么几句代码:

            if (mStatus == STATUS_EXPANDED && deltaY <= -mTouchSlop) {
intercepted = 1;
} else if (mGiveUpTouchEventListener != null) {
if (mGiveUpTouchEventListener.giveUpTouchEvent(event) && deltaY >= mTouchSlop) {
intercepted = 1;
}
}

现在应该明白了吧,呵呵。STATUS_EXPANDED是一个状态值,意思是现在Header部分是展开的,如果Header部分是收起的则会判断giveUpTouchEvent的返回值,如果giveUpTouchEvent返回true说明列表全部被拉下来了,此时应该将Header部分展开。如果返回false则应该下滑列表而不是展开Header部分。

二、PinnedHeaderExpandableListView对OnScrollLister的实现

    @Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (mHeaderView != null && scrollState == SCROLL_STATE_IDLE) {
int firstVisiblePos = getFirstVisiblePosition();
if (firstVisiblePos == 0) {
mHeaderView.layout(0, 0, mHeaderWidth, mHeaderHeight);
}
}
if (mScrollListener != null) {
mScrollListener.onScrollStateChanged(view, scrollState);
}
} @Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (totalItemCount > 0) {
refreshHeader();
}
if (mScrollListener != null) {
mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
}

OnScrollListener是ListView的滚动事件。

在onScrollStateChanged(AbsListView view, int scrollState)中,scrollState有三种状态,分别是:

1、SCROLL_STATE_FLING:开始滚动

2、SCROLL_STATE_TOUCH_SCROLL:正在滚动

3、SCROLL_STATE_IDLE:已经停止

onScroll()方法在列表滚动时一直回调,知道滚动停止才停止回调,另外单击时也回调一次。而OnScrollStateChanged的意思是上面三种状态改变时回调,回调顺序如下:

  1. 第1次:scrollState = SCROLL_STATE_TOUCH_SCROLL(1) 正在滚动
  2. 第2次:scrollState = SCROLL_STATE_FLING(2) 手指做了抛的动作(手指离开屏幕前,用力滑了一下)
  3. 第3次:scrollState = SCROLL_STATE_IDLE(0) 停止滚动
  4. 上面的OnScrollStateChanged方法中在滚动停止后再次布局(绘制)了列表的头。在onScroll方法中主要是为了调用前面提到的refreshHeader()方法去回调并刷新列表头。
  5. 四、如何处理下面列表头将上面列表头顶上去?
  6.     protected void refreshHeader() {
    if (mHeaderView == null) {
    return;
    }
    int firstVisiblePos = getFirstVisiblePosition();
    int pos = firstVisiblePos + 1;
    int firstVisibleGroupPos = getPackedPositionGroup(getExpandableListPosition(firstVisiblePos));
    int group = getPackedPositionGroup(getExpandableListPosition(pos)); if (group == firstVisibleGroupPos + 1) {
    View view = getChildAt(1);
    if (view.getTop() <= mHeaderHeight) {
    int delta = mHeaderHeight - view.getTop();
    mHeaderView.layout(0, -delta, mHeaderWidth, mHeaderHeight - delta);
    }
    } else {
    mHeaderView.layout(0, 0, mHeaderWidth, mHeaderHeight);
    } if (mHeaderUpdateListener != null) {
    mHeaderUpdateListener.updatePinnedHeader(firstVisibleGroupPos);
    }
    }

    refreshHeader()方法中可以看到,先判断上面的是不是多个列表头如果是则重新设置下面列表头的位置到“标准位置”,这样就感觉有一种顶上去的感觉了。

  7. 三、如何展开收缩列表
  8.     @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
    int x = (int) ev.getX();
    int y = (int) ev.getY();
    Log.d(TAG, "dispatchTouchEvent");
    int pos = pointToPosition(x, y);
    if (y >= mHeaderView.getTop() && y <= mHeaderView.getBottom()) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
    mActionDownHappened = true;
    } else if (ev.getAction() == MotionEvent.ACTION_UP) {
    int groupPosition = getPackedPositionGroup(getExpandableListPosition(pos));
    if (groupPosition != INVALID_POSITION && mActionDownHappened) {
    if (isGroupExpanded(groupPosition)) {
    collapseGroup(groupPosition);
    } else {
    expandGroup(groupPosition);
    }
    mActionDownHappened = false;
    } }
    return true;
    } return super.dispatchTouchEvent(ev);
    }

    可以看到PinnedHeaderExpandableListView中的事件分发函数中有如下代码:

  9. if (y >= mHeaderView.getTop() && y <= mHeaderView.getBottom()) 

    限制高度(在列表头位置)

  10. if (isGroupExpanded(groupPosition)) {
    collapseGroup(groupPosition);
    } else {
    expandGroup(groupPosition);
    }

    如果点击则展开或闭合。

先分析到这里吧,差不多完了。。。。。。

最新文章

  1. PHP运行及语句
  2. js实现点击&lt;li&gt;标签弹出其索引值
  3. 夺命雷公狗—angularjs—7—多条数据的遍历
  4. github 使用体会
  5. about tomcat ssl
  6. 【概率】Uva 10900 - So you want to be a 2n-aire?
  7. Python随机生成验证码的两种方法
  8. lazy loading img 图片延迟加载
  9. ASP.NET仿新浪微博下拉加载更多数据瀑布流效果
  10. 常见iis错误之一
  11. angularjs+ionic注册页面表单验证(手机号、确认密码、60s后重发验证码)
  12. 页面加载的时候自动的执行js代码
  13. 【Jenkins】Jenkins权威指南英文版:jenkins-the-definitive-guide.pdf
  14. 记录学习antd design pro dva的过程,主要记错, 多图预警,如有理解偏差,忘指出,多谢!
  15. 自定义shiro实现权限验证方法isAccessAllowed
  16. [Swift]LeetCode547. 朋友圈 | Friend Circles
  17. await这个关键词以及asyncio.wait asyncio.gather
  18. Mac 下配置Lua环境
  19. Mysql EF 触发器生成主键id 存储区更新、插入或删除语句影响到了意外的行数(0)。实体在加载后可能被修改或删除。刷新 ObjectStateManager 项 ;System.Data.Entity.Infrastructure.DbUpdateConcurrencyException
  20. 复制MIFARE Classic卡

热门文章

  1. USACO 2009 Dec cow toll paths 过路费-floyd
  2. itchat转发指定的微信群里某个用户的发言到指定的群
  3. Spring学习总结(5)——IOC注入方式总结
  4. SpringBoot+springmvc异步处理请求
  5. [NPM] Publish npm packages using npm publish
  6. Linux系列-安装经常使用软件
  7. 【C语言】编写函数实现库函数atoi,把字符串转换成整形
  8. Apache中PHP5.3 php5.4如何使用ZEND
  9. python关于字典的操作
  10. (转)oracle常用的数据字典