仿QQ侧滑删除Item效果Demo

Demo 传送门

关于仿QQ侧滑删除Item的代码,还是有许多实现的方式的,就比如上篇博客写的利用HorizontalScrollView嵌套来实现,但是体验效果却并不是特别的理想。之后也尝试了另外的一种方式来达到这种侧滑删除的效果,特别的理想,现在就把代码粘贴上来。


接下是来实现效果的几个步骤:
1.新建一个项目MainActivity类文件以及activity_main.xml布局文件,代码如下:

MainActivity类文件

package com.lw.widget.slideitem;

import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    Context context;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        RecyclerView    recyclerView  = (RecyclerView) findViewById(R.id.rv_data_list);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.addItemDecoration(new LinearItemDecoration()); //设置分割线样式
        SlideItemAdapter slideitemadapter = new SlideItemAdapter(context);
        recyclerView.setAdapter(slideitemadapter);
        slideitemadapter.setItemClickListener(new ItemClickListener() {
            @Override
            public void onItemClick(View view, int postion) {

                Toast.makeText(getApplication(),"商品"+(postion+1),Toast.LENGTH_LONG).show();

            }
        });

    }
}

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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.lw.widget.slideitem.MainActivity"
    android:background="#f6f6f6">

    <android.support.v7.widget.RecyclerView

        android:id="@+id/rv_data_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >

    </android.support.v7.widget.RecyclerView>

</RelativeLayout>

2.根据下边的这张图,创建关键类及接口:

这里写图片描述

SlideItemAdapter 适配器

package com.lw.widget.slideitem;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Administrator on 2016-07-05.
 */

public class SlideItemAdapter  extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements ItemSlideHelper.Callback{

    private RecyclerView mRecyclerView;
    private List<Integer> colors;
    private Context mContext;
    private ItemClickListener mItemClickListener;

    public void setItemClickListener(ItemClickListener mItemClickListener) {

        this.mItemClickListener = mItemClickListener;
    }



    public SlideItemAdapter(Context context) {

        // 初始化
        mContext = context;

        // 填充list的内容模拟数据,否则应该异步执行
        colors = new ArrayList<Integer>();
        for (int i = 0; i < 15; i++) {
            colors.add(R.color.colorAccent);
        }
    }
     @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_slide, parent, false);
         return new TextVH(view);
    }

    @Override
    public int getItemCount() {
        return colors.size();
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
//        String text  = "item: " + position;
        TextVH textVH = (TextVH) holder;
        textVH.item_layout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (null != mItemClickListener) {
                    mItemClickListener.onItemClick(v, position);
                }
            }
        });
//        textVH.textView.setText(text);
        textVH.tv_delete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i("kkkk","删除按钮有效==========="+position);
                colors.remove(position);
                // 刷新ListView内容
//                notifyDataSetChanged();
                notifyItemChanged(colors.size());
            }

        });

    }


    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);

        mRecyclerView = recyclerView;
        mRecyclerView.addOnItemTouchListener(new ItemSlideHelper(mRecyclerView.getContext(), this));
    }





    @Override
    public int getHorizontalRange(RecyclerView.ViewHolder holder) {

        if(holder.itemView instanceof LinearLayout){
            ViewGroup viewGroup = (ViewGroup) holder.itemView;
            if(viewGroup.getChildCount() == 2){
                return viewGroup.getChildAt(1).getLayoutParams().width;
            }
        }


        return 0;
    }

    @Override
    public RecyclerView.ViewHolder getChildViewHolder(View childView) {
        return mRecyclerView.getChildViewHolder(childView);
    }

    @Override
    public View findTargetView(float x, float y) {
        return mRecyclerView.findChildViewUnder(x, y);
    }


}

class TextVH extends RecyclerView.ViewHolder{

    TextView    textView,tv_delete;
    LinearLayout item_layout;


    public TextVH(View itemView) {
        super(itemView);

        textView = (TextView) itemView.findViewById(R.id.tv_text);
        tv_delete = (TextView) itemView.findViewById(R.id.tv_delete);
        item_layout = (LinearLayout) itemView.findViewById(R.id.item_layout);

    }
}

item_slide.xml适配器布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#f6f6f6"
    >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"

        android:layout_marginTop="5dp"
        android:background="#fff"
        >

        <!--商品详情-->
        <LinearLayout
            android:id="@+id/item_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"

            android:orientation="horizontal"
            android:gravity="center|left"
            android:background="#fff">
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:layout_marginBottom="10dp"
                android:layout_weight="7"
                android:layout_marginLeft="15dp"
                android:paddingTop="1dp"
                android:paddingBottom="1dp"
                android:paddingLeft="1dp"
                android:paddingRight="1dp"
                android:orientation="horizontal"
                android:gravity="center|left"
                android:background="#f6f6f6">
                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:orientation="horizontal"
                    android:gravity="center"
                    android:background="#fff">

                    <ImageView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="10dp"
                        android:layout_marginBottom="10dp"
                        android:background="@mipmap/liucha"/>

                </LinearLayout>
            </LinearLayout>
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="3"
                android:layout_marginLeft="15dp"
                android:layout_marginRight="10dp"
                android:orientation="vertical"
                android:gravity="center_horizontal|right">

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="10dp"
                    android:orientation="horizontal"
                    android:gravity="center|left">

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:textColor="#292421"
                        android:text="挑战不一样的fell,大茶网青春小茶合包  (五包)"
                        android:textSize="15dp"/>
                </LinearLayout>
                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="10dp"
                    android:orientation="horizontal"
                    android:gravity="center_horizontal|left">

                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:orientation="horizontal"
                        android:gravity="center_horizontal|left">
                        <TextView
                            android:id="@+id/tv_text"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="5dp"
                            android:text="数量:"
                            android:textColor="#808A87"
                            android:textSize="12dp"/>

                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_marginLeft="5dp"
                            android:layout_marginBottom="5dp"
                            android:text="1"
                            android:textColor="#808A87"
                            android:textSize="12dp"/>
                    </LinearLayout>
                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:orientation="horizontal"
                        android:gravity="center_horizontal|right">
                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="5dp"
                            android:text="单价:"
                            android:textColor="#808A87"
                            android:textSize="12dp"/>

                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="5dp"
                            android:layout_marginLeft="5dp"
                            android:text="¥172.00"
                            android:textColor="#808A87"
                            android:textSize="12dp"/>
                    </LinearLayout>

                </LinearLayout>

            </LinearLayout>
        </LinearLayout>

    </LinearLayout>

    <LinearLayout
        android:layout_width="80dp"
        android:layout_height="match_parent"
        >

        <TextView
            android:id="@+id/tv_delete"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_marginTop="5dp"
            android:layout_weight="1"
            android:text="删除"
            android:background="@android:color/holo_red_light"
            android:gravity="center"
            android:textColor="@android:color/white"
            />
    </LinearLayout>

</LinearLayout>

LinearItemDecoration 类文件

package com.lw.widget.slideitem;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.annotation.ColorInt;
import android.support.v7.widget.RecyclerView;
import android.util.TypedValue;
import android.view.View;

public class LinearItemDecoration extends RecyclerView.ItemDecoration{

    private Paint   mPaint;
    private int     mColor;

    public LinearItemDecoration(@ColorInt int color) {
        mPaint = new Paint();
        mPaint.setColor(color);
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);

    }

    public LinearItemDecoration() {
        this(0);
    }

    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        int height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, parent.getResources().getDisplayMetrics());
        RecyclerView.LayoutManager  layoutManager = parent.getLayoutManager();
        View childView ;
        RecyclerView.LayoutParams   layoutParams;
        int childCount = layoutManager.getChildCount();
        Rect  drawRect = new Rect();
        int top,left,right,bottom;

        for(int childIndex = 0 ; childIndex < childCount - 1; childIndex++){
            childView = layoutManager.getChildAt(childIndex);
            layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
            top = childView.getBottom() + layoutParams.bottomMargin;
            left = childView.getLeft() + layoutParams.leftMargin + childView.getPaddingLeft();
            right = childView.getRight() - childView.getPaddingRight() - layoutParams.rightMargin;
            bottom = top + height;
            drawRect.set(left,top,  right, bottom);
            c.drawRect(drawRect, mPaint);
        }
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        int height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, parent.getResources().getDisplayMetrics());
        outRect.set(0, 0, 0,  height);
    }
}

ItemSlideHelper类文件

package com.lw.widget.slideitem;

import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Rect;
import android.support.v4.view.GestureDetectorCompat;
import android.support.v4.view.MotionEventCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;

/**
 * 帮助显示左滑菜单
 */
public class ItemSlideHelper implements RecyclerView.OnItemTouchListener, GestureDetector.OnGestureListener {


    private static final String TAG = "ItemSwipeHelper";


    private final int DEFAULT_DURATION = 200;

    private View mTargetView;

    private int mActivePointerId;

    private int mTouchSlop;
    private int mMaxVelocity;
    private int mMinVelocity;
    private int mLastX;
    private int mLastY;


    private boolean mIsDragging;

    private Animator mExpandAndCollapseAnim;

    private GestureDetectorCompat mGestureDetector;

    private Callback mCallback;


    public ItemSlideHelper(Context context, Callback callback) {
        this.mCallback = callback;

        //手势用于处理fling
        mGestureDetector = new GestureDetectorCompat(context, this);

        ViewConfiguration configuration = ViewConfiguration.get(context);
        mTouchSlop = configuration.getScaledTouchSlop();
        mMaxVelocity = configuration.getScaledMaximumFlingVelocity();
        mMinVelocity = configuration.getScaledMinimumFlingVelocity();
    }

    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        Log.d(TAG, "onInterceptTouchEvent: " + e.getAction());


        int action =  MotionEventCompat.getActionMasked(e);
        int x = (int) e.getX();
        int y = (int) e.getY();



        //如果RecyclerView滚动状态不是空闲targetView不是空
        if(rv.getScrollState() != RecyclerView.SCROLL_STATE_IDLE){
            if(mTargetView != null){
                //隐藏已经打开
                smoothHorizontalExpandOrCollapse(DEFAULT_DURATION / 2);
                mTargetView = null;
            }

            return false;
        }

        //如果正在运行动画 ,直接拦截什么都不做
        if(mExpandAndCollapseAnim != null && mExpandAndCollapseAnim.isRunning()){
            return true;
        }

        boolean needIntercept =  false;
        switch (action) {
            case MotionEvent.ACTION_DOWN:


                mActivePointerId = MotionEventCompat.getPointerId(e, 0);
                mLastX = (int) e.getX();
                mLastY = (int) e.getY();

                /*
                * 如果之前有一个已经打开的项目,当此次点击事件没有发生在右侧的菜单中则返回TRUE,
                * 如果点击的是右侧菜单那么返回FALSE这样做的原因是因为菜单需要响应Onclick
                * */
                if(mTargetView != null){
                    return !inView(x, y);
                }

                //查找需要显示菜单的view;
                mTargetView = mCallback.findTargetView(x, y);

                break;
            case MotionEvent.ACTION_MOVE:

                int deltaX = (x - mLastX);
                int deltaY = (y - mLastY);

                if(Math.abs(deltaY) > Math.abs(deltaX))
                    return false;

                //如果移动距离达到要求,则拦截
                needIntercept = mIsDragging = mTargetView != null && Math.abs(deltaX) >= mTouchSlop;
                break;

            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                /*
                * 走这是因为没有发生过拦截事件
                * */
                if(isExpanded()){

                    if (inView(x, y)) {
                      // 如果走这那行这个ACTION_UP的事件会发生在右侧的菜单中
                        Log.d(TAG, "click item");
                    }else{
                        //拦截事件,防止targetView执行onClick事件
                        needIntercept = true;
                    }

                    //折叠菜单
                    smoothHorizontalExpandOrCollapse(DEFAULT_DURATION / 2);
                }

                mTargetView = null;
                break;
        }

        return  needIntercept ;
    }



    private boolean isExpanded() {
        return mTargetView != null &&  mTargetView.getScrollX() == getHorizontalRange();
    }

    private boolean isCollapsed() {

        return mTargetView != null && mTargetView.getScrollX() == 0;
    }

    /*
    * 根据targetView的scrollX计算出targetView的偏移,这样能够知道这个point
    * 是在右侧的菜单中
    * */
    private boolean inView(int x, int y) {

        if (mTargetView == null)
            return false;

        int scrollX = mTargetView.getScrollX();
        int left = mTargetView.getWidth() - scrollX;
        int top = mTargetView.getTop();
        int right = left + getHorizontalRange() ;
        int bottom = mTargetView.getBottom();
        Rect rect = new Rect(left, top, right, bottom);
        return rect.contains(x, y);
    }



    @Override
    public void onTouchEvent(RecyclerView rv, MotionEvent e) {
        Log.d(TAG, "onTouchEvent: " + e.getAction());

        if(mExpandAndCollapseAnim != null && mExpandAndCollapseAnim.isRunning() || mTargetView == null)
            return;

        //如果要响应fling事件设置将mIsDragging设为false
        if (mGestureDetector.onTouchEvent(e)) {
            mIsDragging = false;
            return;
        }


        int x = (int) e.getX();
        int y = (int) e.getY();
        int action =  MotionEventCompat.getActionMasked(e);
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                //RecyclerView 不会转发这个Down事件

                break;
            case MotionEvent.ACTION_MOVE:
                int deltaX = (int) (mLastX - e.getX());
                if(mIsDragging) {
                    horizontalDrag(deltaX);
                }
                mLastX = x;
                break;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:

                 if(mIsDragging){
                    if(!smoothHorizontalExpandOrCollapse(0) && isCollapsed())
                        mTargetView = null;

                    mIsDragging = false;
                 }

                break;
        }


    }

    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

    }

    /**
     *
     * 根据touch事件来滚动View的scrollX
     *
     * @param delta
     */
    private void horizontalDrag(int delta) {
        int scrollX = mTargetView.getScrollX();
        int scrollY = mTargetView.getScrollY();
        if ((scrollX + delta) <= 0) {
            mTargetView.scrollTo(0, scrollY);
            return;
        }


        int horRange = getHorizontalRange();
        scrollX += delta;
        if (Math.abs(scrollX) < horRange) {
            mTargetView.scrollTo(scrollX, scrollY);
        } else {
            mTargetView.scrollTo(horRange, scrollY);
        }


    }


    /**
     * 根据当前scrollX的位置判断是展开还是折叠
     *
     * @param velocityX
     *  如果不等于0那么这是一次fling事件,否则是一次ACTION_UP或者ACTION_CANCEL
     */
    private boolean smoothHorizontalExpandOrCollapse(float velocityX) {

        int scrollX = mTargetView.getScrollX();
        int scrollRange = getHorizontalRange();

        if (mExpandAndCollapseAnim != null)
            return false;


        int to = 0;
        int duration = DEFAULT_DURATION;

        if (velocityX == 0) {
            //如果已经展一半,平滑展开
            if (scrollX > scrollRange / 2) {
                to = scrollRange;
            }
        } else {


            if (velocityX > 0)
                to = 0;
            else
                to = scrollRange;

            duration = (int) ((1.f - Math.abs(velocityX) / mMaxVelocity) * DEFAULT_DURATION);
        }

        if(to == scrollX)
            return false;

        mExpandAndCollapseAnim = ObjectAnimator.ofInt(mTargetView, "scrollX", to);
        mExpandAndCollapseAnim.setDuration(duration);
        mExpandAndCollapseAnim.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                mExpandAndCollapseAnim = null;
                if (isCollapsed())
                    mTargetView = null;

                Log.d(TAG, "onAnimationEnd");
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                //onAnimationEnd(animation);
                mExpandAndCollapseAnim = null;

                Log.d(TAG, "onAnimationCancel");
            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
        mExpandAndCollapseAnim.start();

        return true;
    }




    public  int getHorizontalRange(   ) {
        RecyclerView.ViewHolder viewHolder = mCallback.getChildViewHolder(mTargetView);
        return mCallback.getHorizontalRange(viewHolder);
    }


    @Override
    public boolean onDown(MotionEvent e) {
        return false;
    }

    @Override
    public void onShowPress(MotionEvent e) {

    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        return false;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        return false;
    }

    @Override
    public void onLongPress(MotionEvent e) {

    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {

        if(Math.abs(velocityX) > mMinVelocity && Math.abs(velocityX) < mMaxVelocity) {
            if(!smoothHorizontalExpandOrCollapse(velocityX) ) {
                if(isCollapsed())
                    mTargetView = null;
                return true;
            }
        }
        return false;
    }




    public interface Callback {

        int getHorizontalRange(RecyclerView.ViewHolder holder);

        RecyclerView.ViewHolder getChildViewHolder(View childView);

        View findTargetView(float x, float y);

    }
}

ItemClickListener Item监听接口

package com.lw.widget.slideitem;

import android.view.View;

/**
 * Created by Administrator on 2016-07-05.
 */
public interface ItemClickListener {

    /**
     * Item 普通点击
     */
    public void onItemClick(View view, int postion);

}

3.在app目录下的build文件下添加如下代码:

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.1.1'
    compile 'com.android.support:recyclerview-v7:23.1.1'
}

实现之后的效果图如下:

这里写图片描述

这里写图片描述

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值