Android仿qq下拉刷新及向左滑动列表----PullToRefresh, SwipeMenuListView开源项目整合
2016-01-06 10:29
609 查看
版权声明:本文为博主原创文章,未经博主允许不得转载。
项目Github链接:https://github.com/licaomeng/Android-PullToRefresh-SwipeMenuListView-Sample
如果项目对您有帮助,欢迎小伙伴们在github上Star我的项目~
PullToRefresh是一个非常完美的下拉刷新的开源项目,SwipeMenuListView是一个向左滑动ListView中item实现可以删除功能的开源项目。笔者在此将两套开源项目整合形成一套,类似于手机qq那样同时支持下拉刷新和向左滑动的列表。效果如下:
两套框架整合的关键在于对Touch事件处理的把握,Touch事件的处理主要由Action_Down, Action_Move,Action_Up组成。Action_Down表示用户按下屏幕的操作,Action_Up表示用户按下屏幕后抬起的操作,Action_Move表示用户在屏幕上的移动操作。Touch事件处理流程图如下:
1. 当ACTION_UP事件生效的时候,判断如果是下拉操作,执行onRefresh(), 实现下拉刷新,然后执行resetHeaderHeight()恢复Header的高度;判断如果是上拉操作,当mFooterView的高度大于自定义的高度,那么就执行startLoadMore()加载更多,然后执行resetFooterHeight()恢复Footer的高度。判断如果是侧滑那么就结束SwipeMenu的滑动。
2. 当ACTION_MOVE事件生效的时候,分别记录X,Y方向上的偏移。如果是X方向上的偏移,那么对应的就是SwipeMenuListView的特效操作;如果是Y方向上的偏移, Y方向向下,那么对应的就是下拉刷新操作,Y方向向上,那么对应的就是上拉操作。
Touch事件相关代码如下:
[java] view
plaincopy
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mLastY == -1) {
mLastY = ev.getRawY();
}
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastY = ev.getRawY();
int oldPos = mTouchPosition;
mDownX = ev.getX();
mDownY = ev.getY();
mTouchState = TOUCH_STATE_NONE;
mTouchPosition = pointToPosition((int) ev.getX(), (int) ev.getY());
if (mTouchPosition == oldPos && mTouchView != null && mTouchView.isOpen()) {
mTouchState = TOUCH_STATE_X;
mTouchView.onSwipe(ev);
return true;
}
View view = getChildAt(mTouchPosition - getFirstVisiblePosition());
if (mTouchView != null && mTouchView.isOpen()) {
mTouchView.smoothCloseMenu();
mTouchView = null;
return super.onTouchEvent(ev);
}
if (view instanceof SwipeMenuLayout) {
mTouchView = (SwipeMenuLayout) view;
}
if (mTouchView != null) {
mTouchView.onSwipe(ev);
}
break;
case MotionEvent.ACTION_MOVE:
final float deltaY = ev.getRawY() - mLastY;
mLastY = ev.getRawY();
// 非常感谢github上面的G友提出的宝贵建议和启发,这个地方要做一个判断。
// 在侧滑的过程当中不应该同时出现用户本不期望的下拉操作,
// 这样会给用户带来一种很不稳定的感受,是一种非常槽糕的用户体验。
// Modified on 9/16/2015
if (mTouchView == null || !mTouchView.isActive()) {
// 这个地方调换了if和else if的顺序,如果反过来会出现有时无法下拉刷新的bug
// Modified on 9/17/2015
if (getFirstVisiblePosition() == 0 && (mHeaderView.getVisiableHeight() > 0 || deltaY > 0)) {
// the first item is showing, header has shown or pull down.
updateHeaderHeight(deltaY / OFFSET_RADIO);
invokeOnScrolling();
} else if ((mFooterView.getBottomMargin() > 0 || deltaY < 0)) {
// last item, already pulled up or want to pull up.
updateFooterHeight(-deltaY / OFFSET_RADIO);
}
}
float dy = Math.abs((ev.getY() - mDownY));
float dx = Math.abs((ev.getX() - mDownX));
if (mTouchState == TOUCH_STATE_X) {
if (mTouchView != null) {
mTouchView.onSwipe(ev);
}
getSelector().setState(new int[] { 0 });
ev.setAction(MotionEvent.ACTION_CANCEL);
super.onTouchEvent(ev);
return true;
} else if (mTouchState == TOUCH_STATE_NONE) {
if (Math.abs(dy) > MAX_Y) {
mTouchState = TOUCH_STATE_Y;
} else if (dx > MAX_X) {
mTouchState = TOUCH_STATE_X;
if (mOnSwipeListener != null) {
mOnSwipeListener.onSwipeStart(mTouchPosition);
}
}
}
break;
// default:
// 非常感谢博友私信提出的修改意见,这个地方不应该写default,如果写成default的话,
// ACTION_POINTER_DOWN和ACTION_POINTER_UP事件也会执行下面的语句,
// 这样就会出现两个bug:
// 1.当下拉未松开时,屏幕上有其它点按下,下拉状态会变成松开;
// 2.当向左滑动列表的时候,屏幕上有其它点按下,再全部松开,滑出的列表会停在中间状态.
// Modified on 8/26/2015
case MotionEvent.ACTION_UP:
mLastY = -1; // reset
if (mEnablePullLoad && mFooterView.getBottomMargin() > PULL_LOAD_MORE_DELTA) {
startLoadMore();
resetFooterHeight();
// 再次感谢同一位博友的来信提出的新的bug
// 这个地方应该在加一个如下所示的resetHeaderHeight()异步操作,
// 如果不加这个操作的话,会导致下拉接着上拉后,刷新的Header卡住不动的状况,
// 由于resetHeaderHeight()和resetFooterHeight()牵涉同一变量mScroller,
// 因此这里通过Android封装的异步类AsyncTask实现footerHeight和headerHeight的恢复。
// resetFooterHeight(),resetHeaderHeight()其实是同步的,前者执行完毕才执行后者。
// Modified on 8/28/2015
new ResetHeaderHeightTask().execute();
} else if (getFirstVisiblePosition() == 0) {
// invoke refresh
if (mEnablePullRefresh && mHeaderView.getVisiableHeight() > mHeaderViewHeight) {
mPullRefreshing = true;
mHeaderView.setState(PullToRefreshListHeader.STATE_REFRESHING);
if (mListViewListener != null) {
mListViewListener.onRefresh();
}
}
resetHeaderHeight();
}
if (mTouchState == TOUCH_STATE_X) {
if (mTouchView != null) {
mTouchView.onSwipe(ev);
if (!mTouchView.isOpen()) {
mTouchPosition = -1;
mTouchView = null;
}
}
if (mOnSwipeListener != null) {
mOnSwipeListener.onSwipeEnd(mTouchPosition);
}
ev.setAction(MotionEvent.ACTION_CANCEL);
super.onTouchEvent(ev);
return true;
}
break;
}
return super.onTouchEvent(ev);
}
class ResetHeaderHeightTask extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... params) {
try {
Thread.sleep(400);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(Void result) {
mPullRefreshing = false;
mHeaderView.setState(PullToRefreshListHeader.STATE_NORMAL);
resetHeaderHeight();
}
}
其实在整合这两套开源框架的时候遇到了不少的问题,归结起来其实是对Android的Touch事件把握的不充分导致的,真正把原理、基础搞透彻了,做起项目来才能得心应手啊。这里专门写了一篇关于Touch事件的博客:
/article/9220241.html
项目Github链接:https://github.com/licaomeng/Android-PullToRefresh-SwipeMenuListView-Sample
如果项目对您有帮助,欢迎小伙伴们在github上Star我的项目~
PullToRefresh是一个非常完美的下拉刷新的开源项目,SwipeMenuListView是一个向左滑动ListView中item实现可以删除功能的开源项目。笔者在此将两套开源项目整合形成一套,类似于手机qq那样同时支持下拉刷新和向左滑动的列表。效果如下:
两套框架整合的关键在于对Touch事件处理的把握,Touch事件的处理主要由Action_Down, Action_Move,Action_Up组成。Action_Down表示用户按下屏幕的操作,Action_Up表示用户按下屏幕后抬起的操作,Action_Move表示用户在屏幕上的移动操作。Touch事件处理流程图如下:
1. 当ACTION_UP事件生效的时候,判断如果是下拉操作,执行onRefresh(), 实现下拉刷新,然后执行resetHeaderHeight()恢复Header的高度;判断如果是上拉操作,当mFooterView的高度大于自定义的高度,那么就执行startLoadMore()加载更多,然后执行resetFooterHeight()恢复Footer的高度。判断如果是侧滑那么就结束SwipeMenu的滑动。
2. 当ACTION_MOVE事件生效的时候,分别记录X,Y方向上的偏移。如果是X方向上的偏移,那么对应的就是SwipeMenuListView的特效操作;如果是Y方向上的偏移, Y方向向下,那么对应的就是下拉刷新操作,Y方向向上,那么对应的就是上拉操作。
Touch事件相关代码如下:
[java] view
plaincopy
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mLastY == -1) {
mLastY = ev.getRawY();
}
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastY = ev.getRawY();
int oldPos = mTouchPosition;
mDownX = ev.getX();
mDownY = ev.getY();
mTouchState = TOUCH_STATE_NONE;
mTouchPosition = pointToPosition((int) ev.getX(), (int) ev.getY());
if (mTouchPosition == oldPos && mTouchView != null && mTouchView.isOpen()) {
mTouchState = TOUCH_STATE_X;
mTouchView.onSwipe(ev);
return true;
}
View view = getChildAt(mTouchPosition - getFirstVisiblePosition());
if (mTouchView != null && mTouchView.isOpen()) {
mTouchView.smoothCloseMenu();
mTouchView = null;
return super.onTouchEvent(ev);
}
if (view instanceof SwipeMenuLayout) {
mTouchView = (SwipeMenuLayout) view;
}
if (mTouchView != null) {
mTouchView.onSwipe(ev);
}
break;
case MotionEvent.ACTION_MOVE:
final float deltaY = ev.getRawY() - mLastY;
mLastY = ev.getRawY();
// 非常感谢github上面的G友提出的宝贵建议和启发,这个地方要做一个判断。
// 在侧滑的过程当中不应该同时出现用户本不期望的下拉操作,
// 这样会给用户带来一种很不稳定的感受,是一种非常槽糕的用户体验。
// Modified on 9/16/2015
if (mTouchView == null || !mTouchView.isActive()) {
// 这个地方调换了if和else if的顺序,如果反过来会出现有时无法下拉刷新的bug
// Modified on 9/17/2015
if (getFirstVisiblePosition() == 0 && (mHeaderView.getVisiableHeight() > 0 || deltaY > 0)) {
// the first item is showing, header has shown or pull down.
updateHeaderHeight(deltaY / OFFSET_RADIO);
invokeOnScrolling();
} else if ((mFooterView.getBottomMargin() > 0 || deltaY < 0)) {
// last item, already pulled up or want to pull up.
updateFooterHeight(-deltaY / OFFSET_RADIO);
}
}
float dy = Math.abs((ev.getY() - mDownY));
float dx = Math.abs((ev.getX() - mDownX));
if (mTouchState == TOUCH_STATE_X) {
if (mTouchView != null) {
mTouchView.onSwipe(ev);
}
getSelector().setState(new int[] { 0 });
ev.setAction(MotionEvent.ACTION_CANCEL);
super.onTouchEvent(ev);
return true;
} else if (mTouchState == TOUCH_STATE_NONE) {
if (Math.abs(dy) > MAX_Y) {
mTouchState = TOUCH_STATE_Y;
} else if (dx > MAX_X) {
mTouchState = TOUCH_STATE_X;
if (mOnSwipeListener != null) {
mOnSwipeListener.onSwipeStart(mTouchPosition);
}
}
}
break;
// default:
// 非常感谢博友私信提出的修改意见,这个地方不应该写default,如果写成default的话,
// ACTION_POINTER_DOWN和ACTION_POINTER_UP事件也会执行下面的语句,
// 这样就会出现两个bug:
// 1.当下拉未松开时,屏幕上有其它点按下,下拉状态会变成松开;
// 2.当向左滑动列表的时候,屏幕上有其它点按下,再全部松开,滑出的列表会停在中间状态.
// Modified on 8/26/2015
case MotionEvent.ACTION_UP:
mLastY = -1; // reset
if (mEnablePullLoad && mFooterView.getBottomMargin() > PULL_LOAD_MORE_DELTA) {
startLoadMore();
resetFooterHeight();
// 再次感谢同一位博友的来信提出的新的bug
// 这个地方应该在加一个如下所示的resetHeaderHeight()异步操作,
// 如果不加这个操作的话,会导致下拉接着上拉后,刷新的Header卡住不动的状况,
// 由于resetHeaderHeight()和resetFooterHeight()牵涉同一变量mScroller,
// 因此这里通过Android封装的异步类AsyncTask实现footerHeight和headerHeight的恢复。
// resetFooterHeight(),resetHeaderHeight()其实是同步的,前者执行完毕才执行后者。
// Modified on 8/28/2015
new ResetHeaderHeightTask().execute();
} else if (getFirstVisiblePosition() == 0) {
// invoke refresh
if (mEnablePullRefresh && mHeaderView.getVisiableHeight() > mHeaderViewHeight) {
mPullRefreshing = true;
mHeaderView.setState(PullToRefreshListHeader.STATE_REFRESHING);
if (mListViewListener != null) {
mListViewListener.onRefresh();
}
}
resetHeaderHeight();
}
if (mTouchState == TOUCH_STATE_X) {
if (mTouchView != null) {
mTouchView.onSwipe(ev);
if (!mTouchView.isOpen()) {
mTouchPosition = -1;
mTouchView = null;
}
}
if (mOnSwipeListener != null) {
mOnSwipeListener.onSwipeEnd(mTouchPosition);
}
ev.setAction(MotionEvent.ACTION_CANCEL);
super.onTouchEvent(ev);
return true;
}
break;
}
return super.onTouchEvent(ev);
}
class ResetHeaderHeightTask extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... params) {
try {
Thread.sleep(400);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(Void result) {
mPullRefreshing = false;
mHeaderView.setState(PullToRefreshListHeader.STATE_NORMAL);
resetHeaderHeight();
}
}
其实在整合这两套开源框架的时候遇到了不少的问题,归结起来其实是对Android的Touch事件把握的不充分导致的,真正把原理、基础搞透彻了,做起项目来才能得心应手啊。这里专门写了一篇关于Touch事件的博客:
/article/9220241.html
相关文章推荐
- Android-AIDL,service,Binder,Bundles,Messenger,transact()与onTransact()函数
- Android Studio下如何配置AIDL文件
- Android应用之——利用getItemViewType为Listview的item设置不同的布局
- Android 通知栏Notification的整合 全面学习
- Android基础-----Activity,Intent
- Android:图形(二)
- Android中跨进程通讯的4种方式
- Android库工程及代码混淆(project.properties、proguard-project.txt)
- android 监听键盘弹出收起状态
- Android 加密之MD5加密
- Android单元测试研究与实践
- android初学之android:screenOrientation属性
- Failed to resolve: com.android.support:support-v4:23.1.1
- android 监听Home键和亮灭屏
- Android M 新的运行时权限开发者需要知道的一切
- Android不调用第三方程序发送邮件
- android 判断应用是否在后台
- Android实现点击通知栏通知恢复前台
- Android开发之imageView图片按比例缩放的实现方法
- Android使用Fragment打造万能页面切换框架