一般情况下,如果想要在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事件都在这里被处理了,不需要再将事件传递下去。
下面是实现后的效果图:
结束。
分享到:
相关推荐
微信小程序完整demo:Todo(源代码+截图)微信小程序完整demo:Todo(源代码+截图)微信小程序完整demo:Todo(源代码+截图)微信小程序完整demo:Todo(源代码+截图)微信小程序完整demo:Todo(源代码+截图)微信小程序完整...
微信小程序源码 todo list(学习版)微信小程序源码 todo list(学习版)微信小程序源码 todo list(学习版)微信小程序源码 todo list(学习版)微信小程序源码 todo list(学习版)微信小程序源码 todo list(学习版)微信小...
微信小程序 todo list (源码)微信小程序 todo list (源码)微信小程序 todo list (源码)微信小程序 todo list (源码)微信小程序 todo list (源码)微信小程序 todo list (源码)微信小程序 todo list (源码)微信小程序 ...
微信小程序demo:todo:wx.setStorageSync(KEY,DATA) 方法存放数据(源代码+截图)微信小程序demo:todo:wx.setStorageSync(KEY,DATA) 方法存放数据(源代码+截图)微信小程序demo:todo:wx.setStorageSync(KEY,DATA) ...
小程序源码 todo list (代码+截图)小程序源码 todo list (代码+截图)小程序源码 todo list (代码+截图)小程序源码 todo list (代码+截图)小程序源码 todo list (代码+截图)小程序源码 todo list (代码+截图)小程序...
作为android开发初学者,按照书上例程做的一个todo_list的项目,源代码带注释
微信小程序——提醒录todo list(截图+源码).zip 微信小程序——提醒录todo list(截图+源码).zip 微信小程序——提醒录todo list(截图+源码).zip 微信小程序——提醒录todo list(截图+源码).zip 微信小程序...
微信小程序集成Redux实现的Todo list微信小程序集成Redux实现的Todo list
Todo.txt for Android The official Todo.txt Android application by Gina Trapani and the Todo.txt community designed to manage your todo.txt ...There are multitudes of todo list managers for Android. Todo
大学期间学习安卓做的一个小程序,当时todo计划清单类软件比较火爆,自己简单实现了一些功能,没有使用数据库。等有时间了在修改。
免责声明:资料部分来源于合法的互联网渠道收集和整理,部分自己学习积累成果,供大家学习参考与交流。收取的费用仅用于收集和整理资料耗费时间的酬劳。 本人尊重原创作者或出版方,资料版权归原作者或出版方所有,...
小案例——todo-list以及单测
基于vue技术的todolist项目,适合新手练习
微信小程序实训课的大作业基于微信小程序开发的任务列表(TODO-LIST)项目源码 项目简介 “TODO-LIST”是一个任务列表,不同于其它的 TODO-LIST 产品,该项目没有具体的“时间”概念,其它 TODO-LIST 基本都是以...
小西瓜养成计划作为练手级别情侣微信小程序,支持任务完成、奖励兑换、记事本和 TODO List 等功能,方便情侣进行日常互动与事务记录 项目结构(部分) . ├── cloudfunctions # 参考资料:...
微信小程序demo
微信小程序demo
todo list
微信小程序-todo list源码。资源中包含了详细的系统图文搭建教程。源码都是实际项目,经过测试可以正常稳定运行在服务器,需要具备基础服务器搭建知识,不会搭建的请谨慎购买。