首先我们在日常的软件使用中已经有很多这样的例子了,例如我们用到的QQ、微信都具有这样的功能。
btn_delete.xml:
现在我们需要重写含有两个参数的构造方法,并对部分变量就行初始化。
上面是对事件的分发处理,下面我们就来看看事件的处理即: onTouchEvent(MotionEvent ev) 方法。
上面就是定义一个接口并写隐藏pop的方法。以上全部就是自定义的全部代码。接下来只需要将其写到布局文件当中。
而本文只是简单的起一个引导的作用,在已经存在的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>
<?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