Android仿qq下拉刷新及向左滑动列表----PullToRefresh, SwipeMenuListView开源项目整合

项目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 plain copy
  1.  @Override  
  2.     public boolean onTouchEvent(MotionEvent ev) {  
  3.         if (mLastY == -1) {  
  4.             mLastY = ev.getRawY();  
  5.         }  
  6.   
  7.         switch (ev.getAction()) {  
  8.         case MotionEvent.ACTION_DOWN:  
  9.             mLastY = ev.getRawY();  
  10.   
  11.             int oldPos = mTouchPosition;  
  12.             mDownX = ev.getX();  
  13.             mDownY = ev.getY();  
  14.             mTouchState = TOUCH_STATE_NONE;  
  15.   
  16.             mTouchPosition = pointToPosition((int) ev.getX(), (int) ev.getY());  
  17.   
  18.             if (mTouchPosition == oldPos && mTouchView != null && mTouchView.isOpen()) {  
  19.                 mTouchState = TOUCH_STATE_X;  
  20.                 mTouchView.onSwipe(ev);  
  21.                 return true;  
  22.             }  
  23.   
  24.             View view = getChildAt(mTouchPosition - getFirstVisiblePosition());  
  25.   
  26.             if (mTouchView != null && mTouchView.isOpen()) {  
  27.                 mTouchView.smoothCloseMenu();  
  28.                 mTouchView = null;  
  29.                 return super.onTouchEvent(ev);  
  30.             }  
  31.             if (view instanceof SwipeMenuLayout) {  
  32.                 mTouchView = (SwipeMenuLayout) view;  
  33.             }  
  34.             if (mTouchView != null) {  
  35.                 mTouchView.onSwipe(ev);  
  36.             }  
  37.             break;  
  38.         case MotionEvent.ACTION_MOVE:  
  39.             final float deltaY = ev.getRawY() - mLastY;  
  40.             mLastY = ev.getRawY();  
  41.             // 非常感谢github上面的G友提出的宝贵建议和启发,这个地方要做一个判断。  
  42.             // 在侧滑的过程当中不应该同时出现用户本不期望的下拉操作,  
  43.             // 这样会给用户带来一种很不稳定的感受,是一种非常槽糕的用户体验。  
  44.             // Modified on 9/16/2015  
  45.             if (mTouchView == null || !mTouchView.isActive()) {  
  46.                 // 这个地方调换了if和else if的顺序,如果反过来会出现有时无法下拉刷新的bug  
  47.                 // Modified on 9/17/2015  
  48.                 if (getFirstVisiblePosition() == 0 && (mHeaderView.getVisiableHeight() > 0 || deltaY > 0)) {  
  49.                     // the first item is showing, header has shown or pull down.  
  50.                     updateHeaderHeight(deltaY / OFFSET_RADIO);  
  51.                     invokeOnScrolling();  
  52.                 } else if ((mFooterView.getBottomMargin() > 0 || deltaY < 0)) {  
  53.                     // last item, already pulled up or want to pull up.  
  54.                     updateFooterHeight(-deltaY / OFFSET_RADIO);  
  55.                 }  
  56.             }  
  57.   
  58.             float dy = Math.abs((ev.getY() - mDownY));  
  59.             float dx = Math.abs((ev.getX() - mDownX));  
  60.             if (mTouchState == TOUCH_STATE_X) {  
  61.                 if (mTouchView != null) {  
  62.                     mTouchView.onSwipe(ev);  
  63.                 }  
  64.                 getSelector().setState(new int[] { 0 });  
  65.                 ev.setAction(MotionEvent.ACTION_CANCEL);  
  66.                 super.onTouchEvent(ev);  
  67.                 return true;  
  68.             } else if (mTouchState == TOUCH_STATE_NONE) {  
  69.                 if (Math.abs(dy) > MAX_Y) {  
  70.                     mTouchState = TOUCH_STATE_Y;  
  71.                 } else if (dx > MAX_X) {  
  72.                     mTouchState = TOUCH_STATE_X;  
  73.                     if (mOnSwipeListener != null) {  
  74.                         mOnSwipeListener.onSwipeStart(mTouchPosition);  
  75.                     }  
  76.                 }  
  77.             }  
  78.             break;  
  79.         // default:  
  80.         // 非常感谢博友私信提出的修改意见,这个地方不应该写default,如果写成default的话,  
  81.         // ACTION_POINTER_DOWN和ACTION_POINTER_UP事件也会执行下面的语句,  
  82.         // 这样就会出现两个bug:  
  83.         // 1.当下拉未松开时,屏幕上有其它点按下,下拉状态会变成松开;  
  84.         // 2.当向左滑动列表的时候,屏幕上有其它点按下,再全部松开,滑出的列表会停在中间状态.  
  85.         // Modified on 8/26/2015  
  86.         case MotionEvent.ACTION_UP:  
  87.             mLastY = -1// reset  
  88.             if (mEnablePullLoad && mFooterView.getBottomMargin() > PULL_LOAD_MORE_DELTA) {  
  89.                 startLoadMore();  
  90.                 resetFooterHeight();  
  91.                 // 再次感谢同一位博友的来信提出的新的bug  
  92.                 // 这个地方应该在加一个如下所示的resetHeaderHeight()异步操作,  
  93.                 // 如果不加这个操作的话,会导致下拉接着上拉后,刷新的Header卡住不动的状况,  
  94.                 // 由于resetHeaderHeight()和resetFooterHeight()牵涉同一变量mScroller,  
  95.                 // 因此这里通过Android封装的异步类AsyncTask实现footerHeight和headerHeight的恢复。  
  96.                 // resetFooterHeight(),resetHeaderHeight()其实是同步的,前者执行完毕才执行后者。  
  97.                 // Modified on 8/28/2015  
  98.                 new ResetHeaderHeightTask().execute();  
  99.             } else if (getFirstVisiblePosition() == 0) {  
  100.                 // invoke refresh  
  101.                 if (mEnablePullRefresh && mHeaderView.getVisiableHeight() > mHeaderViewHeight) {  
  102.                     mPullRefreshing = true;  
  103.                     mHeaderView.setState(PullToRefreshListHeader.STATE_REFRESHING);  
  104.                     if (mListViewListener != null) {  
  105.                         mListViewListener.onRefresh();  
  106.                     }  
  107.                 }  
  108.                 resetHeaderHeight();  
  109.             }  
  110.   
  111.             if (mTouchState == TOUCH_STATE_X) {  
  112.                 if (mTouchView != null) {  
  113.                     mTouchView.onSwipe(ev);  
  114.                     if (!mTouchView.isOpen()) {  
  115.                         mTouchPosition = -1;  
  116.                         mTouchView = null;  
  117.                     }  
  118.                 }  
  119.                 if (mOnSwipeListener != null) {  
  120.                     mOnSwipeListener.onSwipeEnd(mTouchPosition);  
  121.                 }  
  122.                 ev.setAction(MotionEvent.ACTION_CANCEL);  
  123.                 super.onTouchEvent(ev);  
  124.                 return true;  
  125.             }  
  126.             break;  
  127.         }  
  128.         return super.onTouchEvent(ev);  
  129.     }  
  130.   
  131.     class ResetHeaderHeightTask extends AsyncTask<Void, Void, Void> {  
  132.         protected Void doInBackground(Void... params) {  
  133.             try {  
  134.                 Thread.sleep(400);  
  135.             } catch (InterruptedException e) {  
  136.                 e.printStackTrace();  
  137.             }  
  138.             return null;  
  139.         }  
  140.   
  141.         protected void onPostExecute(Void result) {  
  142.             mPullRefreshing = false;  
  143.             mHeaderView.setState(PullToRefreshListHeader.STATE_NORMAL);  
  144.             resetHeaderHeight();  
  145.   
  146.         }  
  147.     }  

其实在整合这两套开源框架的时候遇到了不少的问题,归结起来其实是对Android的Touch事件把握的不充分导致的,真正把原理、基础搞透彻了,做起项目来才能得心应手啊。这里专门写了一篇关于Touch事件的博客:

http://blog.csdn.net/licaomengrice/article/details/48415431

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值