Android自定义组件系列【7】——进阶实践(4)
上一篇《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次:scrollState = SCROLL_STATE_TOUCH_SCROLL(1) 正在滚动
- 第2次:scrollState = SCROLL_STATE_FLING(2) 手指做了抛的动作(手指离开屏幕前,用力滑了一下)
- 第3次:scrollState = SCROLL_STATE_IDLE(0) 停止滚动
- 上面的OnScrollStateChanged方法中在滚动停止后再次布局(绘制)了列表的头。在onScroll方法中主要是为了调用前面提到的refreshHeader()方法去回调并刷新列表头。
- 四、如何处理下面列表头将上面列表头顶上去?
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()方法中可以看到,先判断上面的是不是多个列表头如果是则重新设置下面列表头的位置到“标准位置”,这样就感觉有一种顶上去的感觉了。
- 三、如何展开收缩列表
@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中的事件分发函数中有如下代码:
if (y >= mHeaderView.getTop() && y <= mHeaderView.getBottom())
限制高度(在列表头位置)
if (isGroupExpanded(groupPosition)) {
collapseGroup(groupPosition);
} else {
expandGroup(groupPosition);
}如果点击则展开或闭合。
Android自定义组件系列【7】——进阶实践(4)的更多相关文章
- Android自定义组件系列【6】——进阶实践(3)
上一篇<Android自定义组件系列[5]--进阶实践(2)>继续对任老师的<可下拉的PinnedHeaderExpandableListView的实现>进行了分析,这一篇计划 ...
- Android自定义组件系列【5】——进阶实践(2)
上一篇<Android自定义组件系列[5]--进阶实践(1)>中对任老师的<可下拉的PinnedHeaderExpandableListView的实现>前一部分进行了实现,这一 ...
- Android自定义组件系列【4】——自定义ViewGroup实现双侧滑动
在上一篇文章<Android自定义组件系列[3]--自定义ViewGroup实现侧滑>中实现了仿Facebook和人人网的侧滑效果,这一篇我们将接着上一篇来实现双面滑动的效果. 1.布局示 ...
- Android自定义组件系列【3】——自定义ViewGroup实现侧滑
有关自定义ViewGroup的文章已经很多了,我为什么写这篇文章,对于初学者或者对自定义组件比较生疏的朋友虽然可以拿来主义的用了,但是要一步一步的实现和了解其中的过程和原理才能真真脱离别人的代码,举一 ...
- Android自定义组件系列【5】——进阶实践(1)
接下来几篇文章将对任老师的博文<可下拉的PinnedHeaderExpandableListView的实现>分步骤来详细实现,来学习一下大神的代码并记录一下. 原文出处:http://bl ...
- Android自定义组件系列【12】——非UI线程绘图SurfaceView
一.SurfaceView的介绍 在前面我们已经会自定义View,使用canvas绘图,但是View的绘图机制存在一些缺陷. 1.View缺乏双缓冲机制. 2.程序必须重绘整个View上显示的图片,比 ...
- Android自定义组件系列【1】——自定义View及ViewGroup
View类是ViewGroup的父类,ViewGroup具有View的所有特性,ViewGroup主要用来充当View的容器,将其中的View作为自己孩子,并对其进行管理,当然孩子也可以是ViewGr ...
- Android自定义组件系列【17】——教你如何高仿微信录音Toast
一.Toast介绍 平时我们在Android开发中会经常用到一个叫Toast的东西,官方解释如下 A toast is a view containing a quick little message ...
- Android自定义组件系列【15】——四个方向滑动的菜单实现
今天无意中实现了一个四个方向滑动的菜单,感觉挺好玩,滑动起来很顺手,既然已经做出来了就贴出来让大家也玩弄一下. 一.效果演示 (说明:目前没有安装Android模拟器,制作的动态图片太卡了,就贴一下静 ...
随机推荐
- 制作 wordpress 博客静态化到本地
wget 克隆 wordpress 博客镜像 wget -e robots=off -w 1 -xq -np -nH -pk -m -t 1 -P "./wordpress.org" ...
- screen-调节屏幕亮度
今天做项目的时候,需要实现一个功能,就是进入一个应用,在这个应用中,屏幕的亮度变为最亮.关键代码如下 bt1.setOnClickListener(new OnClickListener() { @O ...
- Elasticsearch之插件扩展
Elasticsearch之插件介绍及安装 Elasticsearch之head插件安装之后的浏览详解 Elasticsearch之kopf插件安装之后的浏览详解 Elasticsearch-2.4. ...
- POJ Fence Repair(优先队列)
Fence Repair Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 51346 Accepted: 16857 De ...
- Spring源码分析专题 —— IOC容器启动过程(上篇)
声明 1.建议先阅读<Spring源码分析专题 -- 阅读指引> 2.强烈建议阅读过程中要参照调用过程图,每篇都有其对应的调用过程图 3.写文不易,转载请标明出处 前言 关于 IOC 容器 ...
- python 时间合集 一
**以下内容均为我个人的理解,如果发现错误或者疑问可以联系我共同探讨**#### python中4种时间表示形式:1.格式化时间字符串 2.时间戳 3.时间元祖 4.时间对象- string_time ...
- chocolatey 的安装与安装过程中的注意事项
安装的windows需求 Windows 7+ / Windows Server 2003+ PowerShell v2+ .NET Framework 4+ 注意:chocolatey的网站可能 ...
- Servlet 规范笔记—servlet概念及结构
Servlet, 这个词对java程序员并不陌生,我想几乎每个java程序员在学习J2EE知识时,首先学习的就是servlet,这是一种正确的学习方式,在我看来Servlet是J2EE的基础,要熟练 ...
- gogodroid--android 上的IPV6工具
gogodroid--android 上的IPV6工具 系统需求是 Android 1.6以上的系统,已经root,能够执行modprobe命令(在终端里输入modprobe,如果显示了帮助便可以), ...
- 字串乱序 PHP&JS
<?php /** * 字串乱序 PHP&JS * * php 中把字串乱序后输出给客户机的 JAVASCRIPT , JAVASCRIPT 中恢复 * 在指定长度提取一个字符,并把这一 ...