从ListView的滑动删除了解事件分发机制与冲突

来源:互联网 发布:手机淘宝图片保存位置 编辑:程序博客网 时间:2024/06/10 09:49

ListView滑动删除手势:

  1. item 从右向左滑动
  2. item 内部点击事件

事件分发过程中的主要方法:

  1. boolean dispatchTouchEvent(MotionEvent ev):当事件能够传递到View时,那么该View的dispatchTouchEvent方法就会被调用,返回结果表示是否消耗当前事件
  2. boolean onInterceptTouchEvent(MotionEvent ev):在dispatchTouchEvent方法内调用,判断是否拦截某个事件,如果拦截,事件将不会再向下一层传递
  3. boolean onTouchEvent(MotionEvent ev):当onInterceptTouchEvent拦截事件后调用该方法,用来进行对应事件的处理

    上述三个方法的关系下面的伪代码解释的比较清楚:

public boolean dispatchTouchEvent(MotionEvent ev){    boolean consume = false;    if(onInterceptTouchEvent(ev){        consume = onTouchEvent(ev);    }else{        consume = child.dispathcTouchEvent(ev);    }    return consume;}

分析滑动删除功能

事件的传递过程主要看是否消费事件。根据滑动删除功能从右向左的滑动的触摸事件分析一下事件分发:

  1. 用户点击屏幕准备滑动item按钮时会产生一个ACTION_DOWN事件
  2. ACTION_DOWN事件传递到ListView时,ListView不消费、不拦截,记录初始位置,继续向下传递到自定义滑动item
  3. item 接收到ACTION_DOWN事件时,记住初始位置,用来后续测量滑动速度与距离
  4. 用户滑动时,传递一系列的ACTION_MOVE事件
  5. ACTION_MOVE事件传递到ListView时,ListView判断滑动方向,看是否向下s传递滑动,如果滑动方向不是从右向左,则不向下继续传递,如果是,则继续向下传递
  6. item接收到ACTION_MOVE事件时,判断滑动速度,滑动View

上述就是滑动事件的大概流程,下面分析一下具体的代码:

自定义 ListView

public class SlideListView extends ListView {    private SlideItemLayout mCurrentItemView;    /**     * 记录之前滑动的位置     */    private int prePosition = -1;           public SlideListView(Context context) {        super(context);    }    public SlideListView(Context context, AttributeSet attrs) {        super(context, attrs);    }    public SlideListView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    /**     * 测量,主要处理 ListView 在 ScrollView 等试图中的高度问题     *      * @param widthMeasureSpec     * @param heightMeasureSpec     */    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,                MeasureSpec.AT_MOST);        super.onMeasure(widthMeasureSpec, expandSpec);    }    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        switch (ev.getAction()) {            case MotionEvent.ACTION_DOWN:                //当之前有item滑动过,再次滑动另一item时重置前一个item                int position = pointToPosition((int) ev.getX(), (int) ev.getY());                if (position >= 0 && position != prePosition) {                    View currentItemView = getChildAt(position - getFirstVisiblePosition());                    if (mCurrentItemView != null) {                        mCurrentItemView.reset();                    }                    mCurrentItemView = (SlideItemLayout) currentItemView;                    prePosition = position;                }                break;            case MotionEvent.ACTION_MOVE:                //当设置状态为不能启用时不再向下传递滑动事件                if (!isEnabled()) {                    return true;                }                break;        }        return super.onInterceptTouchEvent(ev);    }}

自定义item

public class SlideItemLayout extends ViewGroup {    private float MAX_SLIDE_SPEED = 1000f;  //最大滑动速度    private float deleteWidth;    public SlideItemLayout(Context context) {        super(context);    }    public SlideItemLayout(Context context, AttributeSet attrs) {        super(context, attrs);    }    public SlideItemLayout(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int widthSize = MeasureSpec.getSize(widthMeasureSpec);        int heightSize = MeasureSpec.getSize(heightMeasureSpec);        int widthMode = MeasureSpec.getMode(widthMeasureSpec);        int heightMode = MeasureSpec.getMode(heightMeasureSpec);        // 计算自定义的ViewGroup中所有子控件的大小        measureChildren(widthMeasureSpec, heightMeasureSpec);        setMeasuredDimension(measureSize(widthMode, widthSize), measureSize(heightMode, heightSize));    }    /**     * 测量高度与宽度     *     * @param mode     * @return     */    private int measureSize(int mode, int s) {        int size = 0;        switch (mode) {            case MeasureSpec.AT_MOST:            case MeasureSpec.EXACTLY:                size = s;                break;        }        return size;    }    @Override    protected void onLayout(boolean change, int l, int t, int r, int b) {        int right = 0;        int size = getChildCount();        //获取删除按钮的宽度        deleteWidth = getChildAt(size - 1).getMeasuredWidth();        for (int i = 0; i < size; i++) {            View view = getChildAt(i);            int childHeight = view.getMeasuredHeight();            int chileWidth = view.getMeasuredWidth();            view.layout(right, 0, right + chileWidth, childHeight);            right += chileWidth;        }    }    private float startX = 0;    private float endX = 0;    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        switch (ev.getAction()) {            case MotionEvent.ACTION_DOWN:                startX = ev.getX();                break;            case MotionEvent.ACTION_MOVE:                if (slideSpeed(ev) >= MAX_SLIDE_SPEED) {                    return true;                }                break;        }        return super.onInterceptTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()) {            case MotionEvent.ACTION_MOVE:                endX = event.getX();                if (endX - startX < 0 && startX - endX <= deleteWidth) {      //从右向左滑动                    scrollTo((int) (startX - endX), 0);                }                break;            case MotionEvent.ACTION_UP:                endX = event.getX();                if (startX - endX < deleteWidth / 2) {                    scrollTo(0, 0);                } else {                    scrollTo((int) deleteWidth, 0);                }                releaseVelocityTracker();                break;            case MotionEvent.ACTION_CANCEL:                endX = event.getX();                if (startX - endX < deleteWidth / 2) {                    scrollTo(0, 0);                } else {                    scrollTo((int) deleteWidth, 0);                }                releaseVelocityTracker();                break;        }        return super.onTouchEvent(event);    }    private VelocityTracker mVelocityTracker;    /**     * 计算侧滑速度     *     * @param event     * @return     */    private float slideSpeed(MotionEvent event) {        if (mVelocityTracker == null) {            mVelocityTracker = VelocityTracker.obtain();        }        mVelocityTracker.addMovement(event);        final VelocityTracker velocityTracker = mVelocityTracker;        velocityTracker.computeCurrentVelocity(1000, MAX_SLIDE_SPEED);        return velocityTracker.getXVelocity();    }    /**     * 释放 VelocityTracker     */    private void releaseVelocityTracker() {        if (mVelocityTracker != null) {            mVelocityTracker.clear();            mVelocityTracker.recycle();            mVelocityTracker = null;        }    }    /**     * 返回到初始状态     */    public void reset() {        scrollTo(0, 0);    }}

具体实现效果

注:本文参考《Android开发艺术探索》—— 任玉刚

0 0
原创粉丝点击