最近有一个滑动删除列表的需求,本来以为很简单的,做的和QQ的一样就好,结果自己写发现不是这么回事,QQ的滑动删除是滑动就打开,再触碰除了打开的区域以外的区域,打开的item就回缩,其实就是状态的判断,但是我的判断似乎总是出问题,于是用了点小技巧,现在的功能是实现了,滑动打开,点击其他地方就回缩,特此记录。
其实主要还是滑动事件的分发和item状态的判断,我这里参考了http://blog.csdn.net/harvic880925/article/details/45317951http://blog.csdn.net/harvic880925/article/details/45317951这篇博客,把自定义listview改成自定义linearlayout,由layout自己实现回滚,这样就只需要处理好打开关闭状态就行了,下面看代码
/** * 滑动的监听,传递当前滑动的layout和状态(打开或者关闭),用于action_up */ public static interface OnScrollListener { public void onScroll(DeleteLinearLayout view,int state); } public void setOnScrollListenre(OnScrollListener onScrollListener) { this.onScrollListener = onScrollListener; } /** * 触碰的监听,传递当前触碰的layout,用于action_down */ public static interface OnTouchListener { public void onTouch(DeleteLinearLayout view); } public void setOnTouchListener(OnTouchListener onTouchListener) { this.onTouchListener = onTouchListener; } public DeleteLinearLayout(Context context) { this(context, null); } public DeleteLinearLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public DeleteLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context) { this.context = context; mScroller = new Scroller(context, new LinearInterpolator(context, null)); touchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } @Override public boolean onTouchEvent(MotionEvent ev) { int maxLength = 300; switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: { xDistance = yDistance = 0f; xLast = ev.getX(); yLast = ev.getY(); currentX = (int) ev.getX(); mlastX = (int) ev.getX(); xDistance = yDistance = 0f; xLast = ev.getX(); yLast = ev.getY(); onTouchListener.onTouch(this); } break; case MotionEvent.ACTION_MOVE: { //如果当前状态是关闭状态才执行,相当于拦截了打开状态的action_move if(!Utils.getSP(context)) { final float curX = ev.getX(); final float curY = ev.getY(); // 计算在X和Y方向的偏移量 xDistance += Math.abs(curX - xLast); yDistance += Math.abs(curY - yLast); xLast = curX; yLast = curY; // 横向距离大于纵向距离,layout才可以滑动 if (xDistance > yDistance && xDistance >touchSlop) { currentX = (int) ev.getX(); int scrollX = getScrollX(); int reallyX = scrollX + mlastX - currentX; if (reallyX < 0) { reallyX = 0; } else if (reallyX > maxLength) { reallyX = maxLength; } this.scrollTo(reallyX, 0); mlastX = currentX; } } } break; case MotionEvent.ACTION_UP: { //无论是否拦截action_move,都会进来这一步,所以如果拦截了action_move,reallyX 为0,把当前状态设为了关闭 int scrollX = this.getScrollX(); int reallyX = scrollX + mlastX - currentX; if (scrollX > maxLength / 2) { reallyX = maxLength; CURRENT_STATE = STATE_OPEN; //保存listview当前的状态为打开 Utils.setSP(context,true); } else { CURRENT_STATE = STATE_CLOSE; //保存listview当前的状态为关闭 Utils.setSP(context,false); reallyX = 0; } onScrollListener.onScroll(this,CURRENT_STATE); mScroller.startScroll(scrollX, 0, reallyX - scrollX, 0); invalidate(); } break; } return super.onTouchEvent(ev); } @Override public void computeScroll() { super.computeScroll(); if (mScroller.computeScrollOffset()) { this.scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); } invalidate(); } //提供给外面调用的方法,用于使layout滚回原点 public void smoothScrollTo(int x, int y) { int scrollX = getScrollX(); int deltaX = x - scrollX; mScroller.startScroll(scrollX, 0, deltaX, 0); invalidate(); }
然后是MainActivity
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listView = (MyListView) findViewById(R.id.listview); for (int i = 0; i < 200; i++) { MyAdapter.Data data = new MyAdapter.Data(); data.title = (i + "个人"); list.add(data); } adapter = new MyAdapter(this, list, new View.OnClickListener() { @Override public void onClick(View v) { if(v.getId() == R.id.del) { int positon = listView.getPositionForView(v); Utils.setSP(MainActivity.this,false); adapter.removeItem(positon); curState = 2; adapter.notifyDataSetChanged(); } } }, new DeleteLinearLayout.OnScrollListener() { @Override public void onScroll(DeleteLinearLayout view, int state) { if (state == 1) { lastLayout = view; } curState = state; } }, new DeleteLinearLayout.OnTouchListener() { @Override public void onTouch(DeleteLinearLayout view) { if (curState == 1) { lastLayout.smoothScrollTo(0, 0); } } }); listView.setAdapter(adapter); } @Override protected void onDestroy() { super.onDestroy(); Utils.setSP(this,false);
自定义的listview,不处理具体事件
public boolean onTouchEvent(MotionEvent event) {
int maxLength = 300; int currentY = (int) event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { currentX = (int) event.getX(); //我们想知道当前点击了哪一行 int position = pointToPosition(currentX, currentY); if (position != INVALID_POSITION) { MyAdapter.Data data = (MyAdapter.Data) getItemAtPosition(position); itemLayout = data.listLayout; Log.e("Listview action down: ","valid positon"); } else { Log.e("Listview action down: ","invalid positon"); } } case MotionEvent.ACTION_MOVE: final float curX = event.getX(); final float curY = event.getY(); // 计算在X和Y方向的偏移量 xDistance += Math.abs(curX - xLast); yDistance += Math.abs(curY - yLast); xLast = curX; yLast = curY; break; } if (itemLayout != null) { itemLayout.onTouchEvent(event); } else { } if (xDistance > yDistance) { return true; } return super.onTouchEvent(event); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: xDistance = yDistance = 0f; xLast = ev.getX(); yLast = ev.getY(); break; default: break; } return super.dispatchTouchEvent(ev); }这里用了个小技巧,就是用sharepreferences保存了linearlayout的当前状态,第一次点击,Utils.getSP()返回false,因此可以滑动,走到action_up,如果最后状态为打开,则将sharepreferences设置为true,那么第二次点击的时候,就不会进去action_move,故不会滑动,但是还会进入action_up,最后状态由于action_move的时候没有滑动,为关闭状态,因此把shareprefer置为false,一直循环。
主要是正规的方法想不出来。