項(xiàng)目中用到RecycleView左滑刪除ui及功能效果善镰。自己寫了一個(gè),感覺很簡(jiǎn)單易懂的,現(xiàn)分享一下嫂拴。
效果圖
ViewDragHelper是一個(gè)方便移動(dòng)子控件的輔助類束凑,特別方便。
初始化ViewDragHelper
public DragLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mDragHelper = ViewDragHelper.create(this, 1.0f, new DragHelperCallback());
}
要想達(dá)到子View滑動(dòng)效果富稻,記得將觸摸事件交給ViewDragHelper處理
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = MotionEventCompat.getActionMasked(ev);
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
mDragHelper.cancel();
return false;
}
return mDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
mDragHelper.processTouchEvent(ev);
return true;
}
要想實(shí)現(xiàn)滑動(dòng)刪除效果掷邦,考慮RecylceView適配器里面的item是個(gè)FrameLayout,滑動(dòng)之前的View顯示在最上面,下面是刪除按鈕椭赋,被上面view遮住了抚岗,所以你看不到。當(dāng)上面view向左移動(dòng)時(shí)就會(huì)將刪除按鈕顯露出來了哪怔,于是實(shí)現(xiàn)了滑動(dòng)刪除效果宣蔚。可以限定移動(dòng)范圍剛好是下面刪除按鈕的寬度认境,這樣上面的view就不會(huì)移動(dòng)出邊界胚委。
指定誰(shuí)可以滑動(dòng)
@Override
public boolean tryCaptureView(View child, int pointerId) {
return child == dragView;//出入你想要誰(shuí)可移動(dòng)
}
最重要的邏輯就是限定移動(dòng)左邊界的范圍。
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
mLeft = 0;
if (left < 0 && Math.abs(left) > maxLeftPointReverse) { //限定向左最大到達(dá)位置
mLeft = -maxLeftPointReverse;
return mLeft;
}
if (left > mInitX) { //設(shè)置向右最大的left置
mLeft = mInitX;
return mLeft;
}
mLeft = left;
return mLeft;
}
該方法返回的是移動(dòng)view的左邊位置叉信。
注意2個(gè)邊界值亩冬,注釋都寫清楚了。
學(xué)習(xí)兩個(gè)方法:
viewDragHelper.settleCapturedViewAt(mInitX, mInitTop);
作用像方法名字一樣硼身,該方法只能在onViewReleased方法里面調(diào)用硅急,否則會(huì)報(bào)錯(cuò)。該方法會(huì)將view平滑滑動(dòng)到指定位置佳遂。
我們的效果是點(diǎn)擊刪除按鈕营袜,如果該項(xiàng)不可刪除,則回到初始狀態(tài)讶迁,刪除按鈕隱藏连茧。這時(shí)用
viewDragHelper.smoothSlideViewTo(dragView, mInitX, mInitTop);
將view滑動(dòng)回去核蘸。
另外,在RecycleView上下滑動(dòng)時(shí)啸驯,已處于打開狀態(tài)的item會(huì)自動(dòng)回到關(guān)閉狀態(tài)客扎,刪除按鈕被遮住了。要想解決這個(gè)問題罚斗,我是在adapter里面給item設(shè)置tag的方式徙鱼,當(dāng)打開時(shí)tag為數(shù)據(jù)源,關(guān)閉時(shí)tag為空针姿,當(dāng)判斷為打開過的item時(shí)再將其smoothSlideViewTo滑動(dòng)到打開狀態(tài)袱吆,否則滑動(dòng)到初始狀態(tài)。后來發(fā)現(xiàn)這樣做依然不行距淫,因?yàn)閞ecycleView的回收機(jī)制绞绒,導(dǎo)致滑動(dòng)時(shí)onBindViewHolder走不到,所以即使這樣設(shè)置了也沒有用榕暇,后來找到解決方案蓬衡,需要設(shè)置recycleView.setItemViewCacheSize(0)才能保證onBindViewHolder走得到,這里要注意一下
if (orderTravelViewHolder.swipeLayout.getTag()!=null&&((OrderList_Bean)orderTravelViewHolder.swipeLayout.getTag()).equals(orderInfo)){
orderTravelViewHolder.swipeLayout.toOpen();
}else{
orderTravelViewHolder.swipeLayout.toNormal();
}
另外item的水平滑動(dòng)事件可能會(huì)引起RecycleView的滑動(dòng)事件彤枢,所以也要處理一下狰晚。
如下:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = ev.getRawX();
downY = ev.getRawY();
break;
case MotionEvent.ACTION_MOVE:
float disX = ev.getRawX() - downX;
float disY = ev.getRawY() - downY;
ViewParent viewGroup = getParent();
if (Math.abs(disX) > Math.abs(disY)) {
viewGroup.requestDisallowInterceptTouchEvent(true);//水平滑動(dòng)時(shí)請(qǐng)求reycleView不要攔截事件
} else {
viewGroup.requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
viewDragHelper.cancel();
break;
}
if (canSwipe) {
return viewDragHelper.shouldInterceptTouchEvent(ev);
} else {
return super.onInterceptTouchEvent(ev);
}
}
完整代碼如此
public class SwipeLayout extends FrameLayout {
ViewDragHelper viewDragHelper;
ViewDragHelper.Callback callback;
View dragView;
TextView hideView; //隱藏在下面的view
int maxLeftPointReverse; //左邊所能到達(dá)的最左邊的距離的絕對(duì)值
boolean canSwipe = true; //是否可以滑動(dòng)刪除
int mHorizontalDragRange; //水平可以拖動(dòng)的距離最大范圍
Function<Void, Void> animOpenFunction;//打開后的回調(diào)
Function<Void, Void> animCloseFunction;//關(guān)閉后的回調(diào)
float downX = 0, downY = 0;
private int mInitX;
private int mLeft;
private int mInitTop;
public SwipeLayout(@NonNull Context context) {
super(context);
init(context);
}
public SwipeLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public SwipeLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
public void setCanSwipe(boolean canSwipe) {
this.canSwipe = canSwipe;
}
public void setAnimCloseFunction(Function<Void, Void> animCloseFunction) {
this.animCloseFunction = animCloseFunction;
}
public void setAnimOpenFunction(Function<Void, Void> animOpenFunction) {
this.animOpenFunction = animOpenFunction;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
if (getChildCount() == 2) {
hideView = (TextView) getChildAt(0);
dragView = getChildAt(1);
hideView.post(new Runnable() {
@Override
public void run() {
mHorizontalDragRange = hideView.getWidth();
}
});
dragView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
mInitX = (int) dragView.getX();
mInitTop = dragView.getTop();
maxLeftPointReverse = hideView.getWidth() - mInitX;
dragView.removeOnLayoutChangeListener(this);
}
});
}
}
public void swipeToNormal() {
dragView.post(new Runnable() {
@Override
public void run() {
if (dragView.getX() != mInitX) {
boolean result = viewDragHelper.smoothSlideViewTo(dragView, mInitX, mInitTop);
LogUtil.d("-->result is " + result);
ViewCompat.postInvalidateOnAnimation(SwipeLayout.this);
}
}
});
}
public void toOpen() {
dragView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (dragView.getX() != -maxLeftPointReverse) {
viewDragHelper.smoothSlideViewTo(dragView, -maxLeftPointReverse, mInitTop);
}
dragView.removeOnLayoutChangeListener(this);
}
});
}
public void toNormal() {
dragView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (dragView.getX() != mInitX) {
viewDragHelper.smoothSlideViewTo(dragView, mInitX, mInitTop);
}
dragView.removeOnLayoutChangeListener(this);
}
});
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (canSwipe) {
viewDragHelper.processTouchEvent(ev);
return true;
} else {
return super.onTouchEvent(ev);
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = ev.getRawX();
downY = ev.getRawY();
break;
case MotionEvent.ACTION_MOVE:
float disX = ev.getRawX() - downX;
float disY = ev.getRawY() - downY;
ViewParent viewGroup = getParent();
if (Math.abs(disX) > Math.abs(disY)) {
viewGroup.requestDisallowInterceptTouchEvent(true);//水平滑動(dòng)時(shí)請(qǐng)求reycleView不要攔截事件
} else {
viewGroup.requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
viewDragHelper.cancel();
break;
}
if (canSwipe) {
return viewDragHelper.shouldInterceptTouchEvent(ev);
} else {
return super.onInterceptTouchEvent(ev);
}
}
private void init(Context context) {
callback = new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
return child == dragView;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
mLeft = 0;
if (left < 0 && Math.abs(left) > maxLeftPointReverse) { //限定向左最大到達(dá)位置
mLeft = -maxLeftPointReverse;
return mLeft;
}
if (left > mInitX) { //設(shè)置向右最大位置
mLeft = mInitX;
return mLeft;
}
mLeft = left;
return mLeft;
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
LogUtil.d("-->top is " + top);
return mInitTop;
}
@Override
public void onEdgeTouched(int edgeFlags, int pointerId) {
super.onEdgeTouched(edgeFlags, pointerId);
}
@Override
public int getViewHorizontalDragRange(View child) {
return mHorizontalDragRange;
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
if (Math.abs(mInitX - mLeft) >= mHorizontalDragRange / 3) {//open
if (viewDragHelper.settleCapturedViewAt(-maxLeftPointReverse, mInitTop)) {
ViewCompat.postInvalidateOnAnimation(SwipeLayout.this);
}
if (animOpenFunction != null) {
try {
animOpenFunction.apply(null);
} catch (Exception e) {
e.printStackTrace();
}
}
} else {//close
if (viewDragHelper.settleCapturedViewAt(mInitX, mInitTop)) {
ViewCompat.postInvalidateOnAnimation(SwipeLayout.this);
}
if (animCloseFunction != null) {
try {
animCloseFunction.apply(null);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
};
viewDragHelper = ViewDragHelper.create(this, 1.0f, callback);
}
@Override
public void computeScroll() {
if (viewDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
}
特別鳴謝:
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/0911/1680.html
https://www.cnblogs.com/epilogue/p/7723482.html
如果你覺得對(duì)你有一丁點(diǎn)幫助,歡迎給點(diǎn)個(gè)愛心缴啡,謝謝壁晒!