Android滑動刪除控件
效果展示
代碼實現
靜態佈局
自定義一個ViewGroup,繼承至FrameLayout,覆寫其中的幾個關鍵方法,用於給其中的兩個子view設置佈局位置。
private View contentView, deleteView;
int contentViewHeight, contentViewWidth;
int deleteViewHeight, deleteViewWidth;
private void init() {
}
/**
* 從xml中加載完佈局,只知道有幾個子view,並沒有進行測量
* 一般可以初始化子view的引用
*/
@Override
protected void onFinishInflate() {
super.onFinishInflate();
contentView = getChildAt(0);
deleteView = getChildAt(1);
}
/**
* 測量完子view後調用,在這裏可以直接獲取子view的高度
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
contentViewWidth = contentView.getMeasuredWidth();
deleteViewHeight = deleteView.getMeasuredHeight();
}
/**
* 放置子view到合適的位置
*/
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
contentView.layout(0, 0, contentViewWidth, contentViewHeight);
deleteView.layout(contentViewWidth, 0, contentViewWidth + deleteViewWidth, deleteViewHeight);
}
處理滑動邏輯
利用ViewDragHelper類封裝了對觸摸位置、速度、距離的檢測,以及Scroller.
需要我們制定什麼時候滑動,以及滑動多少。
需要把ViewGroup中受到的觸摸事件傳給ViewDragHelper實例。
觸摸事件傳給ViewDragHelper實例
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mViewDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mViewDragHelper.processTouchEvent(event);
//消費掉此觸摸事件,不向上返回
return true;
}
在ViewDragHelper的回調函數中處理滑動邏輯。
private ViewDragHelper mViewDragHelper;
private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
/**
* @return 返回true表示獲得view的控制權
*/
@Override
public boolean tryCaptureView(View view, int i) {
return view == contentView || view == deleteView;
}
@Override
public void onViewCaptured(View capturedChild, int activePointerId) {
super.onViewCaptured(capturedChild, activePointerId);
}
/**
* 控制view在水平方向上實際滑動了多少
* @param child 當前觸摸的view
* @param left view的左邊座標,負數表示view的左邊超出父view邊界的長度
* @param dx
* @return 返回多少,代表想讓child的left=多少
*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
Log.i(TAG, "clampViewPositionHorizontal-->" + "left=" + left);
if (child == contentView) {
if (left > 0) left = 0;
if (left < -deleteViewWidth) left = -deleteViewWidth;
} else if (child == deleteView) {
//使deleteVie不會超出指定的邊界
if (left < contentViewWidth - deleteViewWidth) {
left = contentViewWidth - deleteViewWidth;
}
}
return left;
}
/**
* 水平方向拖拽的範圍
*/
@Override
public int getViewHorizontalDragRange(View child) {
return super.getViewHorizontalDragRange(child);
}
/**
* view滑動後的回調
* @param changedView
* @param left
* @param top
* @param dx x軸方向的改編值
* @param dy
*/
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
Log.i(TAG, "onViewPositionChanged-->" + "dx=" + dx);
//重新佈局子view的位置
if (changedView == contentView) {
deleteView.layout(deleteView.getLeft() + dx, 0, deleteView.getRight() + dx, deleteView.getBottom());
} else if (changedView == deleteView) {
contentView.layout(contentView.getLeft() + dx, 0, contentView.getRight() + dx, contentView.getBottom());
}
}
/**
* TouchUp的回調
* @param releasedChild
* @param xvel
* @param yvel
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
if (contentView.getLeft() < -deleteViewWidth / 2) { //滑動條打開狀態
mViewDragHelper.smoothSlideViewTo(contentView, -deleteViewWidth, 0);
} else { //滑動條關閉狀態
mViewDragHelper.smoothSlideViewTo(contentView, 0, 0);
}
//
ViewCompat.postInvalidateOnAnimation(SwipeView.this); //動畫刷新
}
};
動畫效果
Scroller幫助計算好view在某個時間點會處於某個位置,達到動畫的效果
@Override
public void computeScroll() {
super.computeScroll();
if(mViewDragHelper.continueSettling(true)){ //內部有Scroller計算位置和移動
ViewCompat.postInvalidateOnAnimation(SwipeView.this); //刷新當前view
}
}