Android仿微信列表滑動刪除 如何實現(xiàn)滑動列表SwipeListView
接上一篇,本篇主要講如何實現(xiàn)滑動列表SwipeListView。
上篇完成了滑動控件SwipeItemView,這個控件是一個自定義的ViewGroup,作為列表的一個item,為列表提供一些方法讓這個SwipeItemView能滑動其視圖內(nèi)容,同時滑動過程中會有順滑的動畫效果。而本篇講的SwipeListView則是這個列表的具體實現(xiàn)了。當(dāng)然啦,這個SwipeListView繼承自ListView,為了實現(xiàn)我們需要的功能,重點就是重寫ListView的onTouchEvent()以及onInterceptTouchEvent()這個方法了。先說onTouchEvent():
@Override public boolean onTouchEvent(MotionEvent ev) { //if user had not set mSwipeItemViewID, not handle any touch event if(mSwipeItemViewID == -1) return super.onTouchEvent(ev); if(mCancelMotionEvent && ev.getAction() == MotionEvent.ACTION_DOWN) { //ev.setAction(MotionEvent.ACTION_CANCEL); LogUtil.Log("SwipeListView.onTouchEvent(), cancel ACTION_DOWN"); hideShowingItem(); return true; } else if(mCancelMotionEvent && ev.getAction() == MotionEvent.ACTION_MOVE) { if(mSwipeItemView.getCurrentScrollX() > 0) { mSwipeItemView.computeScroll(); //mSwipeItemView.scrollBy(-1, 0); } return true; } else if(mCancelMotionEvent && ev.getAction() == MotionEvent.ACTION_UP) { mCancelMotionEvent = false; return true; } switch(ev.getAction()) { case MotionEvent.ACTION_DOWN: { LogUtil.Log("ACTION_DOWN"); if(mTracker == null) { mTracker = VelocityTracker.obtain(); } else { mTracker.clear(); } mActionDownX = ev.getX(); mActionDownY = ev.getY(); mLastMotionX = ev.getX(); mLastMotionY = ev.getY(); }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(Math.abs(ev.getX() - mActionDownX) < TOUCH_SLOP || Math.abs(ev.getY() - mActionDownY) < TOUCH_SLOP) break; float curX = ev.getX(); float curY = ev.getY(); int distanceX = (int)(mLastMotionX - curX); int distanceY = (int)(mLastMotionY - curY); if(mScrollDirection == DIRECTION_UNKNOW && Math.abs(distanceY) <= Math.abs(distanceX)) mScrollDirection = DIRECTION_HORIZONTAL; else if(mScrollDirection == DIRECTION_UNKNOW && Math.abs(distanceY) > Math.abs(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)mItemView.findViewById(mSwipeItemViewID); if(mSwipeItemView.getSlidingView() != null && mSwipeItemView.getScrollX() <= mSwipeItemView.getSlidingView().getWidth() && mSwipeItemView.getScrollX() >= 0) { if(mSwipeItemView.getScrollX() + distanceX > mSwipeItemView.getSlidingView().getWidth()) distanceX = mSwipeItemView.getSlidingView().getWidth() - mSwipeItemView.getScrollX(); else if(mSwipeItemView.getScrollX() + distanceX < 0) distanceX = -mSwipeItemView.getScrollX(); mSwipeItemView.scrollBy(distanceX, 0); } mLastShowingPos = lastPos; ev.setAction(MotionEvent.ACTION_CANCEL); } mLastMotionX = curX; mLastMotionY = curY; }break; case MotionEvent.ACTION_UP: { LogUtil.Log("ACTION_UP"); if(mTracker != null) { mTracker.clear(); mTracker.recycle(); 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 super.onTouchEvent(ev); }
上面代碼,首先分析用戶滑開一個item的操作,這個操作以ACTION_DOWN起始,一系列的ACTION_MOVE,以ACTION_UP作為結(jié)束,所以,在ACTION_DOWN事件里面,我們先記錄下最開始的事件位置mActionDownX和mActionDownY;然后再ACTION_MOVE事件里面,我們先要進行判斷,這個判斷分兩步,第一步,判斷這個ACTION_MOVE事件下,當(dāng)前事件的位置curX和curY在x軸上以及y軸上和ACTION_DOWN里面記錄的位置的距離是否已經(jīng)超過TOUCH_SLOP的值,這個值是android用來判斷是否應(yīng)該進行一次滑動的閾值,第二步,我們要進一步判斷用戶是縱向滑動這整個列表還是左右滑動某個item,這里的邏輯判斷就簡單點處理,若是超過TOUCH_SLOP閾值的情況下,x軸方向上距離大于y軸的,我們就認為用戶是左右滑動單個item,反之則是縱向滑動整個列表,這里我們用三種狀態(tài)區(qū)分,DIRECTION_UNKNOW表示當(dāng)前的滑動操作還沒有進行判斷左右滑動還是縱向滑動,DIRECTION_HORIZONTAL表示當(dāng)前滑動操作判定為左右滑動,DIRECTION_VERTICAL表示判定為縱向滑動,一旦滑動操作被判定了,則在ACTION_UP處理前,我們都認為用戶是做同一方向的滑動;ACTION_UP事件里面,重置滑動操作狀態(tài)為DIRECTION_UNKNOW以便下一次的判定,以及這次ACTION_UP事件處理的時候,如果當(dāng)前滑開的item的位置mLastShowingPos不為-1,則表示當(dāng)前是一次滑開的操作,這次仔細想想,用戶可能在并沒有完全滑開這個item的狀態(tài)下手就離開屏幕了,這時候我們就應(yīng)該要判斷此時這個被滑動的item是應(yīng)該完全打開又或者是關(guān)閉,這里的邏輯判斷是item已經(jīng)滑開的距離超過它本身寬度的一半的話,就完全打開它,否則就關(guān)閉它,ensureIfItemOpenOrHide()的具體代碼如下:
private void ensureIfItemOpenOrHide() { if(mLastShowingPos != -1) { int firstVisibleItemPos = getFirstVisiblePosition() - getHeaderViewsCount(); int factPos = mLastShowingPos - firstVisibleItemPos; mItemView = getChildAt(factPos); if(mItemView != null) { mSwipeItemView = (SwipeItemView)mItemView.findViewById(mSwipeItemViewID); if(mSwipeItemView.getSlidingView() != null && mSwipeItemView.getScrollX() >= mSwipeItemView.getSlidingView().getWidth() / 2) { openShowingItem(); } else if(mSwipeItemView.getSlidingView() != null) { hideShowingItem(); } } } }
那第一次的用戶滑動操作的邏輯判定我們就算處理完了。接下來是第二次的,為什么說第二次呢,第一次用戶滑開了某單個的item,使其處于打開的狀態(tài)下,再一次觸摸屏幕,這次我們則要再一次進行判定,其一,如果ACTION_DOWN發(fā)生的位置在item滑開顯示出來的button的范圍內(nèi),表示當(dāng)前用戶是點擊這個button,這樣我們就不做額外處理,直接交由列表默認的邏輯處理;其二,如果ACTION_DOWN發(fā)生的位置不在item滑開后顯示出來的button范圍內(nèi),怎表示當(dāng)前用戶只是操作列表的其他范圍,這里我們就關(guān)閉當(dāng)前打開了的item,并取消后續(xù)的touch事件,這里的話,我們就要截獲這個ACTIOIN_DOWN事件了,需要重寫ListView的onInterceptTouchEvent()方法,代碼如下:
public boolean onInterceptTouchEvent(MotionEvent ev) { //if user had not set mSwipeItemViewID, not handle any touch event if(mSwipeItemViewID == -1) return super.onInterceptTouchEvent(ev); if(mLastShowingPos != -1 && ev.getAction() == MotionEvent.ACTION_DOWN && !isClickChildView(ev)) { LogUtil.Log("SwipeListView.onInterceptTouchEvent(), intercept ACTION_DOWN"); mCancelMotionEvent = true; return true; } else if(mLastShowingPos == -1 && ev.getAction() == MotionEvent.ACTION_DOWN) { return true; } return super.onInterceptTouchEvent(ev); }
上面的mCancelMotionEvent是用來在onTouchEvent()里面判斷是否需要取消后續(xù)touch事件的標志,那期間,如何判斷當(dāng)前的ACTION_DOWN事件是否發(fā)生在button的范圍內(nèi)呢,使用如下代碼:
private boolean isClickChildView(MotionEvent event) { if(mLastShowingPos != -1) { int firstVisibleItemPos = getFirstVisiblePosition() - getHeaderViewsCount(); int factPos = mLastShowingPos - firstVisibleItemPos; mItemView = getChildAt(factPos); if(mItemView != null) { mSwipeItemView = (SwipeItemView)mItemView.findViewById(mSwipeItemViewID); View slidingView = mSwipeItemView.getSlidingView(); if(slidingView != null) { int[] slidingViewLocation = new int[2]; slidingView.getLocationOnScreen(slidingViewLocation); int left = slidingViewLocation[0]; int right = slidingViewLocation[0] + slidingView.getWidth(); int top = slidingViewLocation[1]; int bottom = slidingViewLocation[1] + slidingView.getHeight(); return (event.getRawX() > left && event.getRawX() < right && event.getRawY() > top && event.getRawY() < bottom); } } } return false; }
剩下的,就是如何打開或者關(guān)閉一個item了,代碼如下:
private void openShowingItem() { if(mLastShowingPos != -1) { int firstVisibleItemPos = getFirstVisiblePosition() - getHeaderViewsCount(); int factPos = mLastShowingPos - firstVisibleItemPos; mItemView = getChildAt(factPos); if(mItemView != null) { mSwipeItemView = (SwipeItemView)mItemView.findViewById(mSwipeItemViewID); if(mSwipeItemView.getSlidingView() != null) mSwipeItemView.scrollToWithAnimation( mSwipeItemView.getSlidingView().getWidth(), 0); } } } private void hideShowingItem() { if(mLastShowingPos != -1) { int firstVisibleItemPos = getFirstVisiblePosition() - getHeaderViewsCount(); int factPos = mLastShowingPos - firstVisibleItemPos; mItemView = getChildAt(factPos); if(mItemView != null) { mSwipeItemView = (SwipeItemView)mItemView.findViewById(mSwipeItemViewID); mSwipeItemView.scrollToWithAnimation(0, 0); } mLastShowingPos = -1; } }
上面的scrollToWithAnimation()方法就是上一篇博客里面我們實現(xiàn)了的移動item并使其帶有動畫效果的方法了。
這樣,整個仿微信滑動刪除操作的總體實現(xiàn)方案就解釋完畢了,具體一些細節(jié)的話可以查看這個工程的源碼,源碼我已經(jīng)上傳到了我的Github主頁上:https://github.com/YoungLeeForeverBoy/SlidingListViewPlus。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android ImageView隨手勢變化動態(tài)縮放圖片
這篇文章主要為大家詳細介紹了Android ImageView隨手勢變化動態(tài)縮放圖片的相關(guān)資料,感興趣的小伙伴們可以參考一下2016-05-05Android 自定義View的構(gòu)造函數(shù)詳細介紹
這篇文章主要介紹了Android 自定義View的構(gòu)造函數(shù)詳細介紹的相關(guān)資料,這里對構(gòu)造函數(shù)進行了對比按需使用,需要的朋友可以參考下2016-12-12Android webview轉(zhuǎn)PDF的方法示例
本篇文章主要介紹了Android webview轉(zhuǎn)PDF的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-01-01Android studio listview實現(xiàn)列表數(shù)據(jù)顯示 數(shù)據(jù)循環(huán)顯示效果
這篇文章主要介紹了Android studio listview實現(xiàn)列表數(shù)據(jù)顯示 數(shù)據(jù)循環(huán)顯示功能,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-04-04android 動態(tài)控制狀態(tài)欄顯示和隱藏的方法實例
這篇文章主要介紹了2013-12-12