- 判断动作是否为横向滑动, 是则拦截并执行自己的方法, 不是则按默认执行;
- 绑定滑动监听器
- 绘制滑动时的效果
下面就一步一步来实现它:
1. 创建一个名为TouchListView 的类, 继承自 ListView
public class TouchListView extends ListView {
// 判断开关向左还是向右的条件
private final float leftOffset = 1f / 3f;
private final float rightOffset = 2f / 3f;
// 判断动作是否为横向滑动的条件
private final float xRange = 20f;
private final float yRange = 100f;
// 当前状态: 是否为滑动
private boolean isSliding = false;
// 手指按下时的位置
private float sx, sy;
// 手指移动过程中的位置
private float mx, my;
// 手指抬起时的位置
private float fx;
// 滑动动作的目标Item
private View touchedView;
// 目标Item在列表中的位置
private int position;
// 目标Item在数据库中的id
private long id;
// 滑动开关监听器
private TriggerListener tl;
// 绘制文字和彩带用的画笔
private Paint textPaint;
private Paint mPaint;
}
还有个init()方法在构造函数里调用, 用来初始化画笔;
private void init() {
// 初始化文字画笔
textPaint = new Paint();
textPaint.setColor(Color.WHITE);
textPaint.setTextSize(25);
textPaint.setTextAlign(Align.CENTER);
textPaint.setAntiAlias(true);
// 初始化彩带画笔
mPaint = new Paint();
mPaint.setAntiAlias(true);
}
2. 判断动作是否为横向滑动, 并加以拦截:
因为和触摸事件有关, 所以我们需要重写 onTouchEvent() 方法;
@Override
public boolean onTouchEvent(MotionEvent ev) {
// 当手指在屏幕上移动时即时记录手指的位置, 下面很多地方都要用到
if(ev.getAction() == MotionEvent.ACTION_MOVE) {
mx = ev.getX();
my = ev.getY();
}
if(isSliding) { // 若状态为滑动则拦截, 并执行滑动时的动作
sliding(ev);
// 在传递前将动作设置为"取消"即可实现拦截
ev.setAction(MotionEvent.ACTION_CANCEL);
} else {
// 非滑动状态时检查是否开始滑动
checkSlide(ev);
}
return super.onTouchEvent(ev);
}
public boolean onTouchEvent(MotionEvent ev) {
// 当手指在屏幕上移动时即时记录手指的位置, 下面很多地方都要用到
if(ev.getAction() == MotionEvent.ACTION_MOVE) {
mx = ev.getX();
my = ev.getY();
}
if(isSliding) { // 若状态为滑动则拦截, 并执行滑动时的动作
sliding(ev);
// 在传递前将动作设置为"取消"即可实现拦截
ev.setAction(MotionEvent.ACTION_CANCEL);
} else {
// 非滑动状态时检查是否开始滑动
checkSlide(ev);
}
return super.onTouchEvent(ev);
}
3. 判断是否为滑动的方法 checkSlide();
private void checkSlide(MotionEvent ev) {
int action = ev.getAction();
switch(action) {
case MotionEvent.ACTION_DOWN:
// 记录开始位置, 即手指按下的位置
sx = ev.getX();
sy = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
// 计算当前位移
float dx = Math.abs(mx - sx);
float dy = Math.abs(my - sy);
if(dx > xRange && dy < yRange) { // 若横向位移大于 xRange, 纵向位移小于 yRange 则算作开始横向滑动
// 获得手指按下处的 Item
position = pointToPosition((int)sx, (int)sy);
int firstPosition = this.getFirstVisiblePosition();
touchedView = getChildAt(position - firstPosition);
if(touchedView != null) { // 只有Item存在时才算作开始滑动, 若在空白处滑动则不算
Log.i("Notebook", "开始横向滑动");
// 将滑动状态设置为 true
this.isSliding = true;
// 获得 Item 的 id, 设置监听器时要用
id = getAdapter().getItemId(position);
// ListView 默认是只在上下滚动时才刷新, 所以这里要强制刷新它
this.postInvalidate();
}
}
break;
}
}
4. 进入滑动状态后要执行的动作 sliding():
private void sliding(MotionEvent ev) {
if(ev.getAction() == MotionEvent.ACTION_UP) { // 滑动时, 手指抬起代表滑动结束
// 首先, 重置滑动状态为 false
this.isSliding = false;
Log.i("Notebook", "滑动结束");
// 记录手指抬起的位置
this.fx = ev.getX();
// 抬起后判断开关的方向, 向左, 向右, 或是空
int width = touchedView.getWidth();
int direction = TriggerListener.NONE;
if(fx > width * rightOffset) direction = TriggerListener.RIGHT;
else if(fx < width * leftOffset) direction = TriggerListener.LEFT;
// 确定方向后执行监听器定义的方法
if(tl != null) {
tl.onTrigger(direction, position, id);
}
}
}
5. 设置动画效果(向左滑变红, 向右滑变红, 同时透明度改变); 需要重写 drawChild(); 方法
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
boolean b = super.drawChild(canvas, child, drawingTime);
// 画在原来的图像上方
// 画在原来的图像上方
if(isSliding) { // 滑动时则开始绘制
// 若不是目标对象则按默认动作返回
if(touchedView != child) return b;
Log.i("Notebook", "开始绘图");
// 得到绘图所需基本数据
int top = touchedView.getTop();
int width = touchedView.getWidth();
int height = touchedView.getHeight();
// 生成彩带
LinearGradient shader = new LinearGradient(mx - width / 2, 0, mx + width / 2, 0, Color.rgb(11,115,6), Color.RED, Shader.TileMode.CLAMP);
mPaint.setShader(shader);
// 计算手指偏移量(相对于中心)
float offset = Math.abs(mx - width / 2f) / (width / 2f);
// 设置透明度
textPaint.setAlpha((int) (255 * offset));
mPaint.setAlpha((int) (200 + 55 * offset));
// 移动画布前保存
canvas.save();
// 将画布移动到要画的条目处
canvas.translate(0, top);
// 画彩带;
canvas.drawRect(0, 0, width, height, mPaint);
// 画文字
String text = "";
if(mx > width * rightOffset) text = "编辑";
else if(mx < width * leftOffset) text = "删除";
canvas.drawText(text, width / 2f, height / 2f, textPaint);
// 还原画布
canvas.restore();
// 刷新View
this.postInvalidate();
}
return b;
}
6. 绑定监听器的方法
/**
* 绑定滑动监听器到 ListView
* @param tl 滑动监听器
*/
public void setTriggerListener(TriggerListener tl) {
this.tl = tl;
}
* 绑定滑动监听器到 ListView
* @param tl 滑动监听器
*/
public void setTriggerListener(TriggerListener tl) {
this.tl = tl;
}
7. 监听器接口
public interface TriggerListener {
public static final int NONE = -1;
public static final int LEFT = 0;
public static final int RIGHT = 1;
/**
* 滑动操作的监听器
* @param direction 滑动操作的方向, 左, 右, 无
* @param Position 该条目在List中的位置
* @param id 该条目在数据库中的id
*/
public void onTrigger(int direction, int Position, long id);
}
使用时和普通的ListView没什么区别, 只要记得实现TriggerListener接口绑定监听器就行了;