catchPrevious article, This article mainly talks about how to implement the SwipeListView.
The previous article completed the slide control SwipeItemView. This control is a custom ViewGroup. It serves as an item of the list, providing some methods for the list to allow this SwipeItemView to slide its view content, and there will be smooth animation effects during the slide. The SwipeListView mentioned in this article is the specific implementation of this list. Of course, this SwipeListView inherits from ListView. In order to realize the functions we need, the focus is to rewrite the onTouchEvent() and onInterceptTouchEvent() methods of ListView. Let's talk about onTouchEvent() first:
@Override public boolean onTouchEvent(MotionEvent ev) { //if user had not set mSwipeItemViewID, not handle any touch event if(mSwipeItemViewID == -1) return (ev); if(mCancelMotionEvent && () == MotionEvent.ACTION_DOWN) { //(MotionEvent.ACTION_CANCEL); ("(), cancel ACTION_DOWN"); hideShowingItem(); return true; } else if(mCancelMotionEvent && () == MotionEvent.ACTION_MOVE) { if(() > 0) { (); //(-1, 0); } return true; } else if(mCancelMotionEvent && () == MotionEvent.ACTION_UP) { mCancelMotionEvent = false; return true; } switch(()) { case MotionEvent.ACTION_DOWN: { ("ACTION_DOWN"); if(mTracker == null) { mTracker = (); } else { (); } mActionDownX = (); mActionDownY = (); mLastMotionX = (); mLastMotionY = (); }break; case MotionEvent.ACTION_MOVE: { //if the scroll distance at X-axis or Y-axis less than the //TOUCH_SLOP, do not handle the event MotionEvent.ACTION_MOVE if((() - mActionDownX) < TOUCH_SLOP || (() - mActionDownY) < TOUCH_SLOP) break; float curX = (); float curY = (); int distanceX = (int)(mLastMotionX - curX); int distanceY = (int)(mLastMotionY - curY); if(mScrollDirection == DIRECTION_UNKNOW && (distanceY) <= (distanceX)) mScrollDirection = DIRECTION_HORIZONTAL; else if(mScrollDirection == DIRECTION_UNKNOW && (distanceY) > (distanceX)) mScrollDirection = DIRECTION_VERTICAL; //if ListView is scrolling vertical, do not handle the touch event if(mScrollDirection == DIRECTION_VERTICAL) break; int lastPos = pointToPosition((int)mActionDownX, (int)mActionDownY); int firstVisibleItemPos = getFirstVisiblePosition() - getHeaderViewsCount(); int factPos = lastPos - firstVisibleItemPos; mItemView = getChildAt(factPos); if(mItemView != null) { mSwipeItemView = (SwipeItemView)(mSwipeItemViewID); if(() != null && () <= ().getWidth() && () >= 0) { if(() + distanceX > ().getWidth()) distanceX = ().getWidth() - (); else if(() + distanceX < 0) distanceX = -(); (distanceX, 0); } mLastShowingPos = lastPos; (MotionEvent.ACTION_CANCEL); } mLastMotionX = curX; mLastMotionY = curY; }break; case MotionEvent.ACTION_UP: { ("ACTION_UP"); if(mTracker != null) { (); (); mTracker = null; } //reset the mScrollDirection to DIRECTION_UNKNOW mScrollDirection = DIRECTION_UNKNOW; //reset the mCancelMotionEvent to false mCancelMotionEvent = false; //ensure if the showing item need open or hide if(mLastShowingPos != -1) ensureIfItemOpenOrHide(); }break; case MotionEvent.ACTION_CANCEL: { hideShowingItem(); }break; } return (ev); }
The above code first analyzes the operation of the user sliding an item. This operation starts with ACTION_DOWN, and a series of ACTION_MOVE ends with ACTION_UP. Therefore, in the ACTION_DOWN event, we first record the initial event locations mActionDownX and mActionDownY; then in the ACTION_MOVE event, we must make a judgment first. This judgment is divided into two steps. The first step is to judge the current event locations curX and curY under the ACTION_MOVE event. Whether the distance between the position recorded on the x-axis and the y-axis and ACTION_DOWN has exceeded the value of TOUCH_SLOP. This value is the threshold used by Android to determine whether a slide should be performed. In the second step, we need to further determine whether the user is sliding the entire list vertically or sliding a certain item left and right. The logical judgment here is simple. If the TOUCH_SLOP threshold exceeds the y-axis distance, we think that the user is sliding a single item left and right, and vice versa. Here we distinguish it by three states. IRECTION_UNKNOW indicates that the current sliding operation has not yet been judged for left or vertical sliding. DIRECTION_HORIZONTAL indicates that the current sliding operation is determined to be left and right sliding. DIRECTION_VERTICAL indicates that the judgment is longitudinal sliding. Once the sliding operation is determined, before ACTION_UP processing, we all think that the user is sliding in the same direction. In the ACTION_UP event, reset the sliding operation status to DIRECTION_UNKNOW for the next judgment, and this ACTION_U When processing the P event, if the position of the currently sliding item is not -1, it means that it is a slide-open operation. This time, if you think about it carefully, the user may leave the screen without completely sliding the item open. At this time, we should judge that the sliding item should be completely opened or closed at this time. The logical judgment here is that the distance the item has been sliding open exceeds half of its own width, then it will be opened completely, otherwise it will be closed. The specific code of ensureIfItemOpenOrHide() is as follows:
private void ensureIfItemOpenOrHide() { if(mLastShowingPos != -1) { int firstVisibleItemPos = getFirstVisiblePosition() - getHeaderViewsCount(); int factPos = mLastShowingPos - firstVisibleItemPos; mItemView = getChildAt(factPos); if(mItemView != null) { mSwipeItemView = (SwipeItemView)(mSwipeItemViewID); if(() != null && () >= ().getWidth() / 2) { openShowingItem(); } else if(() != null) { hideShowingItem(); } } } }
Then we have finished processing the logical judgment of the first user sliding operation. Next is the second time. Why is the second time? The first time the user slides open a single item to make it open and touch the screen again. This time we have to make a judgment again. First, if the position of ACTION_DOWN is within the range of the button displayed by the item slide, it means that the current user is clicking on this button, so we do not do additional processing and directly hand it over to the default logical processing of the list; second, if the position of ACTION_DOWN is not within the range of the button displayed after the item slides open, how does it mean that the current user is only operating the other range of the list, here we close the currently opened item and cancel the subsequent touch event. Here, we have to intercept the ACTIOIN_DOWN event. We need to rewrite the onInterceptTouchEvent() method of ListView, the code is as follows:
public boolean onInterceptTouchEvent(MotionEvent ev) { //if user had not set mSwipeItemViewID, not handle any touch event if(mSwipeItemViewID == -1) return (ev); if(mLastShowingPos != -1 && () == MotionEvent.ACTION_DOWN && !isClickChildView(ev)) { ("(), intercept ACTION_DOWN"); mCancelMotionEvent = true; return true; } else if(mLastShowingPos == -1 && () == MotionEvent.ACTION_DOWN) { return true; } return (ev); }
The above mCancelMotionEvent is used to determine whether the subsequent touch event needs to be cancelled in onTouchEvent(). During that period, how to determine whether the current ACTION_DOWN event occurs within the scope of the button? Use the following code:
private boolean isClickChildView(MotionEvent event) { if(mLastShowingPos != -1) { int firstVisibleItemPos = getFirstVisiblePosition() - getHeaderViewsCount(); int factPos = mLastShowingPos - firstVisibleItemPos; mItemView = getChildAt(factPos); if(mItemView != null) { mSwipeItemView = (SwipeItemView)(mSwipeItemViewID); View slidingView = (); if(slidingView != null) { int[] slidingViewLocation = new int[2]; (slidingViewLocation); int left = slidingViewLocation[0]; int right = slidingViewLocation[0] + (); int top = slidingViewLocation[1]; int bottom = slidingViewLocation[1] + (); return (() > left && () < right && () > top && () < bottom); } } } return false; }
The rest is how to open or close an item, the code is as follows:
private void openShowingItem() { if(mLastShowingPos != -1) { int firstVisibleItemPos = getFirstVisiblePosition() - getHeaderViewsCount(); int factPos = mLastShowingPos - firstVisibleItemPos; mItemView = getChildAt(factPos); if(mItemView != null) { mSwipeItemView = (SwipeItemView)(mSwipeItemViewID); if(() != null) ( ().getWidth(), 0); } } } private void hideShowingItem() { if(mLastShowingPos != -1) { int firstVisibleItemPos = getFirstVisiblePosition() - getHeaderViewsCount(); int factPos = mLastShowingPos - firstVisibleItemPos; mItemView = getChildAt(factPos); if(mItemView != null) { mSwipeItemView = (SwipeItemView)(mSwipeItemViewID); (0, 0); } mLastShowingPos = -1; } }
The scrollToWithAnimation() method above is the method we implemented in the previous blog to move the item and make it animated.
In this way, the overall implementation plan of the entire WeChat sliding deletion operation has been explained. For some details, you can check the source code of this project. The source code I have uploaded to my Github homepage:/YoungLeeForeverBoy/SlidingListViewPlus。
The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.