Android仿微信列表滑動(dòng)刪除 如何實(shí)現(xiàn)滑動(dòng)列表SwipeListView
接上一篇,本篇主要講如何實(shí)現(xiàn)滑動(dòng)列表SwipeListView。
上篇完成了滑動(dòng)控件SwipeItemView,這個(gè)控件是一個(gè)自定義的ViewGroup,作為列表的一個(gè)item,為列表提供一些方法讓這個(gè)SwipeItemView能滑動(dòng)其視圖內(nèi)容,同時(shí)滑動(dòng)過(guò)程中會(huì)有順滑的動(dòng)畫(huà)效果。而本篇講的SwipeListView則是這個(gè)列表的具體實(shí)現(xiàn)了。當(dāng)然啦,這個(gè)SwipeListView繼承自ListView,為了實(shí)現(xiàn)我們需要的功能,重點(diǎn)就是重寫(xiě)ListView的onTouchEvent()以及onInterceptTouchEvent()這個(gè)方法了。先說(shuō)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);
}
上面代碼,首先分析用戶滑開(kāi)一個(gè)item的操作,這個(gè)操作以ACTION_DOWN起始,一系列的ACTION_MOVE,以ACTION_UP作為結(jié)束,所以,在ACTION_DOWN事件里面,我們先記錄下最開(kāi)始的事件位置mActionDownX和mActionDownY;然后再ACTION_MOVE事件里面,我們先要進(jìn)行判斷,這個(gè)判斷分兩步,第一步,判斷這個(gè)ACTION_MOVE事件下,當(dāng)前事件的位置curX和curY在x軸上以及y軸上和ACTION_DOWN里面記錄的位置的距離是否已經(jīng)超過(guò)TOUCH_SLOP的值,這個(gè)值是android用來(lái)判斷是否應(yīng)該進(jìn)行一次滑動(dòng)的閾值,第二步,我們要進(jìn)一步判斷用戶是縱向滑動(dòng)這整個(gè)列表還是左右滑動(dòng)某個(gè)item,這里的邏輯判斷就簡(jiǎn)單點(diǎn)處理,若是超過(guò)TOUCH_SLOP閾值的情況下,x軸方向上距離大于y軸的,我們就認(rèn)為用戶是左右滑動(dòng)單個(gè)item,反之則是縱向滑動(dòng)整個(gè)列表,這里我們用三種狀態(tài)區(qū)分,DIRECTION_UNKNOW表示當(dāng)前的滑動(dòng)操作還沒(méi)有進(jìn)行判斷左右滑動(dòng)還是縱向滑動(dòng),DIRECTION_HORIZONTAL表示當(dāng)前滑動(dòng)操作判定為左右滑動(dòng),DIRECTION_VERTICAL表示判定為縱向滑動(dòng),一旦滑動(dòng)操作被判定了,則在ACTION_UP處理前,我們都認(rèn)為用戶是做同一方向的滑動(dòng);ACTION_UP事件里面,重置滑動(dòng)操作狀態(tài)為DIRECTION_UNKNOW以便下一次的判定,以及這次ACTION_UP事件處理的時(shí)候,如果當(dāng)前滑開(kāi)的item的位置mLastShowingPos不為-1,則表示當(dāng)前是一次滑開(kāi)的操作,這次仔細(xì)想想,用戶可能在并沒(méi)有完全滑開(kāi)這個(gè)item的狀態(tài)下手就離開(kāi)屏幕了,這時(shí)候我們就應(yīng)該要判斷此時(shí)這個(gè)被滑動(dòng)的item是應(yīng)該完全打開(kāi)又或者是關(guān)閉,這里的邏輯判斷是item已經(jīng)滑開(kāi)的距離超過(guò)它本身寬度的一半的話,就完全打開(kāi)它,否則就關(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();
}
}
}
}
那第一次的用戶滑動(dòng)操作的邏輯判定我們就算處理完了。接下來(lái)是第二次的,為什么說(shuō)第二次呢,第一次用戶滑開(kāi)了某單個(gè)的item,使其處于打開(kāi)的狀態(tài)下,再一次觸摸屏幕,這次我們則要再一次進(jìn)行判定,其一,如果ACTION_DOWN發(fā)生的位置在item滑開(kāi)顯示出來(lái)的button的范圍內(nèi),表示當(dāng)前用戶是點(diǎn)擊這個(gè)button,這樣我們就不做額外處理,直接交由列表默認(rèn)的邏輯處理;其二,如果ACTION_DOWN發(fā)生的位置不在item滑開(kāi)后顯示出來(lái)的button范圍內(nèi),怎表示當(dāng)前用戶只是操作列表的其他范圍,這里我們就關(guān)閉當(dāng)前打開(kāi)了的item,并取消后續(xù)的touch事件,這里的話,我們就要截獲這個(gè)ACTIOIN_DOWN事件了,需要重寫(xiě)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是用來(lái)在onTouchEvent()里面判斷是否需要取消后續(xù)touch事件的標(biāo)志,那期間,如何判斷當(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;
}
剩下的,就是如何打開(kāi)或者關(guān)閉一個(gè)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()方法就是上一篇博客里面我們實(shí)現(xiàn)了的移動(dòng)item并使其帶有動(dòng)畫(huà)效果的方法了。
這樣,整個(gè)仿微信滑動(dòng)刪除操作的總體實(shí)現(xiàn)方案就解釋完畢了,具體一些細(xì)節(jié)的話可以查看這個(gè)工程的源碼,源碼我已經(jīng)上傳到了我的Github主頁(yè)上:https://github.com/YoungLeeForeverBoy/SlidingListViewPlus。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android ImageView隨手勢(shì)變化動(dòng)態(tài)縮放圖片
這篇文章主要為大家詳細(xì)介紹了Android ImageView隨手勢(shì)變化動(dòng)態(tài)縮放圖片的相關(guān)資料,感興趣的小伙伴們可以參考一下2016-05-05
Android 自定義View的構(gòu)造函數(shù)詳細(xì)介紹
這篇文章主要介紹了Android 自定義View的構(gòu)造函數(shù)詳細(xì)介紹的相關(guān)資料,這里對(duì)構(gòu)造函數(shù)進(jìn)行了對(duì)比按需使用,需要的朋友可以參考下2016-12-12
Android webview轉(zhuǎn)PDF的方法示例
本篇文章主要介紹了Android webview轉(zhuǎn)PDF的方法示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-01-01
Android studio listview實(shí)現(xiàn)列表數(shù)據(jù)顯示 數(shù)據(jù)循環(huán)顯示效果
這篇文章主要介紹了Android studio listview實(shí)現(xiàn)列表數(shù)據(jù)顯示 數(shù)據(jù)循環(huán)顯示功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04
android 動(dòng)態(tài)控制狀態(tài)欄顯示和隱藏的方法實(shí)例
這篇文章主要介紹了2013-12-12
Android 打開(kāi)網(wǎng)絡(luò)上pdf文件
本文主要介紹了Android打開(kāi)網(wǎng)絡(luò)上pdf文件的方法,具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧2017-04-04
Android AlertDialog對(duì)話框用法示例
這篇文章主要介紹了Android AlertDialog對(duì)話框用法,結(jié)合實(shí)例形式分析了AlertDialog對(duì)話框的功能及常見(jiàn)使用技巧,需要的朋友可以參考下2016-06-06

