仿微信小程序和QQ頭部下拉展開更多佈局項目,主要參考來源於Github項目PullLoadXiaochengxu。
本項目改造主要是優化中間內容佈局是ListView、RecyclerView或ScrollView等可滾動view時與頭部或底部滑動存在的滑動衝突問題,以及參考QQ,處理頭部收起以及底部收起時的回彈效果。
效果演示如下:
關鍵點是確定事件攔截的臨界點,處理好事件分發,本人已在代碼中添加了詳細的註解說明,有興趣的同行可以Review查看,共同學習,有興趣的同行可以star學習一下,項目GitHub地址如下:
PullLoadMoreCopyWeixinAndQQ
事件攔截與消耗代碼處理,具體流程可以查看代碼中的Log說明,完整代碼可以下載demo查看
@Override
public final boolean onInterceptTouchEvent(MotionEvent event) {
Log.d(TAG, "onInterceptTouchEvent");
if (!isInterceptTouchEventEnabled()) {
return false;
}
if (!isPullLoadEnabled() && !isPullRefreshEnabled()) {
return false;
}
final int action = event.getAction();
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
mIsHandledTouchEvent = false;
return false;
}
if (action != MotionEvent.ACTION_DOWN && mIsHandledTouchEvent) {
return true;
}
switch (action) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "onInterceptTouchEvent,MotionEvent.ACTION_DOWN");
mLastMotionX = event.getX();
mLastMotionY = event.getY();
mIsHandledTouchEvent = false;
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "onInterceptTouchEvent,MotionEvent.ACTION_MOVE");
final float deltaX = event.getX() - mLastMotionX;
final float deltaY = event.getY() - mLastMotionY;
final float absDiff = Math.abs(deltaY);
// 位移差大於mTouchSlop,這是爲了防止快速拖動引發刷新
// if ((absDiff > mTouchSlop)) {
mLastMotionX = event.getX();
mLastMotionY = event.getY();
// 第一步,先處理處理是否處於橫滑狀態
if (Math.abs(deltaX) >= Math.abs(deltaY)) {
Log.e(TAG, "onInterceptTouchEvent,MotionEvent.ACTION_MOVE,當前處於橫滑狀態,不攔截,交由父類去處理");
return false;
}
mPullDown = deltaY > 0;
if (mPullDown) {
Log.d(TAG, "當前處於下拉操作");
//下拉需要考慮如下情況:
// 1、頭部處理
// 1)刷新View已到達頂部,判斷是否需要
if (isPullRefreshEnabled() && checkIsContentViewScrollToTop((int) event.getX(), (int) event.getY())) {
Log.d(TAG, "當前處於下拉操作,且內容view已到達頂部,攔截,自己消耗處理,展開頭部");
return true;
} else if (isPullLoadEnabled() && mFooterLayout.getState() == IExtendLayout.State.arrivedListHeight && checkIsContentViewScrollToBottom((int) event.getX(), (int) event.getY())) {
Log.d(TAG, "當前處於下拉操作,且內容view已底部且已是展開狀態,攔截,自己消耗處理,收縮底部");
return true;
} else {
Log.d(TAG, "當前處於下拉操作,且內容view沒有到達頂部/到達底部,不攔截,給子view進行處理,滾動列表");
return false;
}
} else {
Log.d(TAG, "當前處於上拉操作");
if (isPullRefreshEnabled() && mHeaderLayout.getState() == IExtendLayout.State.arrivedListHeight && checkIsContentViewScrollToTop((int) event.getX(), (int) event.getY())) {
Log.d(TAG, "當前處於上拉操作,頭部已完全展開,且內容view已到達頂部,攔截,自己消耗處理,用於頭部收縮");
return true;
} else if (isPullLoadEnabled() && checkIsContentViewScrollToBottom((int) event.getX(), (int) event.getY())) {
Log.d(TAG, "當前處於上拉操作,內容view已到達底部,攔截,自己消耗處理,用於底部展開");
return true;
} else {
Log.d(TAG, "當前處於上拉操作,且內容view沒有到達頂部/到達底部,不攔截,給子view進行處理,滾動列表");
return false;
}
}
// }else{
// Log.d(TAG,"absDiff <= mTouchSlop,認爲不滾動,不處理");
// }
case MotionEvent.ACTION_UP:
Log.d(TAG, "onInterceptTouchEvent,MotionEvent.ACTION_UP");
break;
default:
break;
}
return super.onInterceptTouchEvent(event);
}
@Override
public final boolean onTouchEvent(MotionEvent ev) {
boolean handled = false;
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "onTouchEvent,MotionEvent.ACTION_DOWN");
mLastMotionY = ev.getY();
mIsHandledTouchEvent = false;
break;
case MotionEvent.ACTION_MOVE:
final float deltaY = ev.getY() - mLastMotionY;
mLastMotionY = ev.getY();
mPullDown = deltaY > 0;
Log.d(TAG, "onTouchEvent,MotionEvent.ACTION_MOVE,deltaY = " + deltaY);
if (isPullRefreshEnabled() && isReadyForPullDown(deltaY)) {
// 處理頭部滑動
Log.d(TAG, "onTouchEvent,MotionEvent.ACTION_MOVE,處理頭部滑動");
pullHeaderLayout(deltaY / mOffsetRadio);
handled = true;
if (null != mFooterLayout && 0 != mFooterHeight) {
mFooterLayout.setState(IExtendLayout.State.RESET);
}
} else if (isPullLoadEnabled() && isReadyForPullUp(deltaY)) {
Log.d(TAG, "onTouchEvent,MotionEvent.ACTION_MOVE,處理底部滑動");
// 上拉
pullFooterLayout(deltaY / mOffsetRadio);
handled = true;
if (null != mHeaderLayout && 0 != mHeaderHeight) {
mHeaderLayout.setState(IExtendLayout.State.RESET);
}
} else {
mIsHandledTouchEvent = false;
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
Log.d(TAG, "onTouchEvent,MotionEvent.ACTION_UP");
// if (mIsHandledTouchEvent) {
// mIsHandledTouchEvent = false;
// 當第一個顯示出來時
if (isReadyForPullDown(0)) {
Log.d(TAG, "onTouchEvent,MotionEvent.ACTION_UP,resetHeaderLayout");
if (mPullDown) {
// 彈性展開頭部
resetHeaderLayout();
} else {
// 往上拉時,收縮頭部,不彈性會彈
collapseHeaderLayout();
}
} else if (isReadyForPullUp(0)) {
Log.d(TAG, "onTouchEvent,MotionEvent.ACTION_UP,resetFooterLayout");
if (mPullDown) {
collapseFooterLayout();
} else {
resetFooterLayout();
}
}
// }
break;
default:
break;
}
return handled;
}