防微信左滑删除的效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
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);
    }
 
}

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
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();
    }
}

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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);
        }
    }
}

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);
    }
}

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<?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

1
2
3
4
5
6
7
8
<?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

1
2
3
4
<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>

 效果图:

posted on   LoaderMan  阅读(313)  评论(0编辑  收藏  举报

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

喜欢请打赏

扫描二维码打赏

了解更多

点击右上角即可分享
微信分享提示