項目中用到RecycleView左滑刪除ui及功能效果。自己寫了一個,感覺很簡單易懂的,現(xiàn)分享一下。
效果圖
ViewDragHelper是一個方便移動子控件的輔助類,特別方便。
初始化ViewDragHelper
public DragLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mDragHelper = ViewDragHelper.create(this, 1.0f, new DragHelperCallback());
}
要想達到子View滑動效果,記得將觸摸事件交給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;
}
要想實現(xiàn)滑動刪除效果,考慮RecylceView適配器里面的item是個FrameLayout,滑動之前的View顯示在最上面,下面是刪除按鈕,被上面view遮住了,所以你看不到。當上面view向左移動時就會將刪除按鈕顯露出來了,于是實現(xiàn)了滑動刪除效果。可以限定移動范圍剛好是下面刪除按鈕的寬度,這樣上面的view就不會移動出邊界。
指定誰可以滑動
@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) { //限定向左最大到達位置
mLeft = -maxLeftPointReverse;
return mLeft;
}
if (left > mInitX) { //設(shè)置向右最大的left置
mLeft = mInitX;
return mLeft;
}
mLeft = left;
return mLeft;
}
該方法返回的是移動view的左邊位置。
注意2個邊界值,注釋都寫清楚了。
學習兩個方法:
viewDragHelper.settleCapturedViewAt(mInitX, mInitTop);
作用像方法名字一樣,該方法只能在onViewReleased方法里面調(diào)用,否則會報錯。該方法會將view平滑滑動到指定位置。
我們的效果是點擊刪除按鈕,如果該項不可刪除,則回到初始狀態(tài),刪除按鈕隱藏。這時用
viewDragHelper.smoothSlideViewTo(dragView, mInitX, mInitTop);
將view滑動回去。
另外,在RecycleView上下滑動時,已處于打開狀態(tài)的item會自動回到關(guān)閉狀態(tài),刪除按鈕被遮住了。要想解決這個問題,我是在adapter里面給item設(shè)置tag的方式,當打開時tag為數(shù)據(jù)源,關(guān)閉時tag為空,當判斷為打開過的item時再將其smoothSlideViewTo滑動到打開狀態(tài),否則滑動到初始狀態(tài)。后來發(fā)現(xiàn)這樣做依然不行,因為recycleView的回收機制,導(dǎo)致滑動時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的水平滑動事件可能會引起RecycleView的滑動事件,所以也要處理一下。
如下:
@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);//水平滑動時請求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; //左邊所能到達的最左邊的距離的絕對值
boolean canSwipe = true; //是否可以滑動刪除
int mHorizontalDragRange; //水平可以拖動的距離最大范圍
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);//水平滑動時請求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) { //限定向左最大到達位置
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
如果你覺得對你有一丁點幫助,歡迎給點個愛心,謝謝!