Android开发--自定义控件实现listview的滑动删除item

首先我们在日常的软件使用中已经有很多这样的例子了,例如我们用到的QQ、微信都具有这样的功能。
而本文只是简单的起一个引导的作用,在已经存在的listview的基础上对其进行改造,使它能更好的满足
我们在开发当中遇到的需求。

首先,我们先看下我们要达到的效果:





看到效果图,首先我们来思考一下,要实现这样的效果,我们需要进行怎样的步骤?  
思路:可以确定的是,我们肯定通过手势的 从右自左的滑动,这样,我们就想肯定要获 取到滑动之前的X,Y坐标和滑动结束的X,Y坐标
通过判断 开始和结束时坐标距离的绝对值来计算出滑动的距离。在这之前,我们应该设置一个值,来表示当大于这个值时。
我们就应该响应滑动事件,及显示删除按钮。如何显示一个删除按钮呢?我们这儿采用Android自带的一个控件: PopupWindow。

通过上面简单的分析之后,我们就来看具体的代码实现。然后在慢慢了解编码过程中,我们需要注意的细节问题。

首先,来看下目录结构:


准备两个布局文件:1、activity_main.xml 主界面布局文件。2、btn_delete.xml Button删除按钮的布局文件

下面是布局文件的具体代码实现:

activity_main.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.hql.listviewitemdelete.widget.ListViewDeleteItem
        android:id="@+id/list_item_delete"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </com.hql.listviewitemdelete.widget.ListViewDeleteItem>
</LinearLayout>

btn_delete.xml:

<?xml version="1.0" encoding="utf-8"?>
<Button
    android:id="@+id/btn_delete"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="#ff0202"
    android:text="删除"/>

布局文件都很简单,相信大家都能看得懂。

接下来,我们就来编写 ListViewDeleteItem,这个类,因为我们是在listview进行扩展开发,所以我们应该是继承自listview。
/**
 * listview滑动删除item
 * Created by Mr.H on 2015/11/16.
 */
public class ListViewDeleteItem extends ListView

接下来我们根据刚刚的 思路,首先应该定义一下变量:开始X,Y坐标、结束X,Y坐标、相应滑动的距离、是否正在滑动、布局加载器、
PopupWindow、PopupWindow宽高、删除按钮Button、删除按钮的回调接口、手指现在触摸到的view、和手指触摸到的view的位置。
以上就是我们应该要定义的成员变量。

 //手指按下的X,Y坐标
    private int startX;
    private int startY;
    //手指移动结束时的X,Y坐标
    private int endX;
    private int endY;
    //用户手势滑动的距离
    private int moveLength;
    //是否正在滑动
    private boolean isSliding;
    //布局加载器
    private LayoutInflater mInflater;
    //pop弹出
    private PopupWindow mPopupWindow;
    //pop高度
    private int mPopupWindowHeight;
    //pop宽度
    private int mPopupWindowWidth;
    //删除按钮
    private Button mBtnDelete;
    //删除按钮的回调接口
    public BtnDeleteClickListener mListener;
    //当前手指触摸的View
    private View mCurrentView;
    //当前手指触摸的位置
    private int mCurrentViewPos;

现在我们需要重写含有两个参数的构造方法,并对部分变量就行初始化。

public ListViewDeleteItem(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        //实例化布局加载器
        mInflater = LayoutInflater.from(context);
        //getScaledTouchSlop是一个距离,表示滑动的时候,手的移动要大于这个距离才开始移动控件,如果小于这个距离就不触发移动控件。
        moveLength = ViewConfiguration.get(context).getScaledTouchSlop();
        //获取到删除按钮的布局文件
        View view = mInflater.inflate(R.layout.btn_delete, null);
        //查找删除按钮
        mBtnDelete = (Button) view.findViewById(R.id.btn_delete);
        //实例化POP,并且绑定视图,和设置宽高
        mPopupWindow = new PopupWindow(view, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
        //先调用下measure,否则拿不到宽和高
        mPopupWindow.getContentView().measure(0, 0);
        //获取button的宽高
        mPopupWindowHeight = mPopupWindow.getContentView().getMeasuredHeight();
        mPopupWindowWidth = mPopupWindow.getContentView().getMeasuredWidth();
    }

上面的主要是对布局加载器进行初始化,加载一个布局文件,然后绑定到PopupWindow中,并且测出button在PopupWindow中的宽高。

做完以上操作,我们现在就需要实现我们最重要的部分了,即手势的判断。

接下来我们重写 dispatchTouchEvent(MotionEvent ev)这个方法。这个方法主要是对 点击事件的分发。
首先,我们通过  ev 这个参数来获取到用户的点击事件类型,并且用两个局部变量,记录下点击时的X,Y坐标。
//获取点击类型
        int actiion = ev.getAction();
        //获取按下时的x,y坐标
        int x = (int) ev.getX();
        int y = (int) ev.getY();

对此,我们应该判断用户的两个动作:按下和移动

首先我们先来看按下事件的处理:
 switch (actiion)
        {
            //按下
            case MotionEvent.ACTION_DOWN:
                startX = x;
                startY = y;
                //如果当前的pop显示,则将其隐藏掉
                if (mPopupWindow.isShowing())
                {
                    dismissPopWindow();
                    return false;
                }
                // 获得当前手指按下时的item的位置
                mCurrentViewPos = pointToPosition(startX, startY);
                // 获得当前手指按下时的item
                View view = getChildAt(mCurrentViewPos - getFirstVisiblePosition());
                mCurrentView = view;
                break;

将开始获取到的X,Y坐标赋值给定义的变量。并且判断PopupWindow是否正在显示,如果是则关闭,并且返回false。

之后通过pointToPosition()方法获取到当前按下的item的位置。通过getChildAt()获取到当前点击item位置所对应的view。
以上就是用户按下的事件。

下面来处理用户移动时的事件。
 //移动
            case MotionEvent.ACTION_MOVE:
                endX = x;
                endY = y;
                int dx = endX - startX;
                int dy = endY - startY;
                /**
                 * 判断是否是从右到左的滑动
                 */
                if (endX < startX && Math.abs(dx) > moveLength && Math.abs(dy) < moveLength)
                {
                    isSliding = true;
                }
                break;

上面是对事件的分发处理,下面我们就来看看事件的处理即: onTouchEvent(MotionEvent ev) 方法。
int action = ev.getAction();
        if (isSliding)
        {
            switch (action)
            {
                case MotionEvent.ACTION_UP:
                    isSliding = false;
                    break;
                case MotionEvent.ACTION_MOVE:
                    int[] location = new int[2];
                    mCurrentView.getLocationOnScreen(location);
                    //设置pop显示的位置
                    mPopupWindow.showAtLocation
                            (mCurrentView, Gravity.LEFT | Gravity.TOP,
                                    location[0] + mCurrentView.getWidth(),
                                    location[1] + mCurrentView.getHeight() / 2 - mPopupWindowHeight / 2);
                    mBtnDelete.setOnClickListener(new OnClickListener()
                    {
                        @Override
                        public void onClick(View view)
                        {
                            if (mListener != null)
                            {
                                mListener.btnClick(mCurrentViewPos);
                                mPopupWindow.dismiss();
                            }
                        }
                    });
                    break;
            }
        }
        return super.onTouchEvent(ev);

首先,我还是要先获取到用户的点击事件类型,并且判断是否正在移动,当用户手势抬起的时候,将是否正在滑动设置为false。
处理用户移动的事件,设置PopupWindow的显示位置,并且给button设置点击事件。
public void setBtnDeleteClickListener(BtnDeleteClickListener listener)
    {
        mListener = listener;
    }

    public interface BtnDeleteClickListener
    {
        void btnClick(int position);
    }

    /**
     * 隐藏pop
     */
    private void dismissPopWindow()
    {
        if (mPopupWindow != null && mPopupWindow.isShowing())
        {
            mPopupWindow.dismiss();
        }
    }

上面就是定义一个接口并写隐藏pop的方法。以上全部就是自定义的全部代码。接下来只需要将其写到布局文件当中。

demo下载地址:http://download.csdn.net/detail/poison_h/9275487



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值