Android QQ消息列表(下拉刷新 +item侧滑 )

来源:互联网 发布:美工设计是什么 编辑:程序博客网 时间:2024/06/03 14:56

最近一个项目中用到一个类似于QQ消息的功能,主要是在ListView的每一个Item可以侧滑显示菜单,并且可以下拉刷新。如图:

下拉刷新,Item侧滑出现菜单(ItemMenu)。

现提供Github上的两个ListView相关的开源项目,

Android-PulltoRefersh:https://github.com/chrisbanes/Android-PullToRefresh

SwipeMenuListView:https://github.com/baoyongzhang/SwipeMenuListView

分别是上拉加载,下拉刷新的ListView和侧滑显示菜单(ItemMenu)的ListView。

至于这两个“组件”的使用不用详细说了,在Github里面说的很明确了。


再回到我们这个功能上,我在实现的过程中,将SwipeMenuListView放在了一个PullToRefreshScrollView里面,这样来实现既可以下刷又可以侧滑的ListView,但是这样做有问题,首先是ScrollView中放置ListView导致ScrollView无法计算具体高度,从而只显示一个Item的高度,显示不完全。至于这个问题的解决方式很容易找到。在这里可以参考另一篇Blog -- ScrollView内展示ListView显示不完全(只显示一个Item)


这个问题解决了,可以完全显示了。又有一个新的问题,在滑动Item试图显示ItemMenu的时候发现特别卡顿,本来是互斥(ItemMenu显示)的事件,也不互斥了,而且ItemMenu显示不完全。产生问题的原因就是因为PullToRefreceScrollView的onTouchEvent与SwipeMenuListView的冲突了,就是我在请教鸿婶时候,鸿婶描述的那样,Item的滑动事件被黑了。


下面我们来解决这个问题,就要在ScrollView和SwipeMenuListView的onTouchEvent上入手。

Touch事件主要由Action_Down,Action_Move,Active_up组成,Action_Down表示按下屏幕的操作,Action_ Up表示手指离开屏幕时的操作,Action_Move表示用户在屏幕上面滑动时候的操作。

1.      当ACTION_UP事件生效的时候,判断如果是下拉操作,执行onRefresh(), 实现下拉刷新,然后执行resetHeaderHeight()恢复Header的高度;判断如果是上拉操作,当mFooterView的高度大于自定义的高度,那么就执行startLoadMore()加载更多,然后执行resetFooterHeight()恢复Footer的高度。判断如果是侧滑那么就结束SwipeMenu的滑动。

2.      当ACTION_MOVE事件生效的时候,分别记录X,Y方向上的偏移。如果是X方向上的偏移,那么对应的就是SwipeMenuListView的特效操作;如果是Y方向上的偏移, Y方向向下,那么对应的就是下拉刷新操作,Y方向向上,那么对应的就是上拉操作。

 @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仿qq下拉刷新及向左滑动列表----PullToRefresh, SwipeMenuListView开源项目整合

该项目的地址:https://github.com/licaomeng/Android-PullToRefresh-SwipeMenuListView-Sample


1 1
原创粉丝点击