第一次寫簡書杠园,寫的不好或有錯誤的地方請多多包涵顾瞪,閑話不多說,先上兩張效果圖。
實現(xiàn)此種效果陈醒,大致有兩種方法惕橙,此效果主要是處理滑動事件沖突,第一是重寫RecyclerView孵延,重寫onInterceptTouchEvent和onTouchEvent吕漂。第二種方法時重寫RecyclerView的子View,主要也是重寫這兩個方法尘应,如果是繼承ViewGroup惶凝,那么要重寫onMeasure和onLayout,如果是繼承LinearLayout則只要重寫事件分發(fā)的幾個方法就好犬钢。這里采用的第二種方法苍鲜,重寫RecyclerView的子View。下面主要說說重寫的核心方法:
先看onInterceptTouchEvent方法:
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
boolean consume = false;
int x = (int) e.getX();
int y = (int) e.getY();
switch (e.getAction()){
case MotionEvent.ACTION_DOWN:
//獲取手指按下時的坐標
mDownX = (int) e.getX();
mDownY = (int) e.getY();
Log.d(TAG, "mDownX:"+ mDownX);
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "onInterceptTouchEvent:ACTION_MOVE");
int deltaX = x - mLastX;
int deltaY = y - mLastY;
if(Math.abs(deltaX) > mTouchSlop || Math.abs(deltaY) > mTouchSlop){
//當y方向滑動距離小于x方向時玷犹,攔截事件混滔,交給自己處理
if(Math.abs(deltaX) > Math.abs(deltaY)){
Log.d(TAG, "onInterceptTouchEvent");
//不允許父元素攔截事件
getParent().requestDisallowInterceptTouchEvent(true);
//此時攔截事件,直接調用onTouchEvent歹颓,傳入onTouchEvent的ACTION_MOVE事件中坯屿,不再走onTouchEvent的ACTION_DOWN事件
consume = true;
}else {
getParent().requestDisallowInterceptTouchEvent(false);
}
}
break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "ACTION_UP");
break;
}
mLastX = x;
mLastY = y;
return consume;
}
RecyclerView繼承的是ViewGroup,因此默認不攔截任何點擊事件巍扛,
//當我們重寫子View時领跛,必須在合適的時機調用此方法,此處是不允許父元素也就是RecyclerView攔截該事件撤奸,此方法時設置一個標記位吠昭,具體的可以看源碼或者具體了解一下事件分發(fā)機制。
getParent().requestDisallowInterceptTouchEvent(true);
onInterceptTouchEvent主要的邏輯是攔截事件胧瓜,具體一點矢棚,當我們在x軸滑動的距離大于y軸滑動的距離時,我們需要子View攔截事件府喳,也就是不允許父元素攔截事件蒲肋,即
//不允許父元素攔截事件
getParent().requestDisallowInterceptTouchEvent(true);
//此時攔截事件,直接調用onTouchEvent钝满,傳入onTouchEvent的ACTION_MOVE事件中肉津,不再走onTouchEvent的ACTION_DOWN事件
consume = true;
當我們完成具體的攔截邏輯后,具體的執(zhí)行邏輯在onTouchEvent中執(zhí)行舱沧,下面看看代碼:
@Override
public boolean onTouchEvent(MotionEvent e) {
switch (e.getAction()){
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
mMoveX = (int) e.getX();
mMoveY = (int) e.getY();
Log.d(TAG, "mMoveX - mDownX:"+ (mMoveX - mDownX));
scrollBy(-(mMoveX - mDownX),0);
int scrollX = getScrollX();
if (scrollX > mItemLength) {
scrollTo(mItemLength, 0);
isOpen = true;
}
if (scrollX < 0) {
scrollTo(0, 0);
isOpen = false;
}
break;
case MotionEvent.ACTION_UP:
//滑動超過一定距離時,自動關閉或開啟menu
int upX = (int) getX();
if(Math.abs(getScrollX())>= mMenuWidth && getScrollX() > 0 || Math.abs(getScrollX())< mMenuWidth && getScrollX() < 0){
open();
}else if(Math.abs(getScrollX())>= mMenuWidth && getScrollX() < 0 || Math.abs(getScrollX())< mMenuWidth && getScrollX() > 0){
close();
}
break;
}
mDownX = mMoveX;
mDownY = mMoveY;
return true;
}
具體的執(zhí)行落在onTouchEvent的ACTION_MOVE中執(zhí)行偶洋,這里的代碼也不復雜熟吏,最后在ACTION_UP時判斷,當滑動超過一定距離時,松開手指可通過Scroller實現(xiàn)平緩滑動的效果牵寺。
最后為了實現(xiàn)更好的效果悍引,重寫了RecyclerView的onInterceptTouchEvent方法:
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
switch (e.getAction()){
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "onInterceptTouchEvent: down");
int childCount = getChildCount();
for(int i=0;i<childCount;i++){
SlidingView slidingView = (SlidingView) getChildAt(i);
if(slidingView.isOpen){
slidingView.close();
}
}
break;
}
return super.onInterceptTouchEvent(e);
}
具體的效果是,當手指觸發(fā)ACTION_DOWN事件時帽氓,如果有未關閉的子View趣斤,那么關閉它。
最后一點要說明的是黎休,在RecyclerView的Adapter中浓领,對于Menu的點擊事件采用的是onTouchListener:
((ViewHolder)holder).tvEdit.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
int position = holder.getAdapterPosition();
Log.d(TAG, "onTouch: edit:"+position);
if(onClickListenerEditOrDelete != null){
onClickListenerEditOrDelete.OnClickListenerEdit(position);
}
return true;
}
return true;
}
});
因為OnTouchListener的優(yōu)先級高于OnClickListener,當使用OnClickListener時势腮,有時能觸發(fā)點擊事件有時不能联贩,如有同學知道所以然還想好好請教一番。
具體的實現(xiàn)邏輯就是這樣捎拯,重點是處理各種事件的滑動沖突泪幌,帶一點自定義View的基本使用,Scroller和View的滑動機制署照。demo還有許多不足的地方祸泪,還需要多多優(yōu)化。