自定义View解决滑动冲突

来源:互联网 发布:萌萌猪软件系统开发 编辑:程序博客网 时间:2024/05/16 10:53

最近在读Android开发艺术探索,本文作为自己对view的滑动冲突的理解和实践记录

而滑动冲突,需要了解Android的事件分发机制,如果这个还有些疑惑的地方,请参考这篇文章,以及其中的参考文章

还需要View的Measure和Layout的相关知识View的Measure流程总结

自定义view注意

1.如果直接继承view,此时wrap_content和使用match_parent效果一样.需要在onMeasure()中处理AT_MOST条件,处理wrap_content.

2.margin要在onLayout中设定,padding需要在 onDraw中设定

3.刷新回调,停止县城或者动画 在view.onDetachedFromWindow

4.在dispatchTouchEventTouchEvent中处理好滑动事件.

滑动冲突的种类

场景一:外部和内部俩层滑动方向不一致

场景二:外部和内部俩层滑动方向一致

场景三:主要是针对场景一和二的嵌套

滑动处理千篇一律, 只要你找到什么时候父控件滑动,什么时候子空间滑动.然后再父布局中,选择 是否自己处理onInterceptTouchEvent(),就好了.

Android开发艺术探索中有两种方式,分别为外部拦截发,和内部拦截法.我上面说的是外部拦截法(感觉这个好用些).
具体的内容,大家可以看 Android开发艺术探索第三章第五节相关内容.

情形1的处理

先上效果图
scollview_gif_1.gif

下面是自定义的view,解决了上述情景1的问题.

/**    自定义滑动viewPager * Created by chenchangjun on 17/7/14. */public class HorizontalScrollView extends ViewGroup {    private static final String TAG = HorizontalScrollView.class.getSimpleName();    private int mChildWidth = 1;    private int mChildIndex = 1;    private int mLastX;    private int mLastY;    private int mLastXIntercept = 0;    private int mLastYIntercept = 0;    /**     * Scroller只是个计算器,处理滑动效果的,例如ViewPager,listview等的内部类     */    private Scroller mScroller;    /**     * 速度获取器     */    private VelocityTracker mVelocityTracker;    private int mChildrenCount;    private void init() {        mScroller = new Scroller(getContext());        mVelocityTracker = VelocityTracker.obtain();    }    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        boolean intercept = false;        int x = (int) ev.getX();        int y = (int) ev.getY();        switch (ev.getAction()) {            case MotionEvent.ACTION_DOWN:                intercept = false;                if (!mScroller.isFinished()) {                    mScroller.abortAnimation();                }                break;            case MotionEvent.ACTION_MOVE:                int deltaX = x - mLastXIntercept;                int deltaY = y - mLastYIntercept;                if (Math.abs(deltaX) > Math.abs(deltaY)) {                    intercept = true;                } else {                    intercept = false;                }                break;            case MotionEvent.ACTION_UP:                intercept = false;                break;            default:                break;        }        Log.d(TAG, "intercept=" + intercept);        mLastX = x;        mLastY = y;        mLastXIntercept = x;        mLastYIntercept = y;        return intercept;    }    @Override    public boolean onTouchEvent(MotionEvent event) {        mVelocityTracker.addMovement(event);        int x = (int) event.getX();        int y = (int) event.getY();        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                if (!mScroller.isFinished()) {                    mScroller.abortAnimation();                }                break;            case MotionEvent.ACTION_MOVE:                int deltaX = x - mLastX;                scrollBy(-deltaX, 0);//视觉上向右滑动,相对的,view横向向左移动.                break;            case MotionEvent.ACTION_UP:                int scrollX = getScrollX();//                int srcollToChildIndex=scrollX/mChildWidth;                mVelocityTracker.computeCurrentVelocity(1000);                float xVelocity = mVelocityTracker.getXVelocity();                if (Math.abs(xVelocity) >= 30) { //当一秒滑动像素大于30像素的时候,                    mChildIndex = xVelocity > 0 ? mChildIndex - 1 : mChildIndex + 1;//计算pager下标mChildIndex.如果手指从右向左,则xVelocity为负,mChildIndex+1;反之,易然.                } else {                    mChildIndex = (scrollX + mChildWidth / 2) / mChildWidth;                }                int dex=0;               /* if (mChildIndex >= getChildCount()) {                    mChildIndex = 0;                } else if (mChildIndex < 0) {                    mChildIndex = getChildCount() - 1;                } else {                    dex = mChildIndex * mChildWidth - scrollX;                }*/                mChildIndex=Math.max(0,Math.min(mChildIndex,mChildrenCount-1));                dex = mChildIndex * mChildWidth - scrollX;                smoothScrollBy(dex, 0);                mVelocityTracker.clear();                break;            default:                break;        }        mLastX = x;        mLastY = y;        return true;    }    private void smoothScrollBy(int dx, int dy) {        mScroller.startScroll(getScrollX(), 0, dx, 0, 500);        invalidate();    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        int childLeft = 0;        final int childCount = getChildCount();        mChildrenCount = childCount;        for (int i = 0; i < childCount; i++) {            final View child = getChildAt(i);            if (child.getVisibility() != GONE) {                mChildWidth = child.getMeasuredWidth();                child.layout(childLeft, 0, childLeft + child.getMeasuredWidth(), child.getMeasuredHeight());                childLeft += mChildWidth;            }        }    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int measuredWidth = 0;        int measuredHeight = 0;        final int childCount = getChildCount();        measureChildren(widthMeasureSpec, heightMeasureSpec);        int widthSpaceSize = MeasureSpec.getSize(widthMeasureSpec);        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);        int heightSpaceSize = MeasureSpec.getSize(heightMeasureSpec);        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);        if (childCount == 0) {            setMeasuredDimension(0, 0);        } else if (heightSpecMode == MeasureSpec.AT_MOST && widthSpecMode == MeasureSpec.AT_MOST) {            final View childView = getChildAt(0);//因为这里的child都是同类,所以偷懒~取第一个测量尺寸就够啦            measuredWidth = childView.getMeasuredWidth() * childCount;            measuredHeight = childView.getMeasuredHeight() * childCount;            setMeasuredDimension(measuredWidth, measuredHeight);        } else if (heightSpecMode == MeasureSpec.AT_MOST) {            final View childView = getChildAt(0);//因为这里的child都是同类,所以偷懒~取第一个测量尺寸就够啦            measuredHeight = childView.getMeasuredHeight() * childCount;            setMeasuredDimension(widthSpaceSize, measuredHeight);        } else if (widthSpecMode == MeasureSpec.AT_MOST) {            final View childView = getChildAt(0);//因为这里的child都是同类,所以偷懒~取第一个测量尺寸就够啦            measuredWidth = childView.getMeasuredWidth() * childCount;            setMeasuredDimension(measuredWidth, heightSpaceSize);        } else {            setMeasuredDimension(widthSpaceSize, heightSpaceSize);        }    }    @Override    public void computeScroll() {        if (mScroller.computeScrollOffset()) {            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());            postInvalidate();        }    }    @Override    protected void onDetachedFromWindow() {        mVelocityTracker.recycle();        super.onDetachedFromWindow();    }    public HorizontalScrollView(Context context) {        super(context);        init();    }    public HorizontalScrollView(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public HorizontalScrollView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)    public HorizontalScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {        super(context, attrs, defStyleAttr, defStyleRes);        init();    }}

总结

对于第一种,思路是在ACTION_MOVE的时候,判断x偏移量是否大于y的偏移量.如果大于,就page++,.

对于第二种,需要判断子view是否滑动到了顶部,或者底部,如果是,让父控件滑动即可.

对于第三种,需要结合第一种,和第二种进行判断.

在判断滑动冲突的过程中,重点放在 InterceptTouchEvent()中,还有TouchEvent种进行处理.

原创粉丝点击