防微信左滑删除的效果
package com.loaderman.swiperecycleviewdemo;
import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
public class SwipeRecycleView extends RecyclerView {
private SwipeMenuLayout mLastMenuLayout;
private int mLastTouchPosition;
protected int mScaleTouchSlop;
public SwipeRecycleView(Context context) {
this(context, null);
}
public SwipeRecycleView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public SwipeRecycleView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mScaleTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
private int mLastX, mLastY;
private int mDownX, mDownY;
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean isIntercepted = super.onInterceptTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastX = (int) event.getX();
mLastY = (int) event.getY();
mDownX = (int) event.getX();
mDownY = (int) event.getY();
isIntercepted = false;
//根据MotionEvent的X Y值得到子View
View view = findChildViewUnder(mLastX, mLastY);
if (view == null) return false;
//点击的子View所在的位置
final int touchPos = getChildAdapterPosition(view);
if (touchPos != mLastTouchPosition && mLastMenuLayout != null
&& mLastMenuLayout.currentState != SwipeMenuLayout.STATE_CLOSED) {
if (mLastMenuLayout.isMenuOpen()) {
//如果之前的菜单栏处于打开状态,则关闭它
mLastMenuLayout.smoothToCloseMenu();
}
isIntercepted = true;
} else {
//根据点击位置获得相应的子View
ViewHolder holder = findViewHolderForAdapterPosition(touchPos);
if (holder != null) {
View childView = holder.itemView;
if (childView != null && childView instanceof SwipeMenuLayout) {
mLastMenuLayout = (SwipeMenuLayout) childView;
mLastTouchPosition = touchPos;
}
}
}
break;
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
int dx = (int) (mDownX - event.getX());
int dy = (int) (mDownY - event.getY());
if (Math.abs(dx) > mScaleTouchSlop && Math.abs(dx) > Math.abs(dy)
|| (mLastMenuLayout != null && mLastMenuLayout.currentState != SwipeMenuLayout.STATE_CLOSED)) {
//如果X轴偏移量大于Y轴偏移量 或者上一个打开的菜单还没有关闭 则禁止RecycleView滑动 RecycleView不去拦截事件
return false;
}
break;
}
return isIntercepted;
}
@Override
public boolean onTouchEvent(MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
//若某个Item的菜单还没有关闭,则RecycleView不能滑动
if (!mLastMenuLayout.isMenuClosed()) {
return false;
}
break;
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
if (mLastMenuLayout != null && mLastMenuLayout.isMenuOpen()) {
mLastMenuLayout.smoothToCloseMenu();
}
break;
}
return super.onTouchEvent(e);
}
}
package com.loaderman.swiperecycleviewdemo;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.LinearLayout;
import android.widget.OverScroller;
public class SwipeMenuLayout extends LinearLayout {
public static final int STATE_CLOSED = 0;//关闭状态
public static final int STATE_OPEN = 1;//打开状态
public static final int STATE_MOVING_LEFT = 2;//左滑将要打开状态
public static final int STATE_MOVING_RIGHT = 3;//右滑将要关闭状态
public int currentState = 0;
private int menuWidth;//菜单总长度
private OverScroller mScroller;
private int mScaledTouchSlop;
private int mRightId;//右边隐藏菜单id
private View rightMenuView;//右边的菜单按钮
private int mLastX, mLastY;
private int mDownX, mDownY;
public SwipeMenuLayout(Context context) {
this(context, null);
}
public SwipeMenuLayout(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public SwipeMenuLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mScroller = new OverScroller(context);
ViewConfiguration configuration = ViewConfiguration.get(getContext());
mScaledTouchSlop = configuration.getScaledTouchSlop();
configuration.getScaledMaximumFlingVelocity();
configuration.getScaledMinimumFlingVelocity();
//获取右边菜单id
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SwipeMenuLayout);
mRightId = typedArray.getResourceId(R.styleable.SwipeMenuLayout_right_id, 0);
typedArray.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildren(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
if (mRightId != 0) {
rightMenuView = findViewById(mRightId);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
menuWidth = rightMenuView.getMeasuredWidth();
super.onLayout(changed, l, t, r, b);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mDownX = (int) event.getX();
mDownY = (int) event.getY();
mLastX = (int) event.getX();
break;
case MotionEvent.ACTION_MOVE:
int dx = (int) (mDownX - event.getX());
int dy = (int) (mDownY - event.getY());
//如果Y轴偏移量大于X轴偏移量 不再滑动
if (Math.abs(dy) > Math.abs(dx)) return false;
int deltaX = (int) (mLastX - event.getX());
if (deltaX > 0) {
//向左滑动
currentState = STATE_MOVING_LEFT;
if (deltaX >= menuWidth || getScrollX() + deltaX >= menuWidth) {
//右边缘检测
scrollTo(menuWidth, 0);
currentState = STATE_OPEN;
break;
}
} else if (deltaX < 0) {
//向右滑动
currentState = STATE_MOVING_RIGHT;
if (deltaX + getScrollX() <= 0) {
//左边缘检测
scrollTo(0, 0);
currentState = STATE_CLOSED;
break;
}
}
scrollBy(deltaX, 0);
mLastX = (int) event.getX();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (currentState == STATE_MOVING_LEFT) {
//左滑打开
mScroller.startScroll(getScrollX(), 0, menuWidth - getScrollX(), 0, 300);
invalidate();
} else if (currentState == STATE_MOVING_RIGHT || currentState == STATE_OPEN) {
//右滑关闭
smoothToCloseMenu();
}
//如果小于滑动距离并且菜单是关闭状态 此时Item可以有点击事件
int deltx = (int) (mDownX - event.getX());
return !(Math.abs(deltx) < mScaledTouchSlop && isMenuClosed()) || super.onTouchEvent(event);
}
return super.onTouchEvent(event);
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
// Get current x and y positions
int currX = mScroller.getCurrX();
int currY = mScroller.getCurrY();
scrollTo(currX, currY);
postInvalidate();
}
if (isMenuOpen()) {
currentState = STATE_OPEN;
} else if (isMenuClosed()) {
currentState = STATE_CLOSED;
}
}
/**
* 判断menu此时的状态
*
* @return true 打开状态 false 处于关闭状态
*/
public boolean isMenuOpen() {
return getScrollX() >= menuWidth;
}
/**
* 判断menu此时的状态
*
* @return true 关闭状态 false 未关闭状态
*/
public boolean isMenuClosed() {
return getScrollX() <= 0;
}
/**
* 关闭菜单
*/
public void smoothToCloseMenu() {
mScroller.startScroll(getScrollX(), 0, -getScrollX(), 0, 300);
invalidate();
}
}
package com.loaderman.swiperecycleviewdemo;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
public class SwipeAdapter extends RecyclerView.Adapter<SwipeAdapter.SwipeHolder> {
private Context mContext;
public SwipeAdapter(Context mContext) {
this.mContext = mContext;
}
@Override
public SwipeHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.swipe_menu_item, parent, false);
return new SwipeHolder(view);
}
@Override
public void onBindViewHolder(final SwipeHolder holder, final int position) {
holder.tv_content.setText("这是第" + (position + 1) + "条数据");
holder.tv_to_unread.setVisibility(position % 2 == 0 ? View.VISIBLE : View.GONE);
holder.tv_to_top.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (holder.swipe_menu.isMenuOpen()) {
holder.swipe_menu.smoothToCloseMenu();
}
Toast.makeText(mContext, "置顶", Toast.LENGTH_SHORT).show();
}
});
holder.tv_to_unread.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (holder.swipe_menu.isMenuOpen()) {
holder.swipe_menu.smoothToCloseMenu();
}
Toast.makeText(mContext, "标为未读", Toast.LENGTH_SHORT).show();
}
});
holder.tv_to_delete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (holder.swipe_menu.isMenuOpen()) {
holder.swipe_menu.smoothToCloseMenu();
}
Toast.makeText(mContext, "删除", Toast.LENGTH_SHORT).show();
}
});
holder.swipe_menu.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(mContext, "这是第" + (position + 1) + "条数据", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public int getItemCount() {
return 30;
}
public class SwipeHolder extends RecyclerView.ViewHolder {
private TextView tv_to_top, tv_to_unread, tv_to_delete, tv_content;
private SwipeMenuLayout swipe_menu;
public SwipeHolder(View itemView) {
super(itemView);
swipe_menu = (SwipeMenuLayout) itemView.findViewById(R.id.swipe_menu);
tv_to_top = (TextView) itemView.findViewById(R.id.tv_to_top);
tv_to_unread = (TextView) itemView.findViewById(R.id.tv_to_unread);
tv_to_delete = (TextView) itemView.findViewById(R.id.tv_to_delete);
tv_content = (TextView) itemView.findViewById(R.id.tv_content);
}
}
}
package com.loaderman.swiperecycleviewdemo;
import android.graphics.Rect;
import android.support.v7.widget.RecyclerView;
import android.view.View;
public class MyDividerDecoration extends RecyclerView.ItemDecoration {
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
// super.getItemOffsets(outRect, view, parent, state);
outRect.set(0, 0, 0, 2);
}
}
package com.loaderman.swiperecycleviewdemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SwipeRecycleView swipe_recycleview = (SwipeRecycleView) findViewById(R.id.swipe_recycleview);
swipe_recycleview.setLayoutManager(new LinearLayoutManager(this));
swipe_recycleview.addItemDecoration(new MyDividerDecoration());
SwipeAdapter swipeAdapter = new SwipeAdapter(this);
swipe_recycleview.setAdapter(swipeAdapter);
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.loaderman.swiperecycleviewdemo.MainActivity">
<com.loaderman.swiperecycleviewdemo.SwipeRecycleView
android:id="@+id/swipe_recycleview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
swipe_menu_item.xml
<?xml version="1.0" encoding="utf-8"?>
<com.loaderman.swiperecycleviewdemo.SwipeMenuLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/swipe_menu"
android:layout_width="match_parent"
android:layout_height="70dp"
android:layout_centerInParent="true"
android:background="@color/white"
android:orientation="horizontal"
app:content_id="@+id/ll_layout"
app:right_id="@+id/ll_right_menu">
<LinearLayout
android:id="@+id/ll_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="20dp"
android:gravity="center_vertical"
android:text="HelloWorld"
android:textSize="16sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="right"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:gravity="center_vertical|end"
android:text="左滑←←←"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_right_menu"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_to_top"
android:layout_width="90dp"
android:layout_height="match_parent"
android:background="@color/gray_holo_light"
android:gravity="center"
android:text="置顶"
android:textColor="@color/white"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_to_unread"
android:layout_width="90dp"
android:layout_height="match_parent"
android:background="@color/yellow"
android:gravity="center"
android:text="标为未读"
android:textColor="@color/white"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_to_delete"
android:layout_width="90dp"
android:layout_height="match_parent"
android:background="@color/red_f"
android:gravity="center"
android:text="删除"
android:textColor="@color/white"
android:textSize="16sp" />
</LinearLayout>
</com.loaderman.swiperecycleviewdemo.SwipeMenuLayout>
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SwipeMenuLayout">
<!-- format="reference"意为参考某一资源ID -->
<attr name="content_id" format="reference" />
<attr name="right_id" format="reference" />
</declare-styleable>
</resources>
color.xml
<color name="white">#fff</color>
<color name="red_f">#ff3c00</color>
<item name="gray_holo_light" type="color">#ffd0d0d0</item>
<color name="yellow">#c0ffbd21</color>
效果图:

最后,关注【码上加油站】微信公众号后,有疑惑有问题想加油的小伙伴可以码上加入社群,让我们一起码上加油吧!!!
浙公网安备 33010602011771号