本文将使用上篇文章介绍的Scroller类来完成一个比较常见的效果——滑动删除效果。
主界面代码:
github源码:https://github.com/Rowandjj/SimpleSwipeListView
代码如下:
simpleSwipeListView:
package cn.edu.chd.simpleswipelistview;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.widget.AbsListView;
import android.widget.ListView;
import android.widget.Scroller;
/**
* @author Rowandjj
*
*滑动删除示例
*/
public class SimpleSwipeListView extends ListView
{
private static final String TAG = "SimpleSwipeListView";
private static final boolean DEBUG = true;
private Scroller mScroller = null;
private int mTouchSlop;
/**
* 屏幕宽度
*/
private int mScreenWidth;
private View targetView;
/**
* 当前item的位置
*/
private int position;
/**
* 手指按下的位置
*/
private int downX,downY;
/**
* 是否能删除item
*/
private boolean canRemove = true;
private boolean isSwipe;
/**
* item被删除的回调事件
*/
private OnItemRemovedListener mListener;
public interface OnItemRemovedListener
{
public void removeItem(int position);
}
public void setOnItemRemovedListener(OnItemRemovedListener listener)
{
this.mListener = listener;
}
public SimpleSwipeListView(Context context)
{
this(context, null);
}
public SimpleSwipeListView(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
}
public SimpleSwipeListView(Context context, AttributeSet attrs,
int defStyleAttr)
{
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context)
{
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
mScreenWidth = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getWidth();
mScroller = new Scroller(context);
}
@Override
public boolean onTouchEvent(MotionEvent ev)
{
switch (ev.getAction())
{
case MotionEvent.ACTION_DOWN:
if(!mScroller.isFinished())
return super.onTouchEvent(ev);
downX = (int) ev.getX();
downY = (int) ev.getY();
//根据坐标得到position
position = pointToPosition(downX, downY);
if(position == AbsListView.INVALID_POSITION)
return super.onTouchEvent(ev);
//根据position得到代表item的view对象
targetView = getChildAt(position-getFirstVisiblePosition());
break;
case MotionEvent.ACTION_MOVE:
int x = (int) ev.getX();
int y = (int) ev.getY();
if(Math.abs(x-downX)>mTouchSlop&&Math.abs(y-downY)<mTouchSlop)
{
int deltaX = downX-x;
downX = x;
downY = y;
targetView.scrollBy(deltaX,0);
isSwipe = true;
}
if(isSwipe)//禁止listView滚动
return true;
break;
case MotionEvent.ACTION_UP:
scrollByOffset();
isSwipe = false;
break;
}
return super.onTouchEvent(ev);
}
/**
* 根据偏移量判断是向左还是向右滚动
*/
private void scrollByOffset()
{
int scrollX = targetView.getScrollX();//当前偏移
if(scrollX > mScreenWidth/3)//应该向左滚
{
scrollToLeft(scrollX);
}else if(scrollX < -mScreenWidth/3)//应该向右滚
{
scrollToRight(scrollX);
}else//滚回原点
{
scrollToOrigin(scrollX);
}
}
private void scrollToOrigin(int scrollX)
{
mScroller.startScroll(scrollX,0,-scrollX,0,Math.abs(scrollX));
postInvalidate();
canRemove = false;
}
/**
* 向右滚动
*/
private void scrollToRight(int scrollX)
{
int deltaX = mScreenWidth+scrollX;
mScroller.startScroll(scrollX, 0, -deltaX, 0, Math.abs(deltaX));
invalidate();
canRemove = true;
}
/**
* 向左滚动
*/
private void scrollToLeft(int scrollX)
{
int deltaX = mScreenWidth - scrollX;
mScroller.startScroll(scrollX, 0, deltaX, 0, Math.abs(deltaX));
invalidate();
canRemove = true;
}
@Override
public void computeScroll()
{
if(mScroller != null)
{
if(mScroller.computeScrollOffset())
{
int currX = mScroller.getCurrX();
int currY = mScroller.getCurrY();
targetView.scrollTo(currX,currY);
postInvalidate();
if(mScroller.isFinished())
{
targetView.scrollTo(0, 0);
if(canRemove)
{
if(mListener == null)
throw new RuntimeException("you must call setOnRemoveItemListener to set a listener");
if(DEBUG)
Log.d(TAG,"position:"+position);
//删除item
mListener.removeItem(position);
canRemove = false;
}
}
}
}
}
}
布局:
activity_main.xml
<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"
android:background="#ededed"
tools:context="cn.edu.chd.simpleswipelistview.MainActivity" >
<cn.edu.chd.simpleswipelistview.SimpleSwipeListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_marginTop="2dp"
android:layout_marginBottom="2dp"
android:layout_height="wrap_content"
android:listSelector="@android:color/transparent"
android:cacheColorHint="@android:color/transparent"
android:divider="@null"
android:dividerHeight="2dp"
></cn.edu.chd.simpleswipelistview.SimpleSwipeListView>
</RelativeLayout>
item.xml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="3dp"
android:descendantFocusability="beforeDescendants"
android:paddingRight="3dp" >
<!--滚动的是下面的内容-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="55dp"
android:background="@drawable/card_background_selector"
android:gravity="center_vertical|left"
android:descendantFocusability="afterDescendants"
android:orientation="vertical" >
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:textColor="@android:color/black"
android:textSize="18sp" />
</LinearLayout>
</FrameLayout>
主界面代码:
package cn.edu.chd.simpleswipelistview;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import cn.edu.chd.simpleswipelistview.SimpleSwipeListView.OnItemRemovedListener;
public class MainActivity extends Activity
{
private SimpleSwipeListView mListView = null;
private ArrayAdapter<String> mAdapter = null;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (SimpleSwipeListView) findViewById(R.id.lv);
final List<String> data = new ArrayList<String>();
for(int i = 0; i < 20; i++)
{
data.add("this is text num"+i);
}
mAdapter = new ArrayAdapter<>(this,R.layout.item, R.id.tv, data);
mListView.setAdapter(mAdapter);
mListView.setOnItemRemovedListener(new OnItemRemovedListener()
{
@Override
public void removeItem(int position)
{
//此处并不用调用notifyDataSetChanged,因为remove方法内部调用了
mAdapter.remove(data.get(position));
}
});
}
}
效果截屏不方便,大家自己看吧。
相信大家结合上篇文章介绍的Scroller理解代码应该很easy。
重点需要注意以下几点:
1.如何根据坐标找到view对象。(pointToPosition和getChildAt方法的使用)
2.Scroller的使用,特别是scrollTo、scrollBy以及computeScroll方法。
3.需要注意如果希望对某个view进行滚动,应该调用其父view的scrollTo、scrollBy方法。
4.处理滑动冲突,横向滑动删除时应避免listView滚动。