防微信左滑删除的效果

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>

 效果图:

posted on 2017-06-16 13:50  LoaderMan  阅读(300)  评论(0编辑  收藏  举报

导航