左滑操作(刪除,置頂等。。。)
阿新 • • 發佈:2018-11-06
- 在滑動列表中,常常會有左滑出現刪除,置頂操作的需求,如下qq的左滑效果:
今天也來實現下類似的效果,可供大家參考:
1.實現原理
- 原理圖
- 原理圖
2.實現自定義左滑View
- 1.新增View
首先在自定義View中通用getChildAt來獲取左邊顯示內容的View和右邊的操作View,這裡通過getChildAt可以更方便的定製按鈕的個數,大小。
- 1.新增View
// 左邊顯示內容的View
private View leftContentView;
// 右邊操作的View
private View rightActionView;
@Override
protected void onFinishInflate() {
super.onFinishInflate();
if (getChildCount() < 2) {
throw new IllegalArgumentException("child view less than 2!!");
}
leftContentView = getChildAt(0);
rightActionView = getChildAt(1);
}
- 2.測量佈局
@Override
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {
// 測量子View的寬高
measureChildren(widthMeasureSpec, heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
/*
*(0,0) (左width,0)
* -------------- --------------
* | 左 | | 右 |
* -------------- (左width,左height)-------------- (左width+右width,右height)
*/
int leftLeft = 0;
int leftTop = 0;
int leftRight = leftContentView.getMeasuredWidth();
int leftBottom = leftContentView.getMeasuredHeight();
leftContentView.layout(leftLeft, leftTop, leftRight, leftBottom);
int rightLeft = leftContentView.getMeasuredWidth();
int rightTop = 0;
int rightRight = leftContentView.getMeasuredWidth() + rightActionView.getMeasuredWidth();
int rightBottom = rightActionView.getMeasuredHeight();
rightActionView.layout(rightLeft, rightTop, rightRight, rightBottom);
}
- 寫上我們需要的xml佈局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.welcom.slide.action.view.SlideActionView
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@android:color/white">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="小馬哥" />
</LinearLayout>
<LinearLayout
android:layout_width="100dp"
android:layout_height="match_parent">
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#ff0000"
android:gravity="center"
android:text="刪除" />
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#ff0000"
android:gravity="center"
android:text="拉黑" />
</LinearLayout>
</com.welcom.slide.action.view.SlideActionView>
</LinearLayout>
執行效果:
可以看到和我們的原理圖一樣了,左邊顯示內容區域,右邊因為在螢幕外,暫時看不到。
- 3.實現滑動
// 是否滑動到右邊
private boolean isShowRightView = false;
// 滑動輔助類
private ViewDragHelper helper;
public SlideActionView(Context context) {
this(context, null);
}
public SlideActionView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlideActionView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView() {
helper = ViewDragHelper.create(this, new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(@NonNull View child, int pointerId) {
//捕獲需要滑動的View,這裡返回true
return true;
}
@Override
public void onViewPositionChanged(@NonNull View changedView, int left, int top, int dx, int dy) {
//如果左邊控制元件拖動,我們要讓右邊控制元件也重新佈局,反之
if (changedView == leftContentView) {
rightActionView.layout(rightActionView.getLeft() + dx, 0, rightActionView.getRight() + dx, rightActionView.getBottom() + dy);
} else if (changedView == rightActionView) {
leftContentView.layout(leftContentView.getLeft() + dx, 0, leftContentView.getRight() + dx, leftContentView.getBottom() + dy);
}
}
@Override
public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) {
//對左右越界問題的處理
if (child == leftContentView) {
//處理兩邊的越界問題
if (left >= 0) {
left = 0;
} else if (left <= -rightActionView.getMeasuredWidth()) {
left = -rightActionView.getMeasuredWidth();
}
} else if (child == rightActionView) {
if (left <= leftContentView.getMeasuredWidth() - rightActionView.getMeasuredWidth()) {
left = leftContentView.getMeasuredWidth() - rightActionView.getMeasuredWidth();
} else if (left >= leftContentView.getMeasuredWidth()) {
left = leftContentView.getMeasuredWidth();
}
}
return left;
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
//鬆開後,什麼時候開啟rightActionView,什麼時候關閉leftContentView
//臨界值,rightActionView.getLeft() 和 螢幕的寬度-rightActionView.getWidth()/2
if (releasedChild == leftContentView) {
if (rightActionView.getLeft() < getMeasuredWidth() - rightActionView.getMeasuredWidth() / 2) {
//使用ViewDragHelper來滑動
helper.smoothSlideViewTo(rightActionView, getMeasuredWidth() - rightActionView.getMeasuredWidth(), 0);
isShowRightView = true;
invalidate();
} else {
helper.smoothSlideViewTo(rightActionView, getMeasuredWidth(), 0);
isShowRightView = false;
invalidate();
}
} else if (releasedChild == rightActionView) {
if (rightActionView.getLeft() < getMeasuredWidth() - rightActionView.getMeasuredWidth() / 2) {
helper.smoothSlideViewTo(rightActionView, getMeasuredWidth() - rightActionView.getMeasuredWidth(), 0);
isShowRightView = true;
invalidate();
} else {
helper.smoothSlideViewTo(rightActionView, getMeasuredWidth(), 0);
isShowRightView = false;
invalidate();
}
}
}
});
}
@Override
public void computeScroll() {
if (helper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
//處理父檢視接收的觸控事件。此方法將排程回撥事件。
helper.processTouchEvent(event);
return true;
}
執行程式碼,效果:
- 到這裡就完了嗎?沒有,接著我們將這個放入的RecyclerView中,這裡直接說問題
- 1.在RecyclerView中會出現左右滑動的時候也可以上下滑動
- 2.顯示某條item右邊時,再次滑動因為未復位右邊顯示區域導致複用顯示問題。
- 3.點選回撥未處理
解決方法,繼承RecyclerView,重寫dispatchTouchEvent中判斷
完整程式碼如下:
public class SlideRecyclerView extends RecyclerView {
public SlideRecyclerView(Context context) {
super(context);
}
public SlideRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public SlideRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
try {
if (SlideActionView.isShowRightView()) {
if (SlideActionView.disTouchArea(ev)) {
return super.dispatchTouchEvent(ev);
} else {
SlideActionView.restorePosition();
return true;
}
}
} catch (Exception e) {
//ignore
}
return super.dispatchTouchEvent(ev);
}
}
public class SlideActionView extends ViewGroup {
private static SlideActionView slideActionView = null;
// 左邊顯示內容的View
private View leftContentView;
// 右邊操作的View
private View rightActionView;
// 是否滑動到右邊
private boolean isShowRightView = false;
// 滑動輔助類
private ViewDragHelper helper;
private ISlideRightActionOnClickListener rightActionOnClickListener;
public void setRightActionOnClickListener(ISlideRightActionOnClickListener rightActionOnClickListener) {
this.rightActionOnClickListener = rightActionOnClickListener;
}
public SlideActionView(Context context) {
this(context, null);
}
public SlideActionView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlideActionView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView() {
helper = ViewDragHelper.create(this, new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(@NonNull View child, int pointerId) {
//捕獲需要滑動的View,這裡返回true
return true;
}
@Override
public void onViewPositionChanged(@NonNull View changedView, int left, int top, int dx, int dy) {
//如果左邊控制元件拖動,我們要讓右邊控制元件也重新佈局,反之
if (changedView == leftContentView) {
rightActionView.layout(rightActionView.getLeft() + dx, 0, rightActionView.getRight() + dx, rightActionView.getBottom() + dy);
} else if (changedView == rightActionView) {
leftContentView.layout(leftContentView.getLeft() + dx, 0, leftContentView.getRight() + dx, leftContentView.getBottom() + dy);
}
}
@Override
public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) {
//對左右越界問題的處理
if (child == leftContentView) {
//處理兩邊的越界問題
if (left >= 0) {
left = 0;
} else if (left <= -rightActionView.getMeasuredWidth()) {
left = -rightActionView.getMeasuredWidth();
}
} else if (child == rightActionView) {
if (left <= leftContentView.getMeasuredWidth() - rightActionView.getMeasuredWidth()) {
left = leftContentView.getMeasuredWidth() - rightActionView.getMeasuredWidth();
} else if (left >= leftContentView.getMeasuredWidth()) {
left = leftContentView.getMeasuredWidth();
}
}
return left;
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
//鬆開後,什麼時候開啟rightActionView,什麼時候關閉leftContentView
//臨界值,rightActionView.getLeft() 和 螢幕的寬度-rightActionView.getWidth()/4
if (releasedChild == leftContentView) {
if (rightActionView.getLeft() < getMeasuredWidth() - rightActionView.getMeasuredWidth() / 4) {
//使用ViewDragHelper來滑動
helper.smoothSlideViewTo(rightActionView, getMeasuredWidth() - rightActionView.getMeasuredWidth(), 0);
isShowRightView = true;
invalidate();
} else {
helper.smoothSlideViewTo(rightActionView, getMeasuredWidth(), 0);
isShowRightView = false;
invalidate();
}
} else if (releasedChild == rightActionView) {
if (rightActionView.getLeft() < getMeasuredWidth() - rightActionView.getMeasuredWidth() / 2) {
helper.smoothSlideViewTo(rightActionView, getMeasuredWidth() - rightActionView.getMeasuredWidth(), 0);
isShowRightView = true;
invalidate();
} else {
helper.smoothSlideViewTo(rightActionView, getMeasuredWidth(), 0);
isShowRightView = false;
invalidate();
}
}
}
});
}
@Override
public void computeScroll() {
if (helper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
} else {
if (isShowRightView) {
slideActionView = this;
} else {
slideActionView = null;
}
}
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
//處理父檢視接收的觸控事件。此方法將排程回撥事件。
helper.processTouchEvent(event);
return true;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
if (getChildCount() < 2) {
throw new IllegalArgumentException("child view less than 2!!");
}
leftContentView = getChildAt(0);
rightActionView = getChildAt(1);
if (rightActionView instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) rightActionView).getChildCount(); i++) {
((ViewGroup) rightActionView).getChildAt(i).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (rightActionOnClickListener != null) {
rightActionOnClickListener.onClick(v);
}
}
});
}
} else {
rightActionView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (rightActionOnClickListener != null) {
rightActionOnClickListener.onClick(v);
}
}
});
}
}
public static boolean disTouchArea(MotionEvent ev) {
if (slideActionView == null) return false;
float rawX = ev.getRawX();
float rawY = ev.getRawY();
int[] location = new int[2];
slideActionView.getLocationOnScreen(location);
Log.i("wxq", "rawX->" + rawX);
Log.i("wxq", "rawY->" + rawY);
Log.i("wxq", "locationX->" + location[0]);
Log.i("wxq", "locationY->" + location[1]);
return rawX > location[0] && rawX < (slideActionView.getMeasuredWidth() + location[0])
&& rawY > location[1] && rawY < (slideActionView.getMeasuredHeight() + location[1]);
}
public static boolean isShowRightView() {
return slideActionView != null;
}
//還原位置
public static void restorePosition() {
if (!isShowRightView()) {
return;
}
try {
if (slideActionView.helper.continueSettling(true)) return;
slideActionView.helper.smoothSlideViewTo(slideActionView.rightActionView, slideActionView.getMeasuredWidth(), 0);
slideActionView.isShowRightView = false;
slideActionView.invalidate();
} catch (Exception e) {
//ignore
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 測量子View的寬高
measureChildren(widthMeasureSpec, heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
/*
*(0,0) (左width,0)
* -------------- --------------
* | 左 | | 右 |
* -------------- (左width,左height)-------------- (左width+右width,右height)
*/
int leftLeft = 0;
int leftTop = 0;
int leftRight = leftContentView.getMeasuredWidth();
int leftBottom = leftContentView.getMeasuredHeight();
leftContentView.layout(leftLeft, leftTop, leftRight, leftBottom);
int rightLeft = leftContentView.getMeasuredWidth();
int rightTop = 0;
int rightRight = leftContentView.getMeasuredWidth() + rightActionView.getMeasuredWidth();
int rightBottom = rightActionView.getMeasuredHeight();
rightActionView.layout(rightLeft, rightTop, rightRight, rightBottom);
}
public interface ISlideRightActionOnClickListener {
void onClick(View view);
}
}