在 iOS 的設置裏面,有一種編輯的效果,進入編輯狀態後,列表左邊推出圓形的刪除按鈕,點擊後再出現右邊確認刪除按鈕,至關於給用戶二次確認。看下在 Android 上如何實現。java
iOS 的效果以下:git
我實現的效果是這樣的:github
下面說說我是怎麼作的吧。ide
咱們自定義了一個 EditLayout 繼承 FrameLayout。
能夠看出,這個控件由左中右三部分組成,對應的,我在 EditLsyout 裏建立了如下成員變量:佈局
private View mContentView; //內容部分
private View mLeftView; //左邊圓形刪除按鍵
private View mRightView; //右邊刪除按鍵
private int mWidth; //內容部分寬度
private int mHeight; //內容部分高度
private int mLeftWidth; //左邊部分寬度
private int mRightWidth; //右邊部分寬度複製代碼
當 View 中全部的子控件 均被映射成 xml 後,會觸發 onFinishInflate 方法,當 view 的大小發生變化時,會觸發 onSizeChanged 方法,因此咱們能夠這樣賦值:post
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mLeftView = getChildAt(0);
mContentView = getChildAt(1);
mRightView = getChildAt(2);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
mRightWidth = mRightView.getMeasuredWidth();
mLeftWidth = mLeftView.getMeasuredWidth();
}複製代碼
獲取到控件和寬高,咱們就能夠擺放它們的位置了。咱們知道,View 是經過 onLayout 方法來擺放控件位置的。這裏有兩種擺放方式,編輯狀態和非編輯狀態,代碼以下:this
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
//判斷是否爲編輯模式,擺放每一個子View的位置
if (EditAdapter.isEdit) {
mContentView.layout(mLeftWidth, 0, mLeftWidth + mWidth, mHeight);
mRightView.layout(mWidth + mLeftWidth, 0, mRightWidth + mWidth + LeftWidth, mHeight);
mLeftView.layout(0, 0, mLeftWidth, mHeight);
} else {
mContentView.layout(0, 0, mWidth, mHeight);
mRightView.layout(mWidth, 0, mRightWidth + mWidth, mHeight);
mLeftView.layout(-mLeftWidth, 0, 0, mHeight);
}
}複製代碼
滑動效果,我交給了 ViewDragHelper 處理。要使用 ViewDragHelper ,須要實現一個 ViewDragHelper.Callback,這是一個抽象類,咱們這裏只關注它的三個方法:spa
//返回值決定 child 是否可拖拽
public boolean tryCaptureView(View child, int pointerId) //限定移動範圍,返回值爲對應控件的左邊位置 public int clampViewPositionHorizontal(View child, int left, int dx) //當 changedView 發生移動時的回調(能夠用來更新其餘子 View 的位置) public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy)複製代碼
我實現的 Callback 代碼以下:3d
ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
return false;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
if (child == mContentView) {
if (left < -mRightWidth) {
left = -mRightWidth;
} else if (left > mLeftWidth) {
left = mLeftWidth;
}
} else if (child == mRightView) {
if (left < mWidth - mRightWidth) {
left = mWidth - mRightWidth;
} else if (left > mWidth) {
left = mWidth;
}
} else if (child == mLeftView) {
if (left < mWidth - mRightWidth) {
left = mWidth - mRightWidth;
} else if (left > -mLeftWidth) {
left = 0 - mLeftWidth;
}
}
return left;
}
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
if (changedView == mContentView) {
mRightView.offsetLeftAndRight(dx);
mLeftView.offsetLeftAndRight(dx);
} else if (changedView == mRightView) {
mContentView.offsetLeftAndRight(dx);
mLeftView.offsetLeftAndRight(dx);
} else if (changedView == mLeftView) {
mContentView.offsetLeftAndRight(dx);
mRightView.offsetLeftAndRight(dx);
}
invalidate();
}
};
mDragHelper = ViewDragHelper.create(this, mCallback);複製代碼
對了,實現滑動還須要重寫 computeScroll 方法:code
@Override
public void computeScroll() {
super.computeScroll();
if (mDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}複製代碼
咱們這個控件存在三種狀態,分別是左邊展開,右邊展開,還有關閉。相應的,咱們定義三個方法,用於滑動到不一樣的狀態:
/** * 展開左側 */
public void openLeft() {
if (mOnStateChangeListener != null) {
mOnStateChangeListener.onLeftOpen(this);
}
mDragHelper.smoothSlideViewTo(mContentView, mLeftWidth, 0);
invalidate();
}
/** * 展開右側 */
public void openRight() {
if (mOnStateChangeListener != null) {
mOnStateChangeListener.onRightOpen(this);
}
mDragHelper.smoothSlideViewTo(mContentView, -mRightWidth, 0);
invalidate();
}
/** * 關閉 */
public void close() {
if (mOnStateChangeListener != null) {
mOnStateChangeListener.onClose(this);
}
mDragHelper.smoothSlideViewTo(mContentView, 0, 0);
invalidate();
}複製代碼
mOnStateChangeListener 是一個監聽器,會在 EditLayout 狀態改變的時候調用。我在回調方法裏保存了當前向右展開的 EditLayout。
到這裏,EditLayout 就完成了。
接下來看下適配器 EditAdapter。
item 的 xml 文件裏面,最外層用咱們的 EditLayout 包裹,而後裏面的三個子佈局,按順序,對應咱們左中右三個部分。
這裏須要定義一個 EditLayout 的集合 allItems,在 onBindViewHolder 的時候將佈局添加進去。
而後咱們定義兩個公開方法,用於切換全部 item 的狀態,在切換編輯模式的時候調用:
/** * 關閉全部 item */
public void closeAll() {
for (EditLayout layout : allItems) {
editLayout.close();
}
}
/** * 將全部 item 向左展開 */
public void openLeftAll() {
for (EditLayout layout : allItems) {
editLayout.openLeft();
}
}複製代碼
當列表有某一項是右邊展開了,我但願在滑動列表的時候能將它關閉,變回向左展開的狀態,因此我自定義了一個 RecyclerView。
能夠重寫了 onTouchEvent 方法,實現上面說的效果:
@Override
public boolean onTouchEvent(MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
if (getAdapter() instanceof EditAdapter) {
rightOpenItem = ((EditAdapter) getAdapter()).getRightOpenItem();
}
if (EditAdapter.isEdit && rightOpenItem != null) {
rightOpenItem.openLeft();
}
}
return super.onTouchEvent(e);
}複製代碼
當滑動列表的時候,先判斷是否有向右展開項,有的話就將它變回向左展開。
這樣就完成啦,妥妥的。