編輯:關於Android編程
接上一篇,本篇主要講如何實現滑動列表SwipeListView。
上篇完成了滑動控件SwipeItemView,這個控件是一個自定義的ViewGroup,作為列表的一個item,為列表提供一些方法讓這個SwipeItemView能滑動其視圖內容,同時滑動過程中會有順滑的動畫效果。而本篇講的SwipeListView則是這個列表的具體實現了。當然啦,這個SwipeListView繼承自ListView,為了實現我們需要的功能,重點就是重寫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作為結束,所以,在ACTION_DOWN事件裡面,我們先記錄下最開始的事件位置mActionDownX和mActionDownY;然後再ACTION_MOVE事件裡面,我們先要進行判斷,這個判斷分兩步,第一步,判斷這個ACTION_MOVE事件下,當前事件的位置curX和curY在x軸上以及y軸上和ACTION_DOWN裡面記錄的位置的距離是否已經超過TOUCH_SLOP的值,這個值是android用來判斷是否應該進行一次滑動的阈值,第二步,我們要進一步判斷用戶是縱向滑動這整個列表還是左右滑動某個item,這裡的邏輯判斷就簡單點處理,若是超過TOUCH_SLOP阈值的情況下,x軸方向上距離大於y軸的,我們就認為用戶是左右滑動單個item,反之則是縱向滑動整個列表,這裡我們用三種狀態區分,DIRECTION_UNKNOW表示當前的滑動操作還沒有進行判斷左右滑動還是縱向滑動,DIRECTION_HORIZONTAL表示當前滑動操作判定為左右滑動,DIRECTION_VERTICAL表示判定為縱向滑動,一旦滑動操作被判定了,則在ACTION_UP處理前,我們都認為用戶是做同一方向的滑動;ACTION_UP事件裡面,重置滑動操作狀態為DIRECTION_UNKNOW以便下一次的判定,以及這次ACTION_UP事件處理的時候,如果當前滑開的item的位置mLastShowingPos不為-1,則表示當前是一次滑開的操作,這次仔細想想,用戶可能在並沒有完全滑開這個item的狀態下手就離開屏幕了,這時候我們就應該要判斷此時這個被滑動的item是應該完全打開又或者是關閉,這裡的邏輯判斷是item已經滑開的距離超過它本身寬度的一半的話,就完全打開它,否則就關閉它,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,使其處於打開的狀態下,再一次觸摸屏幕,這次我們則要再一次進行判定,其一,如果ACTION_DOWN發生的位置在item滑開顯示出來的button的范圍內,表示當前用戶是點擊這個button,這樣我們就不做額外處理,直接交由列表默認的邏輯處理;其二,如果ACTION_DOWN發生的位置不在item滑開後顯示出來的button范圍內,怎表示當前用戶只是操作列表的其他范圍,這裡我們就關閉當前打開了的item,並取消後續的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()裡面判斷是否需要取消後續touch事件的標志,那期間,如何判斷當前的ACTION_DOWN事件是否發生在button的范圍內呢,使用如下代碼:
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;
}
剩下的,就是如何打開或者關閉一個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()方法就是上一篇博客裡面我們實現了的移動item並使其帶有動畫效果的方法了。
這樣,整個仿微信滑動刪除操作的總體實現方案就解釋完畢了,具體一些細節的話可以查看這個工程的源碼,源碼我已經上傳到了我的Github主頁上:https://github.com/YoungLeeForeverBoy/SlidingListViewPlus。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持本站。
Android群英傳-拼圖游戲puzzle-6點吐槽
一、緣由經常寫文章,混了一些C幣。最近在深入學習Android應用開發,就從商城裡買了一本《Android群英傳》。這本書的內容,不是純粹的入門那種,分幾個章節,重點講解
ListView的私人訂制
現在開發中Android RecyclerView可能用的比較多,不過ListView作為常用控件學習它的使用和擴展也是十分重要的。簡單封裝了一個下拉刷新和上拉加載的Li
Android熱修復——Tinker微信解決方案
Android的熱修復前言:隨著時代的發展,由於公司的項目需要去求變化平凡計劃總趕不上變化,H5的高靈活性,開發周期短,更新速度快H5以及一些混合開發越來越被看好,然而主
Android編程解析XML方法詳解(SAX,DOM與PULL)
本文實例講述了Android編程解析XML方法。分享給大家供大家參考,具體如下:XML在各種開發中都廣泛應用,Android也不例外。作為承載數據的一個重要角色,如何讀寫