您的位置:首页 > 移动开发 > Android开发

Android学习小Demo(18)Todo List 仿QQ删除任务

2014-04-14 11:34 471 查看
一般情况下,如果想要在ListView上面实现Listitem的滑动删除效果,或者仿QQ的滑动显示删除效果的时候,只需要继承ListView,自定义一个ListView就可以,不过由于之前用了开源库StickyListHeaders来实现ListView的分组,那么这一次在实现这个仿QQ的左右滑动显示隐藏删除按钮的时候,就要在StickyListHeaders的基础上实现。而StickyListHeaders要开放了一个setOnTouchListener的接口,允许调用者传进去一个OnTouchListener,来实现自定义的OnTouch效果,如下:

@Override
public void setOnTouchListener(final OnTouchListener l) {
if (l != null) {
mList.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return l.onTouch(StickyListHeadersListView.this, event);
}
});
} else {
mList.setOnTouchListener(null);
}
}


那么解决问题的方法就在于实现一个OnTouchListener,然后将其传给StickyListHeaderListView。

lvTasks.setOnTouchListener(new ListitemSlopListener(this));


而ListitemSlopListener就是我们自定义的OnTouchListener了,其代码如下:

public class ListitemSlopListener implements OnTouchListener {

...
private void initConfiguration() {
ViewConfiguration vc = ViewConfiguration.get(mContext);
mSlop = vc.getScaledTouchSlop();
mMinFling = vc.getScaledMaximumFlingVelocity();
mMaxFling = mMinFling * 8;
}
...
@Override
public boolean onTouch(View v, MotionEvent event) {
mListView = ((StickyListHeadersListView) v).getWrappedList();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mDownX = event.getX();
mDownY = event.getY();
mPosition = mListView.pointToPosition((int)mDownX, (int)mDownY);
mSlopView = mListView.getChildAt(mPosition - mListView.getFirstVisiblePosition());
Log.v(Helper.TAG,"Action Down : onTouch Triggered");
if(mSlopView != null){
btnDelete = (Button) mSlopView.findViewById(R.id.btnDelete);
if(btnDelete.getVisibility() == View.INVISIBLE || btnDelete.getVisibility() == View.GONE){
btnDelete.setVisibility(View.VISIBLE);
ViewHelper.setAlpha(btnDelete, 0);
}
mBtnWidth = btnDelete.getWidth();
initVelocityTrackerIfNotExists(event);
return true;
}
break;
case MotionEvent.ACTION_MOVE:
if(mVelocityTracker != null && mSlopView != null && btnDelete != null){
float dx = event.getX() - mDownX;
float dy = event.getY() - mDownY;
if(Math.abs(dx) > mSlop && Math.abs(dy) < mSlop){
mMoving = true;
}
if(mMoving){
if (dx > 0) {
//Direction : right to hide the button if the button shows
if(ViewHelper.getAlpha(btnDelete) > 0){
ViewHelper.setAlpha(
btnDelete,Math.min(1f,Math.max(0f, 1f -  Math.abs(dx) /mBtnWidth)));
}
} else {
//Direction : Left to show the button if the button exists
ViewHelper.setAlpha(
btnDelete,
Math.max(0f,Math.min(1f, Math.abs(dx) / mBtnWidth)));
}
return true;
}else{
return false;
}
}
case MotionEvent.ACTION_UP:
if (mVelocityTracker != null && mSlopView != null && mMoving) {
float dx = event.getX() - mDownX;
mVelocityTracker.computeCurrentVelocity(1000);
float vx = Math.abs(mVelocityTracker.getXVelocity());
float vy = Math.abs(mVelocityTracker.getYVelocity());

boolean isShow = false;
if(Math.abs(dx) > mBtnWidth / 2){
isShow = dx < 0;
}else if(vx > mMinFling && vx < mMaxFling && vx > vy){
isShow = vx < 0;
}

if(isShow){
ViewPropertyAnimator.animate(btnDelete).alpha(1f).setDuration(DURATION);
}else{
ViewPropertyAnimator.animate(btnDelete).alpha(0f).setDuration(DURATION);
}
recycleVelocityTracker();
mMoving = false;
}else{
//Only When the Delete Button is not visible, the the onItemClick action was fired.
if(btnDelete != null && ViewHelper.getAlpha(btnDelete) > 0){
ViewPropertyAnimator.animate(btnDelete).alpha(0f).setDuration(DURATION);
}else{
TodoTask todoTask = (TodoTask) mListView.getItemAtPosition(mPosition);
if(todoTask != null){
mListView.performItemClick(mSlopView, mPosition, todoTask.getId());
}
}
}
return true;
}
return false;
}

}


我们知道,在屏幕上的任何事件,包括点击,长按,滑动,还是其它手势,其实都是由一系列的Touch事件组成的,而这些Touch事件其实是下面三步组成的:

0)一个ACTION_DOWN事件

1)N个ACTION_MOVE事件

2)一个ACTION_UP事件组成的。

而根据Android的事件分发机制,在触发View本身的OnTouchEvent方法的时候,如果存在一个OnTouchListener,那么View会首先调用OnTouchListener来响应事件,只有当OnTouchListener返回false,表明其不处理该事件的时候,才会继续将事件传递给View的OnTouchEvent,那么在这里,我们就要在OnTouchListener的onTouch方法中,将这个事件给完全消费掉,来实现我们自己想要的效果。

OnTouch方法中,主要实现了下面的功能:
1)当我们手指接触到屏幕上的时候,一个Down事件就会被触发,于是会来到Listener的Down分支下面。

在这里,我们会首先利用ListView的pointToPosition方法,根据触摸的点坐标来获取ListView中item的位置,并利用getChildAt方法找出手指触摸到的那个Listitem,并将其放置到变量mSlopView中。

mSlopView其实就是TaskItem,其结构如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="@drawable/item_selector"
android:padding="5dip" >

<ImageView
android:id="@+id/ivComplete"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:contentDescription="@string/imageview_contentdesc"
android:padding="5dp" />

<TextView
android:id="@+id/tvTitle"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_toRightOf="@+id/ivComplete"
android:gravity="left|center_vertical"
android:padding="5dp"
android:singleLine="true"
android:textSize="18sp" />

<Button
android:id="@+id/btnDelete"
android:layout_width="wrap_content"
android:layout_height="32dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:background="@drawable/shape_button"
android:padding="5dp"
android:text="@string/action_delete"
android:textSize="12sp"
android:visibility="invisible" />

</RelativeLayout>

删除按钮一开始是invisible的,当Down事件发生的时候,我们要将其置为Visible的,但同时要将其alpha值置为0,因为虽然可见,但此时还不能显示出来。

而最后,要在这里返回一个true,表明在这里,Down事件已经被这个OnTouchListener给消费了,这样,后续的Move事件, Up事件才会继续被这个OnTouchListener来处理。

2)当Donw事件被这个OnTouchListener消费后,后续的Move事件也会来到这里进行处理。

在Move分支中, 我们要根据移动的距离来判断这是不是一次移动,这是通过跟mSlop变量比较实现的。mSlop值是Android定义一个滑动事件的最短距离,当移动超过这个距离,表明这是一个滑动事件,那么这个时候,我们就要根据移动的距离去动态地改变删除按钮的alpha值,来显示或者隐藏删除按钮。在这里,定义

2.1)当手指向左移动的时候,则表明这是要显示按钮,那么会将按钮的alpha值由 0 向 1 变化,最终完全显示按钮。

2.2)当手指向右移动的时候,则表明是要删除按钮,那么会将按钮的alpha值由 1 向 0 变化,最终完全隐藏按钮。

最后,这个方法也要返回 true ,表明Move事件也在这里被消费了。

3)最后就是来到Up事件了。每一个Listitem的OnTouch事件其实分两种情况:

3.1)当OnTouch事件被定义为滑动事件时,即滑动距离超过mSlop的时候,我们要根据最终滑动的距离来决定是否要显示按钮。只有当滑动距离超过按钮的一半宽度的时候,才显示按钮,如果按钮还没有完全显示,则添加一个动画效果,来显示按钮。而如果没有超过按钮的一半宽度的时候,也要为该按钮添加一个动画效果,但是这个效果则是慢慢慢将按钮的alpha值变为0,最终隐藏按钮。

3.1)当滑动的距离小于mSlop的时候,该事件只被定义为一个OnItemClick事件,那么在这里要显示地调用performClick方法来触发ListView的OnItemClick事件,这是因为不想去改变开源库的源码,所以我们必须将所有的OnTouch事件(当然,只是我们想处理的)都在OnTouchListener中处理。

最后,在Up分支中,我们处理了事件之后,同样返回一个true,表明所有的OnTouch事件都在这里被处理了,不需要再将事件传递下去。

下面是实现后的效果图:



结束。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐